dev-master
9999999-dev https://github.com/rubendg/php-partial-constructorPartial constructor application.
MIT
The Requires
- php >=5.4
The Development Requires
by Ruben de Gooijer
functional curry constructor partial currying
Partial constructor application.
A library for partially applying object constructors. Instead of providing all constructor arguments it puts you in control over when you provide which constructor arguments., (*2)
Install using Composer:, (*3)
php composer.phar require php-partial-constructor/php-partial-constructor:dev-master php composer.phar update
A quick example. Suppose you have a class Foo:, (*4)
class Foo { public function __construct(A $a, B $b) {} }
and you want to provide A, but provide B at a later moment. Normally this would not be possible, and one has to reach to other means like using a setter:, (*5)
class Foo { public function __construct(A $a) {} public function setB(B $b) {} }
This would however imply that every method relying on the presence of B will need an additional consistency check. If B really is an integral part of what it means to be a Foo it should belong in the constructor, not in a setter see., (*6)
Using the library it is possible to maintain the previous design with a little extra help:, (*7)
$foo = new Foo_; $fooWithA = $foo->apply(['a' => new A]);
Note that we did not create an instance of Foo but of Foo_. Foo_ acts as a wrapper around the construction process of Foo. Using its apply method you can provide Foo's constructor arguments as an array. If all arguments are provided you get an instance of Foo. Otherwise you will get a new instance of Foo_ that still needs some parameters in order to become a Foo., (*8)
Foo_'s definition is quite easy:, (*9)
class Foo_ implements \ArrayAccess { use Schoenfinkel\Schoenfinkelize; protected static $targetClassName = 'Foo'; }
All you have to do is let "$targetClassName* point to Foo. The rest is handled by the Schoenfinkelize trait., (*10)
Obviously creating such companion classes quickly becomes tiresome. Luckily you can get rid of the manual boilerplate by generating the classes on the fly., (*11)
If you are in a Composer project all you have to do is create your own "autoload.php" file adapting the one generated by Composer:, (*12)
use \Schoenfinkel\Integration\CodeGenerator; use \Schoenfinkel\Integration\PostfixClassNameMapping; use \Schoenfinkel\Integration\ClassLoader\SchoenfinkelClassLoader; $loader = require __DIR__ . '/../vendor/autoload.php'; $custom = new SchoenfinkelClassLoader($loader, new CodeGenerator(new PostfixClassNameMapping())); $loader->unregister(); spl_autoload_register(array($custom, 'loadClass'));
By default classes which have an underscore at the end will be treated as classes that can have their constructor partially applied. The class name without the underscore will become the target class name. Of course you can change this behavior to your own liking by either setting a different postfix on the PostfixClassNameMapping or implement your own ClassNameMapping., (*13)
Note that currently the implementation uses eval for bringing the generated code into scope. This may be a problem for you if you have disabled eval in the disabled_functions directive., (*14)
To sum up some of the libraries' advantages:, (*15)
<service id="a" class="A"/> <service id="foo" class="Foo_"> <argument type="collection"> <argument key="a" id="a" type="service"/> </argument> </service>
Disadvantages:, (*16)
./vendor/bin/phpunit test/performance/PerformanceTest.php
Shows that plain object construction is about 24 times faster. - Does not work for constructors that take a variable amount of arguments (using func_get_args()) - Default and optional arguments are currently treated as regular (required) arguments. - Currently a partially constructed class still expecting 1 parameter cannot be distinguished in its type from the same partially constructed class expecting any other number of parameters., (*17)
Future:, (*18)
Related:, (*19)
Partial constructor application.
MIT
functional curry constructor partial currying