Elgg  Version 3.0
HandlersService.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg;
4 
8 
21 
31  public function call($callable, $object, $args) {
32  $original = $callable;
33 
34  $callable = $this->resolveCallable($callable);
35  if (!is_callable($callable)) {
36  $type = is_string($object) ? $object : $object::EVENT_TYPE;
37  $description = $type . " [{$args[0]}, {$args[1]}]";
38  $msg = "Handler for $description is not callable: " . $this->describeCallable($original);
39  _elgg_services()->logger->warning($msg);
40 
41  return [false, null, $object];
42  }
43 
44  $use_object = $this->acceptsObject($callable);
45  if ($use_object) {
46  if (is_string($object)) {
47  switch ($object) {
48  case 'hook' :
49  $object = new HrsHook(elgg(), $args[0], $args[1], $args[2], $args[3]);
50  break;
51 
52  case 'event' :
53  $object = new HrsEvent(elgg(), $args[0], $args[1], $args[2]);
54  break;
55 
56  case 'middleware' :
57  case 'controller' :
58  case 'action' :
59  $object = new Request(elgg(), $args[0]);
60  break;
61  }
62  }
63 
64  $result = call_user_func($callable, $object);
65  } else {
66  // legacy arguments
67  $result = call_user_func_array($callable, $args);
68  }
69 
70  return [true, $result, $object];
71  }
72 
82  public function isCallable($callback) {
83  $callback = $this->resolveCallable($callback);
84  return $callback && is_callable($callback);
85  }
86 
94  public function getReflector($callable) {
95  if (is_string($callable)) {
96  if (false !== strpos($callable, '::')) {
97  $callable = explode('::', $callable);
98  } else {
99  // function
100  return new \ReflectionFunction($callable);
101  }
102  }
103  if (is_array($callable)) {
104  return new \ReflectionMethod($callable[0], $callable[1]);
105  }
106  if ($callable instanceof \Closure) {
107  return new \ReflectionFunction($callable);
108  }
109  if (is_object($callable)) {
110  return new \ReflectionMethod($callable, '__invoke');
111  }
112 
113  throw new \InvalidArgumentException('invalid $callable');
114  }
115 
123  private function resolveCallable($callable) {
124  if (is_callable($callable)) {
125  return $callable;
126  }
127 
128  if (is_string($callable)
129  && preg_match(DiContainer::CLASS_NAME_PATTERN_53, $callable)
130  && class_exists($callable)) {
131  // @todo Eventually a more advanced DIC could auto-inject dependencies
132  $callable = new $callable;
133  }
134 
135  return is_callable($callable) ? $callable : null;
136  }
137 
145  private function acceptsObject($callable) {
146  // note: caching string callables didn't help any
147  $type = (string) $this->getParamTypeForCallable($callable);
148  if (0 === strpos($type, 'Elgg\\')) {
149  // probably right. We can just assume and let PHP handle it
150  return true;
151  }
152 
153  return false;
154  }
155 
164  public function getParamTypeForCallable($callable, $index = 0) {
165  $params = $this->getReflector($callable)->getParameters();
166  if (!isset($params[$index])) {
167  return null;
168  }
169 
170  return $this->getType($params[$index]);
171  }
172 
180  public function getType(\ReflectionParameter $param) {
181  // @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
182  // @license http://framework.zend.com/license/new-bsd New BSD License
183 
184  if (method_exists($param, 'getType')
185  && ($type = $param->getType())
186  && $type->isBuiltin()) {
187  return (string) $type;
188  }
189 
190  // can be dropped when dropping PHP7 support:
191  if ($param->isArray()) {
192  return 'array';
193  }
194 
195  // can be dropped when dropping PHP7 support:
196  if ($param->isCallable()) {
197  return 'callable';
198  }
199 
200  // ReflectionParameter::__toString() doesn't require loading class
201  if (preg_match('~\[\s<\w+?>\s([\S]+)~s', (string) $param, $m)) {
202  if ($m[1][0] !== '$') {
203  return $m[1];
204  }
205  }
206 
207  return '';
208  }
209 
219  public function describeCallable($callable, $file_root = '') {
220  if (is_string($callable)) {
221  return $callable;
222  }
223  if (is_array($callable) && array_keys($callable) === [0, 1] && is_string($callable[1])) {
224  if (is_string($callable[0])) {
225  return "{$callable[0]}::{$callable[1]}";
226  }
227  return "(" . get_class($callable[0]) . ")->{$callable[1]}";
228  }
229  if ($callable instanceof \Closure) {
230  $ref = new \ReflectionFunction($callable);
231  $file = $ref->getFileName();
232  $line = $ref->getStartLine();
233 
234  if ($file_root && 0 === strpos($file, $file_root)) {
235  $file = substr($file, strlen($file_root));
236  }
237 
238  return "(Closure {$file}:{$line})";
239  }
240  if (is_object($callable)) {
241  return "(" . get_class($callable) . ")->__invoke()";
242  }
243  return print_r($callable, true);
244  }
245 
253  public function fingerprintCallable($callable) {
254  if (is_string($callable)) {
255  return $callable;
256  }
257  if (is_array($callable)) {
258  if (is_string($callable[0])) {
259  return "{$callable[0]}::{$callable[1]}";
260  }
261  return get_class($callable[0]) . "::{$callable[1]}";
262  }
263  if ($callable instanceof \Closure) {
264  return '';
265  }
266  if (is_object($callable)) {
267  return get_class($callable) . "::__invoke";
268  }
269  // this should not happen
270  return '';
271  }
272 }
isCallable($callback)
Test is callback is callable Unlike is_callable(), this function also tests invokable classes...
if(!$item instanceof ElggRiverItem) $object
Definition: responses.php:23
if(!array_key_exists($filename, $text_files)) $file
$params
Saves global plugin settings.
Definition: save.php:13
Helpers for providing callable-based APIs.
getParamTypeForCallable($callable, $index=0)
Get the type for a parameter of a callable.
$args
Some servers don&#39;t allow PHP to check the rewrite, so try via AJAX.
$type
Definition: delete.php:21
getType(\ReflectionParameter $param)
Get the type of a parameter.
getReflector($callable)
Get the reflection interface for a callable.
The object passed to invokable class name handlers.
Definition: Event.php:12
Configuration exception.
The object passed to invokable class name handlers.
Definition: Hook.php:12
fingerprintCallable($callable)
Get a string that uniquely identifies a callback across requests (for caching)
describeCallable($callable, $file_root= '')
Get a string description of a callback.
call($callable, $object, $args)
Call the handler with the hook/event object.
Request container.
Definition: Request.php:13
_elgg_services()
Get the global service provider.
Definition: elgglib.php:1292
$index
Definition: gallery.php:47
if(elgg_view_exists("plugins/{$plugin_id}/settings")) $description
Definition: full.php:179
var elgg
Definition: elgglib.js:4