Macroable.php 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. <?php
  2. namespace Illuminate\Support\Traits;
  3. use BadMethodCallException;
  4. use Closure;
  5. use ReflectionClass;
  6. use ReflectionMethod;
  7. use RuntimeException;
  8. use Throwable;
  9. trait Macroable
  10. {
  11. /**
  12. * The registered string macros.
  13. *
  14. * @var array
  15. */
  16. protected static $macros = [];
  17. /**
  18. * Register a custom macro.
  19. *
  20. * @param string $name
  21. * @param object|callable $macro
  22. *
  23. * @param-closure-this static $macro
  24. *
  25. * @return void
  26. */
  27. public static function macro($name, $macro)
  28. {
  29. static::$macros[$name] = $macro;
  30. }
  31. /**
  32. * Mix another object into the class.
  33. *
  34. * @param object $mixin
  35. * @param bool $replace
  36. * @return void
  37. *
  38. * @throws \ReflectionException
  39. */
  40. public static function mixin($mixin, $replace = true)
  41. {
  42. $methods = (new ReflectionClass($mixin))->getMethods(
  43. ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
  44. );
  45. foreach ($methods as $method) {
  46. if ($replace || ! static::hasMacro($method->name)) {
  47. static::macro($method->name, $method->invoke($mixin));
  48. }
  49. }
  50. }
  51. /**
  52. * Checks if macro is registered.
  53. *
  54. * @param string $name
  55. * @return bool
  56. */
  57. public static function hasMacro($name)
  58. {
  59. return isset(static::$macros[$name]);
  60. }
  61. /**
  62. * Flush the existing macros.
  63. *
  64. * @return void
  65. */
  66. public static function flushMacros()
  67. {
  68. static::$macros = [];
  69. }
  70. /**
  71. * Dynamically handle calls to the class.
  72. *
  73. * @param string $method
  74. * @param array $parameters
  75. * @return mixed
  76. *
  77. * @throws \BadMethodCallException
  78. */
  79. public static function __callStatic($method, $parameters)
  80. {
  81. if (! static::hasMacro($method)) {
  82. throw new BadMethodCallException(sprintf(
  83. 'Method %s::%s does not exist.', static::class, $method
  84. ));
  85. }
  86. $macro = static::$macros[$method];
  87. if ($macro instanceof Closure) {
  88. $macro = $macro->bindTo(null, static::class);
  89. }
  90. return $macro(...$parameters);
  91. }
  92. /**
  93. * Dynamically handle calls to the class.
  94. *
  95. * @param string $method
  96. * @param array $parameters
  97. * @return mixed
  98. *
  99. * @throws \BadMethodCallException
  100. */
  101. public function __call($method, $parameters)
  102. {
  103. if (! static::hasMacro($method)) {
  104. throw new BadMethodCallException(sprintf(
  105. 'Method %s::%s does not exist.', static::class, $method
  106. ));
  107. }
  108. $macro = static::$macros[$method];
  109. if ($macro instanceof Closure) {
  110. try {
  111. $macro = $macro->bindTo($this, static::class) ?? throw new RuntimeException;
  112. } catch (Throwable) {
  113. $macro = $macro->bindTo(null, static::class);
  114. }
  115. }
  116. return $macro(...$parameters);
  117. }
  118. }