├── LICENSE.md ├── README.md ├── composer.json ├── config └── .gitkeep └── src └── Macroable.php /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Spatie bvba 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [](https://supportukrainenow.org) 3 | 4 | # A trait to dynamically add methods to a class 5 | 6 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/spatie/macroable.svg?style=flat-square)](https://packagist.org/packages/spatie/macroable) 7 | ![run-tests](https://github.com/spatie/macroable/workflows/run-tests/badge.svg) 8 | [![Total Downloads](https://img.shields.io/packagist/dt/spatie/macroable.svg?style=flat-square)](https://packagist.org/packages/spatie/macroable) 9 | 10 | This package provides a trait that, when applied to a class, makes it possible to add methods to that class at runtime. 11 | 12 | Here's a quick example: 13 | 14 | ```php 15 | $myClass = new class() { 16 | use Spatie\Macroable\Macroable; 17 | }; 18 | 19 | $myClass::macro('concatenate', function(... $strings) { 20 | return implode('-', $strings); 21 | }); 22 | 23 | $myClass->concatenate('one', 'two', 'three'); // returns 'one-two-three' 24 | ``` 25 | 26 | The idea of a macroable trait and the implementation is taken from [the `macroable` trait](https://github.com/laravel/framework/blob/master/src/Illuminate/Macroable/Traits/Macroable.php) of the [Laravel framework](https://laravel.com). 27 | 28 | ## Support us 29 | 30 | [](https://spatie.be/github-ad-click/macroable) 31 | 32 | We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us). 33 | 34 | We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards). 35 | 36 | ## Installation 37 | 38 | You can install the package via composer: 39 | 40 | ```bash 41 | composer require spatie/macroable 42 | ``` 43 | 44 | ## Usage 45 | 46 | You can add a new method to a class using `macro`: 47 | 48 | ```php 49 | $macroableClass = new class() { 50 | use Spatie\Macroable\Macroable; 51 | }; 52 | 53 | $macroableClass::macro('concatenate', function(... $strings) { 54 | return implode('-', $strings); 55 | }); 56 | 57 | $macroableClass->concatenate('one', 'two', 'three'); // returns 'one-two-three' 58 | ``` 59 | 60 | Callables passed to the `macro` function will be bound to the `class` 61 | 62 | ```php 63 | $macroableClass = new class() { 64 | 65 | protected $name = 'myName'; 66 | 67 | use Spatie\Macroable\Macroable; 68 | }; 69 | 70 | $macroableClass::macro('getName', function() { 71 | return $this->name; 72 | }; 73 | 74 | $macroableClass->getName(); // returns 'myName' 75 | ``` 76 | 77 | You can also add multiple methods in one go by using a mixin class. A mixin class contains methods that return callables. Each method from the mixin will be registered on the macroable class. 78 | 79 | ```php 80 | $mixin = new class() { 81 | public function mixinMethod() 82 | { 83 | return function() { 84 | return 'mixinMethod'; 85 | }; 86 | } 87 | 88 | public function anotherMixinMethod() 89 | { 90 | return function() { 91 | return 'anotherMixinMethod'; 92 | }; 93 | } 94 | }; 95 | 96 | $macroableClass->mixin($mixin); 97 | 98 | $macroableClass->mixinMethod() // returns 'mixinMethod'; 99 | 100 | $macroableClass->anotherMixinMethod() // returns 'anotherMixinMethod'; 101 | ``` 102 | 103 | ## Changelog 104 | 105 | Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. 106 | 107 | ## Testing 108 | 109 | ``` bash 110 | composer test 111 | ``` 112 | 113 | ## Contributing 114 | 115 | Please see [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTING.md) for details. 116 | 117 | ## Security 118 | 119 | If you've found a bug regarding security please mail [security@spatie.be](mailto:security@spatie.be) instead of using the issue tracker. 120 | 121 | ## Postcardware 122 | 123 | You're free to use this package (it's [MIT-licensed](LICENSE.md)), but if it makes it to your production environment we highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. 124 | 125 | Our address is: Spatie, Kruikstraat 22, 2018 Antwerp, Belgium. 126 | 127 | We publish all received postcards [on our company website](https://spatie.be/en/opensource/postcards). 128 | 129 | ## Credits 130 | 131 | - [Freek Van der Herten](https://github.com/freekmurze) 132 | - [All Contributors](../../contributors) 133 | 134 | Idea and code is taken from [the `macroable` trait](https://github.com/laravel/framework/blob/master/src/Illuminate/Support/Traits/Macroable.php) of the [Laravel framework](https://laravel.com). 135 | 136 | ## License 137 | 138 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 139 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spatie/macroable", 3 | "description": "A trait to dynamically add methods to a class", 4 | "keywords": [ 5 | "spatie", 6 | "macroable" 7 | ], 8 | "homepage": "https://github.com/spatie/macroable", 9 | "license": "MIT", 10 | "authors": [ 11 | { 12 | "name": "Freek Van der Herten", 13 | "email": "freek@spatie.be", 14 | "homepage": "https://spatie.be", 15 | "role": "Developer" 16 | } 17 | ], 18 | "require": { 19 | "php": "^8.0" 20 | }, 21 | "require-dev": { 22 | "pestphp/pest": "^1.22", 23 | "phpunit/phpunit": "^9.5" 24 | }, 25 | "autoload": { 26 | "psr-4": { 27 | "Spatie\\Macroable\\": "src" 28 | } 29 | }, 30 | "autoload-dev": { 31 | "psr-4": { 32 | "Spatie\\Macroable\\Test\\": "tests" 33 | } 34 | }, 35 | "scripts": { 36 | "test": "vendor/bin/pest" 37 | }, 38 | "config": { 39 | "sort-packages": true, 40 | "allow-plugins": { 41 | "pestphp/pest-plugin": true 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /config/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spatie/macroable/6647519307b30a4761e593c346036e9c08977203/config/.gitkeep -------------------------------------------------------------------------------- /src/Macroable.php: -------------------------------------------------------------------------------- 1 | getMethods( 22 | ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED 23 | ); 24 | 25 | foreach ($methods as $method) { 26 | $method->setAccessible(true); 27 | 28 | static::macro($method->name, $method->invoke($mixin)); 29 | } 30 | } 31 | 32 | public static function hasMacro(string $name): bool 33 | { 34 | return isset(static::$macros[$name]); 35 | } 36 | 37 | public static function __callStatic($method, $parameters) 38 | { 39 | if (! static::hasMacro($method)) { 40 | throw new BadMethodCallException("Method {$method} does not exist."); 41 | } 42 | 43 | $macro = static::$macros[$method]; 44 | 45 | if ($macro instanceof Closure) { 46 | return call_user_func_array(Closure::bind($macro, null, static::class), $parameters); 47 | } 48 | 49 | return call_user_func_array($macro, $parameters); 50 | } 51 | 52 | public function __call($method, $parameters) 53 | { 54 | if (! static::hasMacro($method)) { 55 | throw new BadMethodCallException("Method {$method} does not exist."); 56 | } 57 | 58 | $macro = static::$macros[$method]; 59 | 60 | if ($macro instanceof Closure) { 61 | return call_user_func_array($macro->bindTo($this, static::class), $parameters); 62 | } 63 | 64 | return call_user_func_array($macro, $parameters); 65 | } 66 | } 67 | --------------------------------------------------------------------------------