Macroable.php 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. <?php
  2. namespace Spatie\Macroable;
  3. use Closure;
  4. use ReflectionClass;
  5. use ReflectionMethod;
  6. use BadMethodCallException;
  7. trait Macroable
  8. {
  9. protected static $macros = [];
  10. /**
  11. * Register a custom macro.
  12. *
  13. * @param string $name
  14. * @param object|callable $macro
  15. */
  16. public static function macro(string $name, $macro)
  17. {
  18. static::$macros[$name] = $macro;
  19. }
  20. /**
  21. * Mix another object into the class.
  22. *
  23. * @param object $mixin
  24. */
  25. public static function mixin($mixin)
  26. {
  27. $methods = (new ReflectionClass($mixin))->getMethods(
  28. ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
  29. );
  30. foreach ($methods as $method) {
  31. $method->setAccessible(true);
  32. static::macro($method->name, $method->invoke($mixin));
  33. }
  34. }
  35. public static function hasMacro(string $name): bool
  36. {
  37. return isset(static::$macros[$name]);
  38. }
  39. public static function __callStatic($method, $parameters)
  40. {
  41. if (! static::hasMacro($method)) {
  42. throw new BadMethodCallException("Method {$method} does not exist.");
  43. }
  44. if (static::$macros[$method] instanceof Closure) {
  45. return call_user_func_array(Closure::bind(static::$macros[$method], null, static::class), $parameters);
  46. }
  47. return call_user_func_array(static::$macros[$method], $parameters);
  48. }
  49. public function __call($method, $parameters)
  50. {
  51. if (! static::hasMacro($method)) {
  52. throw new BadMethodCallException("Method {$method} does not exist.");
  53. }
  54. $macro = static::$macros[$method];
  55. if ($macro instanceof Closure) {
  56. return call_user_func_array($macro->bindTo($this, static::class), $parameters);
  57. }
  58. return call_user_func_array($macro, $parameters);
  59. }
  60. }