[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