{"id":15029820,"url":"https://github.com/aimeos/macro","last_synced_at":"2025-05-13T22:03:34.308Z","repository":{"id":38372278,"uuid":"434119871","full_name":"aimeos/macro","owner":"aimeos","description":"Customize code using closures","archived":false,"fork":false,"pushed_at":"2024-11-07T11:21:21.000Z","size":82,"stargazers_count":1713,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-30T15:17:57.508Z","etag":null,"topics":["class","closure","custom","customize","dynamic","extend","inherit","macro","macros","php"],"latest_commit_sha":null,"homepage":"https://php-macro.org","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/aimeos.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-12-02T07:17:00.000Z","updated_at":"2025-04-30T14:38:19.000Z","dependencies_parsed_at":"2024-06-19T01:33:24.908Z","dependency_job_id":"99db62ec-7aec-42ab-9477-d7206d53c69d","html_url":"https://github.com/aimeos/macro","commit_stats":{"total_commits":27,"total_committers":1,"mean_commits":27.0,"dds":0.0,"last_synced_commit":"8c348d480f1666fd9b97cd37d93ecf7279da0d61"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aimeos%2Fmacro","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aimeos%2Fmacro/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aimeos%2Fmacro/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aimeos%2Fmacro/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aimeos","download_url":"https://codeload.github.com/aimeos/macro/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254036811,"owners_count":22003653,"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":["class","closure","custom","customize","dynamic","extend","inherit","macro","macros","php"],"created_at":"2024-09-24T20:11:42.115Z","updated_at":"2025-05-13T22:03:34.278Z","avatar_url":"https://github.com/aimeos.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"## PHP Macro\n\nThe PHP Macro package offers closure (anonymous function) based setter dependency\ninjection by providing a trait which can be included into any class.\n\n```bash\ncomposer req aimeos/macro\n```\n\nThis package is for application, framework and library developers who want to\nallow customizing the behavior of their code by their users.\n\n## Why macros\n\nIn applications, frameworks or libraries which are build for customization it’s\nnecessary to allow overwriting existing functionality to be able customize its\nbehavior. This is where macros are very handy because they can add custom code\nusing closures.\n\nWith the PHP Macro package, you can also allow users to overwrite methods in\nbase classes without forcing your users to extend these classes. The PHP Macro\npackage uses **NO reflection** or other hacks, just **pure PHP methods**.\n\nThere are some pros and cons when compared to class based depencency injection:\n\n**Pro:**\n\n* Less code to write and much easier to implement for simple stuff\n* Custom closures can be inherited and overwritten like class methods\n\n**Con:**\n\n* Limited static code analysis possibilities\n* Anonymous function can not be forced to implement an interface\n\nThus, it's not a replacement for class based depencency injection but a lightweight\naddition for small extension points where full-blown dependency injection using\nclasses implementing interfaces are too much work.\n\n## Allow customization\n\nThe result of existing methods can be modified if the original method checks\nfor an existing macro and use that instead its own implementation:\n\n```php\n// original code\n\nclass Order\n{\n    use Aimeos\\Macro\\Macroable;\n\n    private $id = '123';\n\n    public function getOrderNumber()\n    {\n        $fcn = static::macro( 'orderNumber' );\n        return $fcn ? $fcn( $this-\u003eid ) : $this-\u003eid;\n    }\n};\n```\n\nNow, you can add your custom `orderNumber` macro that will be used instead:\n\n```php\n// user code\n\nOrder::macro( 'orderNumber', function( string $id ) {\n   return date( 'Y' ) . '-' . $id;\n} );\n\n(new Order)-\u003egetOrderNumber(); // now returns '2020-123'\n```\n\nThus, you can generate own output or pass a different result to subseqent methods\nwithin the application.\n\n## Access class properties\n\nWhen macros are called in an object context, they can also access class properties:\n\n```php\n// original code\n\nclass A\n{\n    use Aimeos\\Macro\\Macroable;\n    private $name = 'A';\n};\n```\n\nHere, the private property `$name` is available in the macro:\n\n```php\n// user code\n\nA::macro( 'concat', function( array $values ) {\n   return $this-\u003ename . ':' . implode( '-', $values );\n} );\n\n(new A)-\u003econcat( ['1', '2', '3'] ); // returns 'A:1-2-3'\n```\n\nThe macro can use the property as input for creating the returned value.\n\n## Use inherited macros\n\nThe PHP macro package also allows to inherit macros from parent classes. Then,\nthey can access class properties of the child class just like regular class\nmethods:\n\n```php\n// original code\n\nclass A\n{\n    use Aimeos\\Macro\\Macroable;\n    private $name = 'A';\n};\n\nclass B extends A\n{\n    private $name = 'B';\n};\n```\n\nMacros added to the parent class will be available in child classes too:\n\n```php\n// user code\n\nA::macro( 'concat', function( array $values ) {\n   return $this-\u003ename . ':' . implode( '-', $values );\n} );\n\n(new B)-\u003econcat( ['1', '2', '3'] ); // returns 'B:1-2-3'\n```\n\nClass `B` extends from class `A` but provides a different `$name` property. The\nmacro inherited from class `A` will now use the property of class `B`.\n\n\n## Overwrite inherited macros\n\nIt's also possible to overwrite macros inherited from parent classes as it's\npossible with regular class methods:\n\n```php\n// original code\n\nclass A\n{\n    use Aimeos\\Macro\\Macroable;\n\n    public function do() {\n        return static::macro( 'concat' )( [1, 2, 3] );\n    }\n};\n\nclass B extends A {};\n\nclass C extends A {};\n```\n\nNow you can add macros to the parent class and one of the child classes:\n\n```php\n// user code\n\nA::macro( 'concat', function( array $values ) {\n   return implode( ',', $values );\n} );\n\nC::macro( 'concat', function( array $values ) {\n   return implode( '-', $values );\n} );\n\n(new B)-\u003edo(); // returns '1,2,3'\n\n(new C)-\u003edo(); // returns '1-2-3'\n```\n\nThis enables you to add special handling for single classes even if all other\nclasses still use the macro added to class `A`.\n\n## Overwrite protected methods\n\nBase classes often offer a set of methods that are used by the child classes.\nIn PHP, replacing the methods of a base class is impossible and thus, you have\nto overwrite each child class with your own implementation.\n\nTo avoid that, the original method can use the `call()` method instead of calling\nthe method of the parent class directly:\n\n```php\n// original code\n\nclass A\n{\n    use Aimeos\\Macro\\Macroable;\n\n    protected function getName( $prefix )\n    {\n        return $prefix . 'A';\n    }\n};\n\nclass B extends A\n{\n    public function do()\n    {\n        return $this-\u003ecall( 'getName', 'B-' );\n    }\n};\n```\n\nThis will check if there's a macro `getName` available and will call that instead\nof the `getName()` method:\n\n```php\n// user code\n\n(new B)-\u003edo(); // returns 'B-A'\n\nA::macro( 'getName', function( $prefix ) {\n   return $this-\u003egetName( $prefix ) . '-123';\n} );\n\n(new B)-\u003edo(); // returns 'B-A-123'\n```\n\nThe original `getName()` method can still be used in the macro.\n\n## Reset macros\n\nSometimes, it may be necessary to remove macros from objects, especially when\nrunning automated tests. You can unset a macro by using:\n\n```php\nclass A\n{\n    use Aimeos\\Macro\\Macroable;\n};\n\n// add macro\nA::macro( 'test', function() {\n   return 'test';\n} );\n\n// remove macro\nA::unmacro( 'test' );\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faimeos%2Fmacro","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faimeos%2Fmacro","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faimeos%2Fmacro/lists"}