/src/Reflection/CallableReflection.php

https://github.com/Kakwen/Trash · PHP · 289 lines · 142 code · 31 blank · 116 comment · 22 complexity · c7863a4864aac1d7b51674b3027bfaa4 MD5 · raw file

  1. <?php
  2. /**
  3. * Class CallableReflection
  4. * Callable Reflection: You can reflect a callback and know its type.
  5. * PHP >= 5.4
  6. */
  7. class CallableReflection
  8. {
  9. /**
  10. * type const
  11. */
  12. const ERROR_TYPE = 0;
  13. const FUNCTION_TYPE = 1;
  14. const CLOSURE_TYPE = 2;
  15. const INSTANCE_METHOD_TYPE = 3;
  16. const STATIC_METHOD_TYPE = 4;
  17. const INVOKED_OBJECT_TYPE = 5;
  18. /**
  19. * reflected callback
  20. *
  21. * @var callable
  22. */
  23. private $callable;
  24. /**
  25. * type of the reflected callback (const XXX_TYPE)
  26. *
  27. * @var int
  28. */
  29. private $type;
  30. /**
  31. * initializes the reflected callback
  32. *
  33. * @param callable $callable
  34. */
  35. function __construct(callable $callable)
  36. {
  37. $this->setCallable($callable);
  38. }
  39. /**
  40. * getter of $callable
  41. *
  42. * @return callable
  43. */
  44. public function getCallable()
  45. {
  46. return $this->callable;
  47. }
  48. /**
  49. * invokes the reflected callback
  50. *
  51. * @return mixed
  52. */
  53. public function call()
  54. {
  55. return call_user_func_array($this->getCallable(), func_get_args());
  56. }
  57. /**
  58. * invokes the reflected callback with static binding
  59. *
  60. * @return mixed
  61. */
  62. public function callStatic()
  63. {
  64. return forward_static_call_array($this->getCallable(), func_get_args());
  65. }
  66. /**
  67. * getter of $type
  68. *
  69. * @return int
  70. */
  71. public function getType()
  72. {
  73. if (is_null($this->type)) {
  74. $this->initType();
  75. }
  76. return $this->type;
  77. }
  78. /**
  79. * indicates whether the callback is a function
  80. *
  81. * @return bool
  82. */
  83. public function isFunction()
  84. {
  85. return $this->getType() === self::FUNCTION_TYPE;
  86. }
  87. /**
  88. * indicates whether the callback is a Closure
  89. *
  90. * @return bool
  91. */
  92. public function isClosure()
  93. {
  94. return $this->getType() === self::CLOSURE_TYPE;
  95. }
  96. /**
  97. * indicates whether the callback is a method
  98. *
  99. * @return bool
  100. */
  101. public function isMethod()
  102. {
  103. return $this->isInstanceMethod() || $this->isStaticMethod();
  104. }
  105. /**
  106. * indicates whether the callback is a object method
  107. *
  108. * @return bool
  109. */
  110. public function isInstanceMethod()
  111. {
  112. return $this->getType() === self::INSTANCE_METHOD_TYPE;
  113. }
  114. /**
  115. * indicates whether the callback is a class method
  116. *
  117. * @return bool
  118. */
  119. public function isStaticMethod()
  120. {
  121. return $this->getType() === self::STATIC_METHOD_TYPE;
  122. }
  123. /**
  124. * indicates whether the callback is an invoked object
  125. *
  126. * @return bool
  127. */
  128. public function isInvokedObject()
  129. {
  130. return $this->getType() === self::INVOKED_OBJECT_TYPE;
  131. }
  132. /**
  133. * returns the name of the function, if the reflected callback is a function
  134. *
  135. * @return string
  136. */
  137. public function getFunctionName()
  138. {
  139. if ($this->isFunction()) {
  140. return $this->getCallable();
  141. }
  142. return '';
  143. }
  144. /**
  145. * returns the Closure object, if the reflected callback is a Closure
  146. *
  147. * @return Closure|null
  148. */
  149. public function getClosure()
  150. {
  151. if ($this->isClosure()) {
  152. return $this->getCallable();
  153. }
  154. return null;
  155. }
  156. /**
  157. * returns the name of the method, if the reflected callback is a method (except __invoke)
  158. *
  159. * @return string
  160. */
  161. public function getMethodName()
  162. {
  163. if ($this->isMethod()) {
  164. $callable = $this->explodeCallable();
  165. return $callable[1];
  166. }
  167. return '';
  168. }
  169. /**
  170. * returns the name of the class, if the reflected callback is an object (except Closure)
  171. *
  172. * @return string
  173. */
  174. public function getClassName()
  175. {
  176. if ($this->isMethod() || $this->isInvokedObject()) {
  177. $callable = $this->explodeCallable();
  178. if ($this->isInstanceMethod() || $this->isInvokedObject()) {
  179. return get_class($callable[0]);
  180. }
  181. return $callable[0];
  182. }
  183. return '';
  184. }
  185. /**
  186. * returns the object, if the reflected callback is an object (except Closure)
  187. *
  188. * @return object|null
  189. */
  190. public function getObject()
  191. {
  192. if ($this->isInstanceMethod() || $this->isInvokedObject()) {
  193. $callable = $this->explodeCallable();
  194. return $callable[0];
  195. }
  196. return null;
  197. }
  198. /**
  199. * setter of $callable
  200. *
  201. * @param callable $callable
  202. */
  203. private function setCallable(callable $callable)
  204. {
  205. $this->callable = $callable;
  206. }
  207. /**
  208. * explodes the reflected callback in an array and splits method/function from class/object
  209. *
  210. * @return array
  211. */
  212. private function explodeCallable()
  213. {
  214. if (is_string($this->getCallable())) {
  215. return explode('::', $this->getCallable());
  216. } elseif (is_array($this->getCallable())) {
  217. return $this->getCallable();
  218. } else {
  219. return array($this->getCallable());
  220. }
  221. }
  222. /**
  223. * setter of $type
  224. *
  225. * @param int $type
  226. */
  227. private function setType($type)
  228. {
  229. $this->type = (int)$type;
  230. }
  231. /**
  232. * initializes $type
  233. */
  234. private function initType()
  235. {
  236. if ($this->getCallable() instanceof \Closure) {
  237. $this->setType(self::CLOSURE_TYPE);
  238. } else {
  239. $callable = $this->explodeCallable();
  240. if (count($callable) === 1) {
  241. if (is_object($callable[0])) {
  242. $this->setType(self::INVOKED_OBJECT_TYPE);
  243. } else {
  244. $this->setType(self::FUNCTION_TYPE);
  245. }
  246. } elseif (count($callable) === 2) {
  247. if (is_object($callable[0])) {
  248. $this->setType(self::INSTANCE_METHOD_TYPE);
  249. } else {
  250. $this->setType(self::STATIC_METHOD_TYPE);
  251. }
  252. } else {
  253. $this->setType(self::ERROR_TYPE);
  254. trigger_error("Type not found for callable", E_USER_ERROR);
  255. }
  256. }
  257. }
  258. }