2017 © Pedro PelĂĄez
 

library php-partial-constructor

Partial constructor application.

image

php-partial-constructor/php-partial-constructor

Partial constructor application.

  • Thursday, August 22, 2013
  • by rubendg
  • Repository
  • 0 Watchers
  • 3 Stars
  • 35 Installations
  • PHP
  • 0 Dependents
  • 0 Suggesters
  • 0 Forks
  • 0 Open issues
  • 1 Versions
  • 0 % Grown

The README.md

Schönfinkelize your constructor - partial constructor application for PHP

Build Status, (*1)

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)

  • For simple cases replaces to need for applying the builder pattern
  • No more setters for attributes that should be provided in the constructor
  • Type hinting support for partially constructed classes (albeit of minimal expressiveness, see below)
  • Until all parameters are provided every parameter can accessed by name and or overwritten
  • Partially applied objects can be passed around like ordinary classes
  • Independent of dependency injection framework
  • Type hints of target class are checked during parameter application (fail-fast)
  • The library also plays nice with Symfony DIC. A subset of the actual constructor parameters can be passed directly into the partial constructor:
<service id="a" class="A"/>

<service id="foo" class="Foo_">
   <argument type="collection">
      <argument key="a" id="a" type="service"/>
   </argument>
</service>

Disadvantages:, (*16)

  • Induces a slight performance overhead.
./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)

  • Maybe provide class generation based on PHP annotations @Curried
  • Maybe replace eval with something like this: http://www.whitewashing.de/2010/12/18/generate-proxy-code-using-a-stream-wrapper.html
  • Reflect the number of expected parameters in the type of a partially constructed class.

Related:, (*19)

  • Constructor currying with Google Guice: http://slesinsky.org/brian/code/guice_with_curry.html
  • PHP function currying: https://github.com/reactphp/curry
  • PHP functional prelude: https://github.com/lstrojny/functional-php
  • Currying vs partial application: http://allthingsphp.blogspot.nl/2012/02/currying-vs-partial-application.html
  • Currying vs partial function application in C#: http://msmvps.com/blogs/jon_skeet/archive/2012/01/30/currying-vs-partial-function-application.aspx

The Versions

22/08 2013

dev-master

9999999-dev https://github.com/rubendg/php-partial-constructor

Partial constructor application.

  Sources   Download

MIT

The Requires

  • php >=5.4

 

The Development Requires

by Ruben de Gooijer

functional curry constructor partial currying