{"id":15028943,"url":"https://github.com/jclaveau/php-deferred-callchain","last_synced_at":"2026-01-19T16:32:54.736Z","repository":{"id":56997426,"uuid":"156361726","full_name":"jclaveau/php-deferred-callchain","owner":"jclaveau","description":"Simple fluent API that can be applied on instances or native variables asynchronously","archived":false,"fork":false,"pushed_at":"2019-11-19T11:37:05.000Z","size":71,"stargazers_count":1,"open_issues_count":3,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-09T01:09:58.816Z","etag":null,"topics":["async","call-chain","deferred","fluent","later","lazy","php5","php7"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jclaveau.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-11-06T09:50:41.000Z","updated_at":"2020-01-09T23:23:33.000Z","dependencies_parsed_at":"2022-08-21T14:50:15.040Z","dependency_job_id":null,"html_url":"https://github.com/jclaveau/php-deferred-callchain","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/jclaveau/php-deferred-callchain","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jclaveau%2Fphp-deferred-callchain","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jclaveau%2Fphp-deferred-callchain/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jclaveau%2Fphp-deferred-callchain/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jclaveau%2Fphp-deferred-callchain/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jclaveau","download_url":"https://codeload.github.com/jclaveau/php-deferred-callchain/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jclaveau%2Fphp-deferred-callchain/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28574372,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-19T16:29:19.148Z","status":"ssl_error","status_checked_at":"2026-01-19T16:29:17.772Z","response_time":67,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","call-chain","deferred","fluent","later","lazy","php5","php7"],"created_at":"2024-09-24T20:09:24.478Z","updated_at":"2026-01-19T16:32:54.720Z","avatar_url":"https://github.com/jclaveau.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PHP Deferred Callchain\nThis class simply provides a way to define fluent chain of methods, functions, \narray access calls before having the target (object or native value) you wan't to apply it to.\nOnce the expected targets are available, simply call the chain on them as if it was a function.\n\n[![Latest Stable Version](https://poser.pugx.org/jclaveau/php-deferred-callchain/v/stable)](https://packagist.org/packages/jclaveau/php-deferred-callchain)\n[![License](https://poser.pugx.org/jclaveau/php-deferred-callchain/license)](https://packagist.org/packages/jclaveau/php-deferred-callchain)\n[![Total Downloads](https://poser.pugx.org/jclaveau/php-deferred-callchain/downloads)](https://packagist.org/packages/jclaveau/php-deferred-callchain)\n[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/jclaveau/php-deferred-callchain/issues)\n\n## Quality\n[![Build Status](https://travis-ci.org/jclaveau/php-deferred-callchain.png?branch=master)](https://travis-ci.org/jclaveau/php-deferred-callchain)\n[![codecov](https://codecov.io/gh/jclaveau/php-deferred-callchain/branch/master/graph/badge.svg)](https://codecov.io/gh/jclaveau/php-deferred-callchain)\n[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/jclaveau/php-deferred-callchain/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/jclaveau/php-deferred-callchain/?branch=master)\n\n## Overview \n```php\n// having\nclass MyClass\n{\n    protected $name = 'unnamed';\n    \n    public function setName($name)\n    {\n        $this-\u003ename = $name;\n        return $this;\n    }\n    \n    public function nameEntries()\n    {\n        return [\n            'my_entry_1' =\u003e $this-\u003ename . \" 1\",\n            'my_entry_2' =\u003e $this-\u003ename . \" 2\",\n        ];\n    }\n}\n\n// We can define some chained calls (none is executed)\n$doDummyCallsLater = DeferredCallChain::new_( MyClass::class )  // Targets must be MyClass instances\n    -\u003enameEntries()['my_entry_2']                               // access array entry\n    -\u003estrtoupper()                                              // apply strtoupper() to it\n    ;\n\n// do whatever we want\n// ...\n\n// Get your targets\n$myInstance1 = (new MyClass)-\u003esetName('foo');\n$myInstance2 = (new MyClass)-\u003esetName('bar');\n\n// Execute the callchain\necho $doDummyCallsLater( $myInstance1 ); // =\u003e FOO 2\necho $doDummyCallsLater( $myInstance2 ); // =\u003e BAR 2\n\n```\n\n\n## Installation\nphp-deferred-callchain is installable via [Composer](http://getcomposer.org)\n\n    composer require jclaveau/php-deferred-callchain\n\n\n## Testing\nTests are located [here](tests/unit/DeferredCallChainTest.php) and runnable\nby calling\n\n    ./phpunit\n\n\n## Usage\n\n  * [Functionnal and chained construction](#functionnal-and-chained-construction)\n  * [Fluent call chain](#fluent-call-chain)\n  * [Working with arrays](#working-with-arrays)\n  * [Working with native types and functions](#working-with-native-types-and-functions)\n  * [Specifying on which class, interface, type or instance, the chain is callable](#specifying-on-which-class-interface-type-or-instance-the-chain-is-callable)\n  * [Calls provoking exceptions](#calls-provoking-exceptions)\n  * [Static calls](#static-calls)\n  * [API Reference](api_reference)\n\n\n### Functionnal and chained construction\n\nDeferredCallChain can be instanciated classically\n```php\n$nameRobert = (new DeferredCallChain(Human::class))-\u003e...\n```\n\nStatically\n```php\n$nameRobert = DeferredCallChain::new_(Human::class)-\u003e...\n```\n\nOr functionnaly\n```php\n$nameRobert = later(Human::class)-\u003e...\n```\n\n\n### Fluent call chain\n```php\n$nameRobert = DeferredCallChain::new_()\n    -\u003esetName('Muda')\n    -\u003esetFirstName('Robert')\n    ;\n\n$mySubjectIMissedBefore = new Human;\n$robert = $nameRobert( $mySubjectIMissedBefore );\n\necho $robert-\u003egetFullName(); // =\u003e \"Robert Muda\"\necho (string) $nameRobert;   // =\u003e \"(new JClaveau\\Async\\DeferredCallChain)-\u003esetName('Muda')-\u003esetFirstName('Robert')\"\n```\n\n\n### Working with arrays\n```php\n$getSubColumnValue = DeferredCallChain::new_()\n    ['column_1']\n    ['sub_column_3']\n    ;\n\n$sub_column_3_value = $getSubColumnValue( [\n    'column_1' =\u003e [\n        'sub_column_1' =\u003e 'lalala',\n        'sub_column_2' =\u003e 'lololo',\n        'sub_column_3' =\u003e 'lilili',\n    ],\n    'column_2' =\u003e [\n        'sub_column_1' =\u003e 'lululu',\n        'sub_column_2' =\u003e 'lelele',\n        'sub_column_3' =\u003e 'lylyly',\n    ],\n] );\n\necho $sub_column_3_value;           // =\u003e \"lilili\"\necho (string) $getSubColumnValue;   // =\u003e \"(new JClaveau\\Async\\DeferredCallChain)['column_1']['sub_column_3']\"\n```\n\n\n### Working with native types and functions\nThe features above make calls to objects methods easy and async but when\ntheir result is not an object, the fluent syntax has to stop, and the async\nbehavior also.\n\nBased on Daniel S Deboer work https://github.com/danielsdeboer/pipe, \nsupport of chained function calls has been added.\n\n```php\nclass MyClass\n{\n    public function getString()\n    {\n        return 'string';\n    }\n}\n\n$upperifyMyClassString = DeferredCallChain::new_( MyClass::class )\n    -\u003egetString()\n    -\u003estrtoupper();\n\necho $upperifyMyClassString( new MyClass ); // prints \"STRING\"\n\n```\n\nSome functions do not use the subject of the fluent syntax as first argument.\nIn this case, giving '$$' as the parameter you want to be replaced by the subject. \n\n```php\nclass MyClass\n{\n    public function getSentence()\n    {\n        return 'such a funny lib to implement';\n    }\n}\n\n$explodeMyClassSentence = DeferredCallChain::new_( MyClass::class )\n    -\u003egetSentence()\n    -\u003eexplode(' ', '$$');\n\n$explodeMyClassSentence( new MyClass ); // returns ['such', 'a', 'funny', 'lib', 'to', 'implement']\n\n```\n\n\n### Specifying on which class, interface, type or instance, the chain is callable\nYou can force the target of your call chain to:\n\n+ be an instance of a specific class\n```php\n$nameRobert = DeferredCallChain::new_(Alien::class)\n    -\u003esetName('Muda')\n    -\u003esetFirstName('Robert')\n    ;\n\n$mySubjectIMissedBefore = new Human;\n$robert = $nameRobert( $mySubjectIMissedBefore );\n\n// throws BadTargetClassException\n\n```\n\n+ implement a specific interface\n\n```php\n$getCount = DeferredCallChain::new_(\"\\Traversable\")\n    -\u003ecount()\n    ;\n\n$myCountableIMissedBefore = new CountableClass; // class implementing Countable\n\n// throws BadTargetInterfaceException\n\n```\n\n+ be of a specific native type\n\n```php\n$nameRobert = DeferredCallChain::new_(\"array\")\n    -\u003esetName('Muda')\n    -\u003esetFirstName('Robert')\n    ;\n\n$mySubjectIMissedBefore = new Human;\n$robert = $nameRobert( $mySubjectIMissedBefore );\n\n// throws BadTargetTypeException\n\n```\n\n+ be a specific instance given at construction\n\n```php\n$myTarget = new Human;\n$nameRobert = DeferredCallChain::new_($myTarget)\n    -\u003esetName('Muda')\n    -\u003esetFirstName('Robert')\n    ;\n\n$robert = $nameRobert( new Human );\n\n// throws TargetAlreadyDefinedException\n\n```\n\n### Calls provoking exceptions\nAs a call can be made far before it's effectivelly applied, exceptions\nneed more debug information for a smooth workflow. To achieve that, \na line is added to every exception message thrown during a DeferredCallChain \nexecution, pointing to the buggy call and where it is coded.\n\nFor example, an exception having as message ```An exception has been thrown by some user code```\nwill print\n```\nAn exception has been thrown by some user code\nWhen applying (new JClaveau\\Async\\DeferredCallChain( \u003cinstance id\u003e ))-\u003epreviousSuccessfullCall()-\u003ebuggyCall('Robert') defined at \u003cfile\u003e:\u003cline\u003e\n```\n\n### Static calls\nStatic calls can be useful, especially for singletons. For some technical reasons explained [here](https://github.com/jclaveau/php-deferred-callchain/issues/9),\nthe only way to support it is to call them as normal methods (e.g. with -\u003e )\nand look for it as a static method once we know it doesn't exist as a regular one.\n```php\nlater(MyModel::class)-\u003egetInstance()-\u003emyNormalGetter();\n// or\nlater($myModel)-\u003egetInstance()-\u003emyNormalGetter(); // like $myModel::getInstance()\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjclaveau%2Fphp-deferred-callchain","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjclaveau%2Fphp-deferred-callchain","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjclaveau%2Fphp-deferred-callchain/lists"}