[wp-trac] [WordPress Trac] #37699: Death to Globals Episode #1: A Registry, A Pattern
WordPress Trac
noreply at wordpress.org
Sun Feb 12 17:35:22 UTC 2017
#37699: Death to Globals Episode #1: A Registry, A Pattern
----------------------------+------------------------------
Reporter: wonderboymusic | Owner:
Type: enhancement | Status: new
Priority: normal | Milestone: Awaiting Review
Component: General | Version:
Severity: normal | Resolution:
Keywords: | Focuses:
----------------------------+------------------------------
Comment (by xedin.unknown):
Hi!
Firstly, I have not read every word of every comment here, so apologies if
this has already been mentioned. Recently, I have come across a problem in
plugin development, the best solution for which seems to be DI containers.
I have found this thread only now, when the solution is mostly ready, so
even though I'm not 100% fixed on anything, most of the solution will
likely be included in the next version of the plugin I maintain. Allow me
to share some of it with you.
Surely, a DI container in WordPress would be great. I understand that it
may take a lot of time before we see a new version of WordPress that ships
with a DI container implementation, but I hope that this day will come.
Which is why I created a function called `wp_container()` which returns a
[https://github.com/container-interop/container-
interop/blob/master/src/Interop/Container/ContainerInterface.php
ContainerInterface]. It is only defined if not function exists, so if it
is implemented by WordPress or another plugin in the future, it will cause
no problems, as long as it does more or less the same thing.
Now, I guess it's worth mentioning that I'm using
[https://github.com/Dhii/di my own container implementation]. This is
mostly because I could not find an existing implementation that would be
at the same time simple, standards-compliant, and PHP 5.3 compatible (I
maintain a line of plugins that require PHP >= 5.3.9). My container
accepts definitions in the form of a [https://github.com/container-interop
/service-provider/blob/master/src/ServiceProvider.php ServiceProvider] via
[https://github.com/Dhii/di-
interface/blob/master/src/WritableContainerInterface.php
WritableContainerInterface], implements [https://github.com/container-
interop/container-interop/blob/master/docs/Delegate-lookup-meta.md lookup
delegation] via [https://github.com/Dhii/di-
interface/blob/master/src/ParentAwareContainerInterface.php
ParentAwareContainerInterface] and [https://github.com/Dhii/di-
interface/blob/master/src/CompositeContainerInterface.php
CompositeContainerInterface]. It is possible to use the same service
definitions for creating multiple instances (like if you need to populate
an array of objects) by using [https://github.com/Dhii/di-
interface/blob/master/src/FactoryInterface.php FactoryInterface].
Back to `wp_container()`, this function maintains a singleton of
`ContainerInterface` in the form of
[https://github.com/Dhii/di/blob/master/src/CompositeContainer.php
CompositeContainer] via a static variable, and when initializing it for
the first time, this object is exposed to the environment via action
`wp_container_init`. Handlers of this action are guaranteed to receive a
[https://github.com/Dhii/di-
interface/blob/master/src/WritableCompositeContainerInterface.php
WritableCompositeContainerInterface], which allows extensions to hook in
their own containers with their own service definitions. This allows for a
very flexible approach, where all extensions can add their own service
definitions using common pattern, but at the same time are in complete
control of what happens within their domain.
In my case, I use the `wp_container_init` hook to attach another composite
container to the global one in my main plugin, and then each of my
plugin's extensions and the main plugin attach regular
[https://github.com/Dhii/di/blob/master/src/Container.php Container]
instances. Like this, I have a single composite container that is
responsible for resolving dependencies in my plugin and the plugins'
extensions, and then a series of containers, one for each extension, that
stores services specific to those extensions. This is very modular and
convenient. Because I use lookup delegation and service providers, all my
service definitions (i.e. factory closures) accept the main global
WordPress container as their first parameter. This means that my services
can depend on any other service registered anywhere in the WP environment.
Services can, of course, also completely replace other services by being
registered with the same name as an existing service. Lookup takes place
in LIFO order, so services from containers registered later will override
definitions from containers registered before. Services registered by my
plugins are prefixed with a plugin code and a `.` (period), thus forming
namespaces to avoid clashes with WP Core or other services.
I hope this helps. Please let me know what you think. If the Core team
decides that they want to use my container implementation, I would be
willing to work on a stable version, which could slightly change the way I
currently do things, for the benefit of all. For example, I plan to
replace `WritableContainerInterface` with a `WritableRegistryInterface`,
so that it could be re-used in other projects that need similar methods,
and de-couple it from the `ContainerInterface`. This would allow
developers to not depend on `wp_container_init` returning a container, but
anything that can register services that would later be available via
`wp_container_init`. A separate function in the global namespace will be
put in to retrieve `FactoryInteface`, so as not to require the factory and
the container to be the same things (they currently are, but will be
separated in a future version, and factory will get DI'd into container).
Finally, I have not yet tested with PHP 5.2, but I don't remember dong
anything incompatible with PHP 5.2 in the implementation, aside from
providing service definitions in the form of closures. This is not
required, however, as long as the definition is something that can be
invoked, can receive the required params, and can return the service
instance. If necessary, I would be willing to include PHP 5.2 in the list
of platforms I run tests in, and make some changes to the code if needed.
In the future, of course I plan to require a higher version of PHP, but
1.x will be compatible with lower versions.
All the best!
--
Ticket URL: <https://core.trac.wordpress.org/ticket/37699#comment:113>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list