-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
Add symfony/contracts: a set of abstractions extracted out of the Symfony components #27093
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This would only work if the used terms have an equal purpose and meaning, Resettable has a different purpose in Cache than OutputFormatterStyleStack or Compiler in the Expression-Language. Basically this would make the interfaces extreme generic in purpose and less about their specific situation. A Trait can be easily re-used as it's simple code-level copy-paste but doesn't enforce an interface contract. |
Correct, and that's actually the exact purpose of the proposed interface. I don't know why you state that they have a different purpose in your list, because AFAIK, they have the exact same one: resetting an object to its initial state. This is even more apparent when considering that all (most) these existing "reset" methods are bound to "kernel.reset" tags. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like it 👍
(minor comments / questions)
69525e5
to
1446d14
Compare
Maybe we should use the discussion of PR to assemble a list of classes, interfaces and traits that could possibly be moved into the abstractions package, to get an idea of where we would be heading to with this idea. |
@derrabus why not. Here is my short list (I would really try hard to not add any classes here, even abstract ones):
There might be more abstractions that we could extract from other components (Console, EventDispatcher, Lock, Messenger, Routing, Translation, HttpKernel, etc.?) |
I really like the idea of separating abstractions from implementations.
Regarding EventDispatcher, a (dead-simple) class would be among the candidates for me:
Moving those to interfaces into an abstraction package would allow components to define optional event subscribers without depending on the actual event dispatcher. We could also simplify code like this. |
This PR is weird, seeing #6129 was voted down on. What has changed? |
1446d14
to
f670d56
Compare
Thanks for the link @ostrolucky. About your question, re-reading my own downvote, I then stated:
that's exactly what I'm proposing here: have the Symfony project broaden its missions a bit, and allow itself to publish interfaces on their own for genericity. The benefits are well discussed and presented in the issue. Why did I change my mind? Because I feel like not having this package is preventing us from some kind of innovation we'd like to do. E.g. taggable cache items, this linked cache interface, the attached reset method, etc. It comes also as a realization that the FIG is not the right place for all of these: I don't expect everyone in the FIG to agree on all things we'd still like to do. And this is fine, we don't have the exact same goals obviously. We need our own freedom on the "abstractions" topic, so that we're able to contribute to this space also. Some topics could be of interest to the FIG (e.g. taggable cache items), still that shouldn't prevent us from being able to move forward at our own pace. |
I like the idea of creating standards by extracting or defining interfaces out of established and working solutions. This is what PSRs are missing in many cases. However, how do you see the My main issue with this idea is that the package wouldn't be cohesive, as a lot of unrelated interfaces would be packaged together. If I only need the |
@nicolas-grekas to better understand the purpose (and limits) of this new package, which of the following packages fits the idea of symfony/abstractions better?
Thanks! |
That's a desired property of the package IMHO: it will ease discoverability and dependency maintenance (there are some comments in #6129 on the topic). We might reconsider later if the number of interfaces grows out of control, but I don't expect this to happen anytime soon. Meanwhile, we could/should group domains in subnamespaces, when cohesiveness is desired (not the case for ResettableInterface, but could be for the mentioned Cache interfaces.) The fact that you get potentially unused interfaces poses no issues IMHO. @javiereguiluz certainly not like doctrine/common, which is not an abstraction at all. What I presented above: this would contain interfaces (=abstractions), related documentation defining their semantics (in docblocks), generic traits (=type-less behaviors) and reference test suites when applicable (of primary importance, see eg https://github.com/php-cache/integration-tests/ for what I mean). |
f670d56
to
7112913
Compare
3d2b01d
to
e6b23db
Compare
ping @symfony/deciders |
This seems like a simple optimization: by moving some interfaces to this component, the only practical difference is that you're allowing a library that wants to use that interface (but not use our implementation) to depend on the smaller Are there any big downsides? |
* process loop (note that we advise making your services stateless instead of | ||
* implementing this interface when possible.) | ||
*/ | ||
interface ResettableInterface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the Symfony world, we would say ResetInterface
(I don't think we have any -able interface right now in Symfony). In the ZF world, it would be Resettable
. I propose to rename to ResetInterface
here for consistency with our practices. Any better ideas?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fabpot Symfony\Component\HttpKernel\RebootableInterface
, Symfony\Component\HttpKernel\TerminableInterface
, Symfony\Component\Cache\PruneableInterface
, Symfony\Component\Cache\ResettableInterface
...
src/Symfony/Contract/README.md
Outdated
A set of abstractions extracted out of the Symfony components. | ||
|
||
Can be used to build on semantics that the Symfony components proved useful - and | ||
that already have neat implementations if you don't mind. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would remove neat
.
if you don't mind
-> can be removed.
src/Symfony/Contract/README.md
Outdated
Design Principles | ||
----------------- | ||
|
||
* contracts are split by domain, each into their own sub-namespace; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
namespaces
src/Symfony/Contract/README.md
Outdated
### How to use this package? | ||
|
||
The abstractions in this package are useful to achieve loose coupling and | ||
interoperability. By using the provided interfaces as type hints, you will |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are able
src/Symfony/Contract/README.md
Outdated
interoperability. By using the provided interfaces as type hints, you will | ||
be able to reuse any implementations that match their contracts. It could be a | ||
Symfony component, or another one provided by the PHP community at large | ||
(or by you.) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would remove this last part.
src/Symfony/Contract/README.md
Outdated
encourage relying on them and won't duplicate the effort. Still, the FIG has | ||
different goals and different processes. Here, we don't need to seek universal | ||
standards. Instead, we're providing abstractions that are compatible with the | ||
implementations provided by Symfony components. This should actually also |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
by Symfony (components can be removed as we are talking about the project here)
src/Symfony/Contract/README.md
Outdated
different goals and different processes. Here, we don't need to seek universal | ||
standards. Instead, we're providing abstractions that are compatible with the | ||
implementations provided by Symfony components. This should actually also | ||
contribute positively to the FIG (from which Symfony is a member), by hinting the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PHP-FIG
The |
02f8c05
to
b90951a
Compare
Here we are, the interface is now
Makes perfect sense to me, everything ready to put it into practice :) |
b90951a
to
a5c87e3
Compare
I absolutely agree.
I see how keeping the contracts inside the monorepo would ease the extraction of more interfaces, but I must admit that the detached versioning confuses me already, thinking about branching tagging of releases. A separate repository would be easier to maintain and understand in the long run, I guess. How about this: We could start with the contracts in the monorepo to ease the extraction for now and right before the feature freeze of Symfony 4.2 (which would be 1.0 of the contracts), the subtree split becomes an independant repository like the polyfills or the monolog bundle. |
@derrabus we'll have some time to figure out what works best. For now, we anticipate that the easiest to maintain could be a subtree-splitted repository. Status: needs review |
a5c87e3
to
8982036
Compare
Thank you @nicolas-grekas. |
… out of the Symfony components (nicolas-grekas) This PR was merged into the 4.2-dev branch. Discussion ---------- Add symfony/contracts: a set of abstractions extracted out of the Symfony components | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | - | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - A set of abstractions extracted out of the Symfony components. This is a topic I've been thinking about for a long time. I feel like the time has come for Symfony to publish some abstractions so that people could build on them in a decoupled way. I've identified interfaces in some components that would greatly benefit from being moved out from the components where they are for now. E.g. #26929 is something that has a broader scope than the Cache component itself. By putting them in a new `symfony/abstractions` package, we would allow more innovation in the Symfony community, at the abstraction level. In order to start small, I propose only one interface that gathers a concept that is shared amongst many components already: `ResetInterface`. It would provide a standard `reset()` method, whose purpose is to set an object back to its initial state, allowing it to be reused many times with no side effects/leaks related to its history. By this definition, it could also be autoconfigured (as done here, see update in FrameworkExtension). See wording in the docblock in the attached source code. Ideally, I'd like this package to provide not only interfaces, by also generic traits, and reference test suites when possible. We could work on adding more abstractions during the 4.2 cycle. WDYT? ## Here is the attached README: A set of abstractions extracted out of the Symfony components. Can be used to build on semantics that the Symfony components proved useful - and that already have battle tested implementations. Design Principles ----------------- * contracts are split by domain, each into their own sub-namespaces; * contracts are small and consistent sets of PHP interfaces, traits, normative docblocks and reference test suites when applicable, etc.; * all contracts must have a proven implementation to enter this repository; * they must be backward compatible with existing Symfony components. FAQ --- ### How to use this package? The abstractions in this package are useful to achieve loose coupling and interoperability. By using the provided interfaces as type hints, you are able to reuse any implementations that match their contracts. It could be a Symfony component, or another one provided by the PHP community at large. Depending on their semantics, some interfaces can be combined with autowiring to seamlessly inject a service in your classes. Others might be useful as labeling interfaces, to hint about a specific behavior that could be enabled when using autoconfiguration or manual service tagging (or any other means provided by your framework.) ### How is this different from PHP-FIG's PSRs? When applicable, the provided contracts are built on top of PHP-FIG's PSR. We encourage relying on them and won't duplicate the effort. Still, the FIG has different goals and different processes. Here, we don't need to seek universal standards. Instead, we're providing abstractions that are compatible with the implementations provided by Symfony. This should actually also contribute positively to the PHP-FIG (from which Symfony is a member), by hinting the group at some abstractions the PHP world might like to take inspiration from. ### Why isn't this package split into several packages? Putting all interfaces in one package eases discoverability and dependency management. Instead of dealing with a myriad of small packages and the corresponding matrix of versions, you just need to deal with one package and one version. Also when using IDE autocompletion or just reading the source code, it makes it easier to figure out which contracts are provided. There are two downsides to this approach: you may have unused files in your `vendor/` directory, and in the future, it will be impossible to use two different sub-namespaces in different major versions of the package. For the "unused files" downside, it has no practical consequences: their file sizes are very small, and there is no performance overhead at all since they are never loaded. For major versions, this package follows the Symfony BC + deprecation policies, with an additional restriction to never remove deprecated interfaces. Resources --------- * [Documentation](https://symfony.com/doc/current/components/contracts.html) * [Contributing](https://symfony.com/doc/current/contributing/index.html) * [Report issues](https://github.com/symfony/symfony/issues) and [send Pull Requests](https://github.com/symfony/symfony/pulls) in the [main Symfony repository](https://github.com/symfony/symfony) Commits ------- 8982036 Added symfony/contracts: a set of abstractions extracted out of the components
\o/ thank you all for the discussion, to the core team for their support and to fabpot for merging. |
I'd love to help. I would start with the event interfaces as I suggested in my comment: #27093 (comment) Do you track somewhere what needs to be done and who's already working on which part? |
I just created https://github.com/symfony/symfony/projects/7 so we can track ideas and progress. |
A set of abstractions extracted out of the Symfony components.
This is a topic I've been thinking about for a long time. I feel like the time has come for Symfony to publish some abstractions so that people could build on them in a decoupled way.
I've identified interfaces in some components that would greatly benefit from being moved out from the components where they are for now. E.g. #26929 is something that has a broader scope than the Cache component itself.
By putting them in a new
symfony/abstractions
package, we would allow more innovation in the Symfony community, at the abstraction level.In order to start small, I propose only one interface that gathers a concept that is shared amongst many components already:
ResetInterface
. It would provide a standardreset()
method, whose purpose is to set an object back to its initial state, allowing it to be reused many times with no side effects/leaks related to its history. By this definition, it could also be autoconfigured (as done here, see update in FrameworkExtension). See wording in the docblock in the attached source code.Ideally, I'd like this package to provide not only interfaces, by also generic traits, and reference test suites when possible. We could work on adding more abstractions during the 4.2 cycle. WDYT?
Here is the attached README:
A set of abstractions extracted out of the Symfony components.
Can be used to build on semantics that the Symfony components proved useful - and
that already have battle tested implementations.
Design Principles
docblocks and reference test suites when applicable, etc.;
FAQ
How to use this package?
The abstractions in this package are useful to achieve loose coupling and
interoperability. By using the provided interfaces as type hints, you are able
to reuse any implementations that match their contracts. It could be a Symfony
component, or another one provided by the PHP community at large.
Depending on their semantics, some interfaces can be combined with autowiring to
seamlessly inject a service in your classes.
Others might be useful as labeling interfaces, to hint about a specific behavior
that could be enabled when using autoconfiguration or manual service tagging (or
any other means provided by your framework.)
How is this different from PHP-FIG's PSRs?
When applicable, the provided contracts are built on top of PHP-FIG's PSRs. But
the group has different goals and different processes. Here, we're focusing on
providing abstractions that are useful on their own while still compatible with
implementations provided by Symfony. Although not the main target, we hope that
the declared contracts will directly or indirectly contribute to the PHP-FIG.
Why isn't this package split into several packages?
Putting all interfaces in one package eases discoverability and dependency
management. Instead of dealing with a myriad of small packages and the
corresponding matrix of versions, you just need to deal with one package and one
version. Also when using IDE autocompletion or just reading the source code, it
makes it easier to figure out which contracts are provided.
There are two downsides to this approach: you may have unused files in your
vendor/
directory, and in the future, it will be impossible to use twodifferent sub-namespaces in different major versions of the package. For the
"unused files" downside, it has no practical consequences: their file sizes are
very small, and there is no performance overhead at all since they are never
loaded. For major versions, this package follows the Symfony BC + deprecation
policies, with an additional restriction to never remove deprecated interfaces.
Resources
send Pull Requests
in the main Symfony repository