[wp-trac] [WordPress Trac] #37699: Death to Globals Episode #1: A Registry, A Pattern

WordPress Trac noreply at wordpress.org
Sat Sep 3 18:58:48 UTC 2016


#37699: Death to Globals Episode #1: A Registry, A Pattern
----------------------------+------------------
 Reporter:  wonderboymusic  |       Owner:
     Type:  enhancement     |      Status:  new
 Priority:  normal          |   Milestone:  4.7
Component:  General         |     Version:
 Severity:  normal          |  Resolution:
 Keywords:                  |     Focuses:
----------------------------+------------------

Comment (by ChriCo):

 Howdy.

 as i see, @schlessera found enough valid arguments for this "discussion"
 above, so we can continue the work on a integration of a ServiceContainer
 into WordPress.

 Since we're also working on composer-integration (#36335), we should maybe
 think - even it's still far far away and no PHP > 5.2 - about the
 integration of some PSR-"standards". The PSR-group has currently a
 proposal for the integration of Containers (https://github.com/php-fig
 /fig-standards/blob/master/proposed/container.md), which should be at
 least known and respected when developing something similar for WordPress.

 Based on the topics above there should also be discussed how the
 ServiceContainer should be work.

 == 0. Naming ==
 The Container should be called {{{WP_Service_Container}}} and be placed
 into its own subfolder called {{{service}}}. This - based on my notes
 above - follows not directly the PSR-standards but in future we ''maybe''
 switch to namespaces which leads us to PSR-4 (https://github.com/php-fig
 /fig-standards/blob/master/accepted/PSR-4-autoloader.md) and to
 {{{WP\Service\Container}}} --> {{{/service/Container.php}}}. ''Note:'' i
 just want to keep - here - the possibility to improve even further in
 future.. ;)

 == 1. API Design ==
 It needs to be discussed how the API should look like.

 === WP_Service_Container ===
 This seems to be the major class which has basic implementation of
 {{{get}}}, {{{set}}}, {{{has}}}. It needs to be discussed what..

 - {{{get}}} returns/throws if something is accessed which is not there.
 - how to implemented {{{protected}}} values which are not overwriteable
 and how to handle setting a protected value.
 - how to implemented creation of new instances --> e.g. {{{factory}}} -
 see Pimple http://pimple.sensiolabs.org/#defining-factory-services.
 - what should be extended/implemented to the class (such as
 {{{ArrayAccess}}})
 - naming rules of the {{$key}} such as "prefix every internal thing with
 the "vendor"-name and seperate it with dot's. Configurations are called
 e.G. {{{wp.config.db}}}) and classes are called e.G. {{{wp.db}}})
 - LazyLoading of instances when they are first accessed via {{{set}}}
 - ...

 Short example of usage:

 {{{#!php
 <?php
 $c = new WP_Service_Container();

 // ---------------
 // set-get-has
 $c->set( 'foo', 'bar' );
 echo $c->get( 'foo' ); // bar
 var_dump(
         $c->has( 'foo' ),
         $c->has( 'lorum ipsum' )
 ); // TRUE, FALSE

 // ---------------
 // protected
 $c->protected( 'foo', 'bar' );
 $c->set( 'foo', 'b..arrrrr i\'m a pirate' ); // '''not''' possible -
 exception or at least return FALSE;
 echo $c->get( 'foo' ); // bar

 // factory
 // just an example, since we're not able to use closures we maybe need to
 build something around it.
 // $key, $class_name (instance of class name is the returned value), $args
 $c->factory( 'wp.db', 'wpdb', array( 'db-user', 'db-pass', 'db-name', 'db-
 host' ) );
 $wpdb1 = $c->get( 'wp.db' ); // complete new instance of wpdb
 $wpdb2 = $c->get( 'wp.db' ); // complete new instance of wpdb
 }}}


 === Configurations ===
 The ServiceContainer on its own is a nice thing. But one step further we
 can think about the generalization of configurations (such as database-
 credentials, debug modes, ... stuff from {{{wp-config.php}}} and
 {{{define}}}). The configurations should be "protected" (not changeable)
 and grouped by "type" such as "db", "debug", ...

 Short example to give you a quick view on it:

 {{{#!php
 <?php
 // get somehow the service container..
 // and set the database configuration as "protected" to disallow
 overwriting the values.
 $service_container->protected(
         'wp.config.db',
         array(
                 'user' => 'db-user',
                 'pass' => 'db-pass',
                 'name' => 'db-name',
                 'host' => 'localhost'
         )
 );

 // later on in code when setting up the wpdb
 $conf = $service_container->get( 'wp.config.db' );
 $service_container->protected(
         'wp.db',
         new wpdb(
                 $conf[ 'user' ],
                 $conf[ 'pass' ],
                 $conf[ 'name' ],
                 $conf[ 'host' ]
         )
 );
 }}}

 == 2. $GLOBALS vs. Singleton vs. instance-holding function vs. hook ==
 Since we're hardly depending on some {{{$GLOBALS}}} in every single
 situation, its maybe not the best choice to add the ServiceContainer to
 the globals to remove some of them. On the other hand, if we don't rely on
 a global available class, it's highly possible, that the ServiceContainer
 will contain static methods and implement some Singleton-Pattern.


 === v1 - global ===
 But it to global..than its accessible to everyone and overwritable too..

 {{{#!php
 <?php
 $GLOBALS[ 'wp_service_container' ] = new WP_Service_Container();
 // ...later on
 $GLOBALS[ 'wp_service_container' ]->set( $key, $value );
 $value = $GLOBALS[ 'wp_service_container' ]->get( $key );
 }}}

 === v2 - singleton ===
 Singleton, no global variable required..but everything is static. Not the
 best choice here since mocking in UnitTests could be getting much more
 difficulty.

 {{{#!php
 <?php
 WP_Service_Container::get_instance();
 // ...later on
 WP_Service_Container::set( $key, $value );
 $value = WP_Service_Container::get( $key );
 }}}

 === v3 - instance holding function ===
 The intance-holding function is a mix of "global"-accessible and
 singleton, but no static methods/variables required. At least worth to
 discuss about it.

 {{{#!php
 <?php
 function wp_service_container( ) {
         static $instance;
         if ( $instance === NULL ) {
                 $instance = new WP_Service_Container();
         }

         return $instance;
 }

 // ...later on
 wp_service_container()->set( $key, $value );
 $value = wp_service_container()->get( $key );
 }}}

 === v4 - hook ===
 The hook seems to be another valid solution here, since we're not having
 any global variable and its accessible on a very specific point for
 everyone. The main problem here is, that the complete core has to rely on
 this single hook to get everything which is stored into the container. So
 everything which needs access to a {{{$value}}} needs to use this hook,
 which means we could maybe run into "priority"-problems.

 {{{#!php
 do_action( 'wp_service_container', new WP_Service_Container() );

 // ...later on
 add_action( 'wp_service_container', function(
 WP_Service_Container_Interface $container ) {
         $container->set( $key, $value );
         $value = $container->get( $key );
 } );
 }}}

 This needs to be discussed since we're still relying heavily on global
 variables in every single template. I would not implement a Singleton-
 class and/or something with static methods/variables here, since we're
 also having a lot of disadvantages with it. A good way could be v3, since
 we can mock the function in unit tests and access it easily everywhere.
 But could also be a main disadvantage, if the container is always
 accessible.

 ----

 For now i think thats enough input. We should define some basics and
 afterwards we can continue with more features such es ConfigBuilder's
 (e.G. {{{WP_Service_ConfigBuilder_PluginConfig}}} whic parses the Plugin
 file headers:

 {{{#!php
 <?php
 $service_container->set( 'my-plugin.config', new
 WP_Service_ConfigBuilder_PluginConfig( __FILE__ ) );
 $config = $service_container->get( 'my-plugin.config' );

 echo $config[ 'textdomain' ]; // yay, the textdomain!
 }}}


 Have a nice weekend!

--
Ticket URL: <https://core.trac.wordpress.org/ticket/37699#comment:73>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list