This class simply provides a way to define fluent chain of method calls before having the instance you wan't to applty it to. Once the expected instance is available, simply call the chain on it. It can now also handle function calls on non-objects and access array entries.
php-deferred-callchain is installable via Composer
composer require jclaveau/php-deferred-callchain
$nameRobert = DeferredCallChain::new_()
->setName('Muda')
->setFirstName('Robert')
;
$mySubjectIMissedBefore = new Human;
$robert = $nameRobert( $mySubjectIMissedBefore );
echo $robert->getFullName(); // => "Robert Muda"
echo (string) $nameRobert; // => "(new JClaveau\Async\DeferredCallChain)->setName('Muda')->setFirstName('Robert')"
DeferredCallChain can be instanciated classically
$nameRobert = (new DeferredCallChain(Human::class))
->setName('Muda')->setFirstName('Robert');
Statically
$nameRobert = DeferredCallChain::new_(Human::class)
->setName('Muda')->setFirstName('Robert');
Or functionnaly
$nameRobert = later(Human::class)
->setName('Muda')->setFirstName('Robert');
$getSubColumnValue = (new DeferredCallChain)
['column_1']
['sub_column_3']
;
$sub_column_3_value = $getSubColumnValue( [
'column_1' => [
'sub_column_1' => 'lalala',
'sub_column_2' => 'lololo',
'sub_column_3' => 'lilili',
],
'column_2' => [
'sub_column_1' => 'lululu',
'sub_column_2' => 'lelele',
'sub_column_3' => 'lylyly',
],
] );
echo $sub_column_3_value; // => "lilili"
echo (string) $getSubColumnValue; // => "(new JClaveau\Async\DeferredCallChain)['column_1']['sub_column_3']"
The features above make calls to objects methods easy and async but when their result is not an object, the fluent syntax has to stop, and the async behavior also.
Based on Daniel S Deboer work https://github.com/danielsdeboer/pipe, support of chained function calls has been added.
class MyClass
{
public function getString()
{
return 'string';
}
}
$upperifyMyClassString = DeferredCallChain::new_( MyClass::class )
->getString()
->strtoupper();
echo $upperifyMyClassString( new MyClass ); // prints "STRING"
Some functions do not use the subject of the fluent syntax as first argument. In this case, giving '$$' as the parameter you want to be replaced by the subject.
class MyClass
{
public function getSentence()
{
return 'such a funny lib to implement';
}
}
$explodeMyClassSentence = DeferredCallChain::new_( MyClass::class )
->getSentence()
->explode(' ', '$$');
$explodeMyClassSentence( new MyClass ); // returns ['such', 'a', 'funny', 'lib', 'to', 'implement']
You can force the target of your call chain to:
- be an instance of a specific class
$nameRobert = DeferredCallChain::new_("Alien")
->setName('Muda')
->setFirstName('Robert')
;
$mySubjectIMissedBefore = new Human;
$robert = $nameRobert( $mySubjectIMissedBefore );
// throws BadTargetClassException
- implement a specific interface
$getCount = (new DeferredCallChain("\Traversable"))
->count()
;
$myCountableIMissedBefore = new CountableClass; // class implementing Countable
// throws BadTargetInterfaceException
- be of a specific native type
$nameRobert = DeferredCallChain::new_("array")
->setName('Muda')
->setFirstName('Robert')
;
$mySubjectIMissedBefore = new Human;
$robert = $nameRobert( $mySubjectIMissedBefore );
// throws BadTargetTypeException
- be a specific instance given at construction
$myTarget = new Human;
$nameRobert = DeferredCallChain::new_($myTarget)
->setName('Muda')
->setFirstName('Robert')
;
$robert = $nameRobert( new Human );
// throws TargetAlreadyDefinedException
As a call can be made far before it's effectivelly applied, exceptions need more debug information for a smooth workflow. To achieve that, a line is added to every exception message thrown during a DeferredCallChain execution, pointing to the buggy call and where it is coded.
For example, an exception having as message An exception has been thrown by some user code
will print
An exception has been thrown by some user code
When applying (new JClaveau\Async\DeferredCallChain( <instance id> ))->previousSuccessfullCall()->buggyCall('Robert') called in <file>:<line>
Static calls can be useful, especially for singletons. For some technical reasons explained here (#9), the only way to support it is to call them as normal methods (e.g. with -> ) and look for it as a static method once we know it doesn't exist as as regular one.
later(MyModel)->getInstance()->myNormalGetter();
// or
later(MyModel::class)->getInstance()->myNormalGetter();