jclaveau/php-deferred-callchain

注册一个稍后将在实例上应用的调用链

1.4.2 2019-11-19 11:23 UTC

This package is auto-updated.

Last update: 2024-09-19 22:15:29 UTC


README

这个类简单地提供了一种在目标(对象或原生值)可用之前定义流畅的方法、函数、数组访问调用的方式。一旦预期的目标可用,只需像调用函数一样调用链即可。

Latest Stable Version License Total Downloads contributions welcome

质量

Build Status codecov Scrutinizer Code Quality

概述

// having
class MyClass
{
    protected $name = 'unnamed';
    
    public function setName($name)
    {
        $this->name = $name;
        return $this;
    }
    
    public function nameEntries()
    {
        return [
            'my_entry_1' => $this->name . " 1",
            'my_entry_2' => $this->name . " 2",
        ];
    }
}

// We can define some chained calls (none is executed)
$doDummyCallsLater = DeferredCallChain::new_( MyClass::class )  // Targets must be MyClass instances
    ->nameEntries()['my_entry_2']                               // access array entry
    ->strtoupper()                                              // apply strtoupper() to it
    ;

// do whatever we want
// ...

// Get your targets
$myInstance1 = (new MyClass)->setName('foo');
$myInstance2 = (new MyClass)->setName('bar');

// Execute the callchain
echo $doDummyCallsLater( $myInstance1 ); // => FOO 2
echo $doDummyCallsLater( $myInstance2 ); // => BAR 2

安装

php-deferred-callchain 可以通过 Composer 安装

composer require jclaveau/php-deferred-callchain

测试

测试位于 此处,可通过调用

./phpunit

用法

函数和链式构造

DeferredCallChain 可以以传统方式实例化

$nameRobert = (new DeferredCallChain(Human::class))->...

静态地

$nameRobert = DeferredCallChain::new_(Human::class)->...

或功能性地

$nameRobert = later(Human::class)->...

流畅的调用链

$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')"

与数组一起工作

$getSubColumnValue = DeferredCallChain::new_()
    ['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']"

与原生类型和函数一起工作

上述功能使对对象方法的调用变得简单且异步,但当他们返回的不是对象时,流畅的语法必须停止,异步行为也必须停止。

基于 Daniel S Deboer 的工作 https://github.com/danielsdeboer/pipe,已添加了对链式函数调用的支持。

class MyClass
{
    public function getString()
    {
        return 'string';
    }
}

$upperifyMyClassString = DeferredCallChain::new_( MyClass::class )
    ->getString()
    ->strtoupper();

echo $upperifyMyClassString( new MyClass ); // prints "STRING"

某些函数不使用流畅语法的主题作为第一个参数。在这种情况下,将 '$$' 作为参数提供,以便将其替换为主题。

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']

指定链可以在哪个类、接口、类型或实例上调用

您可以强制您的调用链的目标

  • 成为特定类的实例
$nameRobert = DeferredCallChain::new_(Alien::class)
    ->setName('Muda')
    ->setFirstName('Robert')
    ;

$mySubjectIMissedBefore = new Human;
$robert = $nameRobert( $mySubjectIMissedBefore );

// throws BadTargetClassException
  • 实现特定接口
$getCount = DeferredCallChain::new_("\Traversable")
    ->count()
    ;

$myCountableIMissedBefore = new CountableClass; // class implementing Countable

// throws BadTargetInterfaceException
  • 是特定原生类型
$nameRobert = DeferredCallChain::new_("array")
    ->setName('Muda')
    ->setFirstName('Robert')
    ;

$mySubjectIMissedBefore = new Human;
$robert = $nameRobert( $mySubjectIMissedBefore );

// throws BadTargetTypeException
  • 是构造时提供的特定实例
$myTarget = new Human;
$nameRobert = DeferredCallChain::new_($myTarget)
    ->setName('Muda')
    ->setFirstName('Robert')
    ;

$robert = $nameRobert( new Human );

// throws TargetAlreadyDefinedException

引发异常的调用

由于调用可以在实际应用之前很久就进行,因此异常需要更多的调试信息以实现流畅的工作流程。为此,在 DeferredCallChain 执行期间抛出的每个异常消息中添加了一行,指向有问题的调用以及它的编码位置。

例如,消息为 一些用户代码抛出了一个异常 的异常将打印

An exception has been thrown by some user code
When applying (new JClaveau\Async\DeferredCallChain( <instance id> ))->previousSuccessfullCall()->buggyCall('Robert') defined at <file>:<line>

静态调用

静态调用可能很有用,特别是对于单例。由于一些技术原因,如 此处 所述,支持它的唯一方法是像普通方法(例如,使用 ->)一样调用它们,一旦我们知道它们不是普通方法,就将其视为静态方法。

later(MyModel::class)->getInstance()->myNormalGetter();
// or
later($myModel)->getInstance()->myNormalGetter(); // like $myModel::getInstance()