diff options
Diffstat (limited to 'libs/jsonrpc/src/JsonRPC/ProcedureHandler.php')
-rw-r--r-- | libs/jsonrpc/src/JsonRPC/ProcedureHandler.php | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/libs/jsonrpc/src/JsonRPC/ProcedureHandler.php b/libs/jsonrpc/src/JsonRPC/ProcedureHandler.php new file mode 100644 index 00000000..fe33f6b1 --- /dev/null +++ b/libs/jsonrpc/src/JsonRPC/ProcedureHandler.php @@ -0,0 +1,296 @@ +<?php + +namespace JsonRPC; + +use BadFunctionCallException; +use Closure; +use InvalidArgumentException; +use ReflectionFunction; +use ReflectionMethod; + +/** + * Class ProcedureHandler + * + * @package JsonRPC + * @author Frederic Guillot + */ +class ProcedureHandler +{ + /** + * List of procedures + * + * @access protected + * @var array + */ + protected $callbacks = array(); + + /** + * List of classes + * + * @access protected + * @var array + */ + protected $classes = array(); + + /** + * List of instances + * + * @access protected + * @var array + */ + protected $instances = array(); + + /** + * Before method name to call + * + * @access protected + * @var string + */ + protected $beforeMethodName = ''; + + /** + * Register a new procedure + * + * @access public + * @param string $procedure Procedure name + * @param closure $callback Callback + * @return $this + */ + public function withCallback($procedure, Closure $callback) + { + $this->callbacks[$procedure] = $callback; + return $this; + } + + /** + * Bind a procedure to a class + * + * @access public + * @param string $procedure Procedure name + * @param mixed $class Class name or instance + * @param string $method Procedure name + * @return $this + */ + public function withClassAndMethod($procedure, $class, $method = '') + { + if ($method === '') { + $method = $procedure; + } + + $this->classes[$procedure] = array($class, $method); + return $this; + } + + /** + * Bind a class instance + * + * @access public + * @param mixed $instance + * @return $this + */ + public function withObject($instance) + { + $this->instances[] = $instance; + return $this; + } + + /** + * Set a before method to call + * + * @access public + * @param string $methodName + * @return $this + */ + public function withBeforeMethod($methodName) + { + $this->beforeMethodName = $methodName; + return $this; + } + + /** + * Register multiple procedures from array + * + * @access public + * @param array $callbacks Array with procedure names (array keys) and callbacks (array values) + * @return $this + */ + public function withCallbackArray($callbacks) + { + foreach ($callbacks as $procedure => $callback) { + $this->withCallback($procedure, $callback); + } + + return $this; + } + + /** + * Bind multiple procedures to classes from array + * + * @access public + * @param array $callbacks Array with procedure names (array keys) and class and method names (array values) + * @return $this + */ + public function withClassAndMethodArray($callbacks) + { + foreach ($callbacks as $procedure => $callback) { + $this->withClassAndMethod($procedure, $callback[0], $callback[1]); + } + + return $this; + } + + /** + * Execute the procedure + * + * @access public + * @param string $procedure Procedure name + * @param array $params Procedure params + * @return mixed + */ + public function executeProcedure($procedure, array $params = array()) + { + if (isset($this->callbacks[$procedure])) { + return $this->executeCallback($this->callbacks[$procedure], $params); + } elseif (isset($this->classes[$procedure]) && method_exists($this->classes[$procedure][0], $this->classes[$procedure][1])) { + return $this->executeMethod($this->classes[$procedure][0], $this->classes[$procedure][1], $params); + } + + foreach ($this->instances as $instance) { + if (method_exists($instance, $procedure)) { + return $this->executeMethod($instance, $procedure, $params); + } + } + + throw new BadFunctionCallException('Unable to find the procedure'); + } + + /** + * Execute a callback + * + * @access public + * @param Closure $callback Callback + * @param array $params Procedure params + * @return mixed + */ + public function executeCallback(Closure $callback, $params) + { + $reflection = new ReflectionFunction($callback); + + $arguments = $this->getArguments( + $params, + $reflection->getParameters(), + $reflection->getNumberOfRequiredParameters(), + $reflection->getNumberOfParameters() + ); + + return $reflection->invokeArgs($arguments); + } + + /** + * Execute a method + * + * @access public + * @param mixed $class Class name or instance + * @param string $method Method name + * @param array $params Procedure params + * @return mixed + */ + public function executeMethod($class, $method, $params) + { + $instance = is_string($class) ? new $class : $class; + $reflection = new ReflectionMethod($class, $method); + + $this->executeBeforeMethod($instance, $method); + + $arguments = $this->getArguments( + $params, + $reflection->getParameters(), + $reflection->getNumberOfRequiredParameters(), + $reflection->getNumberOfParameters() + ); + + return $reflection->invokeArgs($instance, $arguments); + } + + /** + * Execute before method if defined + * + * @access public + * @param mixed $object + * @param string $method + */ + public function executeBeforeMethod($object, $method) + { + if ($this->beforeMethodName !== '' && method_exists($object, $this->beforeMethodName)) { + call_user_func_array(array($object, $this->beforeMethodName), array($method)); + } + } + + /** + * Get procedure arguments + * + * @access public + * @param array $requestParams Incoming arguments + * @param array $methodParams Procedure arguments + * @param integer $nbRequiredParams Number of required parameters + * @param integer $nbMaxParams Maximum number of parameters + * @return array + */ + public function getArguments(array $requestParams, array $methodParams, $nbRequiredParams, $nbMaxParams) + { + $nbParams = count($requestParams); + + if ($nbParams < $nbRequiredParams) { + throw new InvalidArgumentException('Wrong number of arguments'); + } + + if ($nbParams > $nbMaxParams) { + throw new InvalidArgumentException('Too many arguments'); + } + + if ($this->isPositionalArguments($requestParams)) { + return $requestParams; + } + + return $this->getNamedArguments($requestParams, $methodParams); + } + + /** + * Return true if we have positional parameters + * + * @access public + * @param array $request_params Incoming arguments + * @return bool + */ + public function isPositionalArguments(array $request_params) + { + return array_keys($request_params) === range(0, count($request_params) - 1); + } + + /** + * Get named arguments + * + * @access public + * @param array $requestParams Incoming arguments + * @param array $methodParams Procedure arguments + * @return array + */ + public function getNamedArguments(array $requestParams, array $methodParams) + { + $params = array(); + + foreach ($methodParams as $p) { + $name = $p->getName(); + + if (array_key_exists($name, $requestParams)) { + $params[$name] = $requestParams[$name]; + } elseif ($p->isDefaultValueAvailable()) { + $params[$name] = $p->getDefaultValue(); + } else { + throw new InvalidArgumentException('Missing argument: '.$name); + } + } + + return $params; + } +} |