{"id":28099139,"url":"https://github.com/technically-php/callable-reflection","last_synced_at":"2026-02-22T22:15:18.012Z","repository":{"id":57066128,"uuid":"353387314","full_name":"technically-php/callable-reflection","owner":"technically-php","description":":call_me_hand: a handy library to reflect and invoke any callable. :elephant: PHP8-ready :tada:","archived":false,"fork":false,"pushed_at":"2024-12-22T22:44:59.000Z","size":90,"stargazers_count":9,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-12-22T23:27:25.680Z","etag":null,"topics":[],"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/technically-php.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-03-31T14:35:54.000Z","updated_at":"2024-12-22T22:45:03.000Z","dependencies_parsed_at":"2024-12-22T23:33:56.939Z","dependency_job_id":null,"html_url":"https://github.com/technically-php/callable-reflection","commit_stats":{"total_commits":59,"total_committers":2,"mean_commits":29.5,"dds":"0.016949152542372836","last_synced_commit":"0634449080e030438f68127f84281fdbb85f9778"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/technically-php%2Fcallable-reflection","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/technically-php%2Fcallable-reflection/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/technically-php%2Fcallable-reflection/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/technically-php%2Fcallable-reflection/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/technically-php","download_url":"https://codeload.github.com/technically-php/callable-reflection/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254000308,"owners_count":21997427,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":[],"created_at":"2025-05-13T17:59:36.505Z","updated_at":"2026-02-22T22:15:18.003Z","avatar_url":"https://github.com/technically-php.png","language":"PHP","readme":"# Technically Callable Reflection\n\n`Technically\\CallableReflection` is a handy library to simplify reflecting any `callable`.\nIt provides a unified interface for reading callable arguments seamlessly supporting PHP8 union types.\nIt also lets you to easily invoke a callable with `call()` and `apply()` supporting named parameters.\n\n![Status][badge]\n\n## Features\n\n- Unified and simplified interface to reflect callable parameters \n- No dependencies\n- Supports PHP 8.0, 8.1, 8.2, 8.3, 8.4 (supports union types, `mixed`, `false`, etc; see examples below)\n- PHP 7.1+ compatible \u0026mdash; runs on as low as PHP 7.1\n- Semver\n- Tests\n\n## Installation\n\nUse [Composer][2] package manager to add *Technically\\CallableReflection* to your project:\n\n```\ncomposer require technically/callable-reflection\n```\n\n## Examples\n\n### Reflecting callable properties\n\n```php\n\u003c?php\n$function = function (string $abstract, Closure|string|null $concrete): mixed {\n    // function body\n};\n\n$reflection = CallableReflection::fromCallable($function);\n\nvar_dump($reflection-\u003eisFunction()); // false\nvar_dump($reflection-\u003eisMethod()); // false\nvar_dump($reflection-\u003eisClosure()); // true\n\n[$p1, $p2] = $reflection-\u003egetParameters();\n\nvar_dump($p2-\u003egetName()); // \"concrete\"\nvar_dump($p2-\u003eisNullable()); // true\nvar_dump($p2-\u003eisOptional()); // false\nvar_dump($p2-\u003ehasTypes()); // true\n\n[$t1, $t2, $t3] = $p2-\u003egetTypes();\n\nvar_dump($t1-\u003eisScalar()); // false \nvar_dump($t1-\u003eisClassName()); // true \nvar_dump($t1-\u003egetType()); // \"Closure\" \n\nvar_dump($t2-\u003eisScalar()); // true \nvar_dump($t2-\u003eisClassName()); // false \nvar_dump($t2-\u003egetType()); // \"string\"\n\nvar_dump($t3-\u003eisNull()); // true\nvar_dump($t3-\u003eisScalar()); // false \nvar_dump($t3-\u003eisClassName()); // false \nvar_dump($t3-\u003egetType()); // \"null\" \n```\n\n### Reflecting arbitrary class constructor\n\n```php\n\u003c?php\nfinal class MyService {\n    public function __construct(LoggerInterface $logger, bool $debug = false)\n    {\n        // ...\n    }\n}\n\n$reflection = CallableReflection::fromConstructor(MyService::class);\n\nvar_dump($reflection-\u003eisConstructor()); // true\nvar_dump($reflection-\u003eisFunction()); // false\nvar_dump($reflection-\u003eisMethod()); // false\nvar_dump($reflection-\u003eisClosure()); // false\n\n[$p1, $p2] = $reflection-\u003egetParameters();\n\nvar_dump($p1-\u003egetName()); // \"logger\"\nvar_dump($p1-\u003eisNullable()); // false\nvar_dump($p1-\u003eisOptional()); // false\nvar_dump($p1-\u003ehasTypes()); // true\n\nvar_dump($p2-\u003egetName()); // \"debug\"\nvar_dump($p2-\u003eisNullable()); // false\nvar_dump($p2-\u003eisOptional()); // true\nvar_dump($p2-\u003ehasTypes()); // true\n```\n\n### Checking if value satisfies parameter type declaration\n\n```php\n$function = function (int|string $value = null): mixed {\n    // function body\n};\n\n$reflection = CallableReflection::fromCallable($function);\n\n[$param] = $reflection-\u003egetParameters();\n\nvar_dump($param-\u003esatisfies(null)); // true\nvar_dump($param-\u003esatisfies(1)); // true\nvar_dump($param-\u003esatisfies('Hello')); // true\nvar_dump($param-\u003esatisfies(2.5)); // false\nvar_dump($param-\u003esatisfies([])); // false\nvar_dump($param-\u003esatisfies(true)); // false\n```\n\n### Invoking callable via reflection\n\n```php\n$function = function (string $abstract, Closure|string|null $concrete): mixed {\n// function body\n};\n\n$reflection = CallableReflection::fromCallable($function);\n\n// 1) call with positional parameters\n$result = $reflection-\u003ecall(LoggerInterface::class, MyLogger::class);\n\n// 1) call with named parameters\n$result = $reflection-\u003ecall(concrete: MyLogger::class, abstract: LoggerInterface::class);\n\n// 2) call with positional parameters array \n$result = $reflection-\u003eapply([LoggerInterface::class, MyLogger::class]);\n\n// 3) call with named parameters array \n$result = $reflection-\u003eapply(['concrete' =\u003e MyLogger::class, 'abstract' =\u003e LoggerInterface::class]);\n\n// 4) call with mixed named and positional parameters array \n$result = $reflection-\u003eapply([LoggerInterface::class, 'concrete' =\u003e MyLogger::class]);\n\n// 5) CallableReflection is a callable by itself\n$result = $reflection(LoggerInterface::class, MyLogger::class);\n```\n\n### Invoking constructor via reflection\n\n```php\nfinal class MyService {\n    public function __construct(LoggerInterface $logger, bool $debug = false)\n    {\n        // ...\n    }\n}\n\n$reflection = CallableReflection::fromConstructor(MyService::class);\n\n$service = $reflection-\u003ecall(new NullLogger());\n// or alternatively:\n// $service = $reflection-\u003eapply(['logger' =\u003e new NullLogger()]);\nassert($service instanceof MyService);\n```\n\n## How is this better than reflecting `Closure::fromCallable()`\n\nThis library's functionality is somewhat similar to what a reflection of the standard `Closure::fromCallable()` can provide. \nAnd if that's enough for your use case, you probably want to be using the standard reflection.\n\nThis library, however, does provide some added value on top:\n\n- It unifies interactions with all kinds of callables, including class constructors.\n  That was actually the primary use case I had in mind for it — building a DI service container.\n\n  For example, you cannot instantiate a new instance with Closure::fromCallable():\n\n  ```php\n  $reflection = new ReflectionFunction(Closure::fromCallable([MyRemoteService::class, '__construct']));\n  $service = $reflection-\u003ecall($token); // (!) doesn't work\n  ```\n\n  but you can call a constructor with this library:\n\n  ```php\n  $reflection = CallableReflection::fromConstructor(MyRemoteService::class);\n  $service = $reflection-\u003ecall($token);\n  ```\n  \n- It provides utilities to check if a specific value satisfies the given function argument type declaration (see `-\u003esatisfies()`). \n\n- It can know and tell what type of callable was reflected, while with Closure::fromCallable() this information is lost.\n  This might be important for certain use cases.\n\n- It has nice additional convenience getters and checkers. Like `satisfies()`, `isOptional()`, `isNull()`, `isScalar()`, etc.\n\n- The API is more intuitive and straighforward and hides away PHP version differences introduced to the Reflections API\n  over years. The native Reflections API is polluted with BC-preserving constructs.\n\n- The library is operating on top of the native Reflections API, and therefore is as performant as it is, plus a little\n  overhead for the convenience wrappers.\n\n\n## Changelog\n\nAll notable changes to this project will be documented in the [CHANGELOG](./CHANGELOG.md) file.\n\n\n## Credits\n\nImplemented by :space_invader: [Ivan Voskoboinyk][3].\n\n[1]: https://www.php-fig.org/psr/psr-11/\n[2]: https://getcomposer.org/\n[3]: https://voskoboinyk.com/\n[badge]: https://github.com/technically-php/callable-reflection/actions/workflows/test.yml/badge.svg\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftechnically-php%2Fcallable-reflection","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftechnically-php%2Fcallable-reflection","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftechnically-php%2Fcallable-reflection/lists"}