[wp-trac] [WordPress Trac] #37699: Death to Globals Episode #1: A Registry, A Pattern
WordPress Trac
noreply at wordpress.org
Sat Sep 3 22:51:30 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 jacobsantos):
I think your design is too "WordPress", in that it condenses what should
be multiple methods or classes to a single method. I would also suggest
The PHP League Container library. The problem with Pimple or really any
container library is that it still needs to be accessed in some way.
Something you mention below. Whether that retrieval is in WP or some other
Singleton, that class would not be a container. It would hold the
container.
I don't believe you looked at MimicCMS Storage implementation. As a POC, I
would rather you used it as a foundation since the ideas are:
1. The storage container should be separate from the retrieval. This
should allow for proper testing of each part of the service container
without interference of other pieces of WordPress code. Proper unit test
cases, instead of the herculean effort found currently in the WordPress
test suite to make the test cases as unit as possible. It is easier to
unit test something that is meant to be independent than to make something
that is not meant to be independent unit testable.
1. The focus of the service container implementation should be separable
from WordPress. This means that you would have another service container
implementation in the wild that could gain traction outside of WordPress
and therefore support larger than the maintainers. I doubt this would
happen as there would likely be a split between what is included in
WordPress and the progress of any library. The other, more important,
reason is so that the implementation in WordPress could be replaced with
another library at a later date. Think WP_HTTP with Requests, except
WP_Service with Pimple or League Container.
The reason I don't recommend Pimple is that array access is a bit more
difficult to work with than object methods.
{{{#!php
<?php
$service = WP::service();
$wpdb = $service['wpdb'];
/// ---------------
$service = WP::service();
$service['wpdb'] = $service->protect(function() {
return new wpdb;
});
$service['event'] = function() {
return new WP_Event;
};
}}}
Compare that to League's Container library
{{{#!php
<?php
WP::service()->share('wpdb', new wpdb());
WP::service()->get('wpdb');
}}}
It seems the primary advantage and the reason Pimple is being chosen is
because it is simple with few lines of code. Practically, the only thing
you would need to do to make it PHP5.2 is to remove the namespace and
replace the acceptance of closures. If you want to remove SPL dependency
and you should do this anyway, you would just rename the ArrayAccess
methods.
I will say that the protection and service layer of MimicCMS Storage is
within the classes you pass. It is a better candidate for PHP7 with
anonymous classes, but I think it would better serve you to imitate the
composition and design of it than Pimple. The PHP League Container library
may seem more involved, but once you remove the interfaces and drill down
to its code, it isn't any more or less complex than MimicCMS Storage.
== What is This? ==
Replying to [comment:73 ChriCo]:
> {{{#!php
> <?php
> $c = new WP_Service_Container();
> $c->protected( 'foo', 'bar' );
> // ---------------
> // 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
> }}}
Again, the design seems to accept arbitrary data types. This is unwise and
I would recommend instead creating a class to contain the data types that
are not objects.
Prefer instead:
{{{#!php
<?php
final class Storage_Factory_Wpdb implements Storage_FactoryInterface {
// Implementation creates and stores wpdb instance.
}
final class Storage_Data implements Storage_DataInterface {
private $data;
public function __construct($data) {
$this->data = $data;
}
public function get() {
return $this->data;
}
public function set($data) {
$this->data = $data;
}
}
final class Storage_ProtectedData implements Storage_DataInterface {
private $data;
public function __construct($data) {
$this->data = $data;
}
public function get() {
return $this->data;
}
public function set($data) { }
}
$c = new WP_Service_Container();
$c->protected( 'foo', new Storage_Data('bar') );
// ---------------
// protected
$c->protected( 'foo', new Storage_Data('bar') );
$c->set( 'foo', new Storage_Data("b..arrrrr i'm a pirate") ); // '''not'''
possible - exception or at least return FALSE;
echo $c->get( 'foo' )->get(); // 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', new Storage_Factory_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
}}}
The concept is more that if you always return an object, then you can do
more with it. Take a facade for example.
{{{#!php
<?php
class Facade_WP_Database extends WP_Facade {
protected function _serviceName() {
return 'wp.db';
}
}
Facade_WP_Database::insert( 'table', array( 'fieldName' => 'data' ) );
}}}
{{{#!php
<?php
class Facade_Foo extends WP_Facade {
protected function _serviceName() {
return 'foo';
}
}
echo Facade_Foo::get(); // bar -- from above.
}}}
Sure, it borrows much from Laravel, but it is simple enough to implement
with a service container. The point is that it doesn't matter what the
implementation for registering and retrieving from the service container,
because no one is going to use it. Or more that there is a better, easier
way to use it.
Tell people that there is an easier API with a Facade and see if people
move over to it. Laravel is MIT licensed, you would just have to backport
and provide attribution, but given that the implementation is basic and
simple, it shouldn't be that difficult to implement, if you don't want to
look at Laravel.
That it is more verbose is a nature of supporting a PHP version that
doesn't support many of the features in PHP5.3 and above. Since it is just
objects and closures are objects, it should be fine to store closures and
execute them when retrieved.
It also allows for more composition with the code, which should open the
possibilities and extensions a lot more than a simple array would.
--
Ticket URL: <https://core.trac.wordpress.org/ticket/37699#comment:74>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list