├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json └── src ├── AbstractServer.php ├── Cache.php ├── Client.php ├── Definition.php ├── Exception ├── BadMethodCallException.php ├── ExceptionInterface.php ├── InvalidArgumentException.php └── RuntimeException.php ├── Method ├── Callback.php ├── Definition.php ├── Parameter.php └── Prototype.php ├── Reflection.php ├── Reflection ├── AbstractFunction.php ├── Exception │ ├── BadMethodCallException.php │ ├── ExceptionInterface.php │ ├── InvalidArgumentException.php │ └── RuntimeException.php ├── Node.php ├── Prototype.php ├── ReflectionClass.php ├── ReflectionFunction.php ├── ReflectionMethod.php ├── ReflectionParameter.php └── ReflectionReturnValue.php └── Server.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file, in reverse chronological order by release. 4 | 5 | ## 2.8.2 - TBD 6 | 7 | ### Added 8 | 9 | - Nothing. 10 | 11 | ### Changed 12 | 13 | - Nothing. 14 | 15 | ### Deprecated 16 | 17 | - Nothing. 18 | 19 | ### Removed 20 | 21 | - Nothing. 22 | 23 | ### Fixed 24 | 25 | - Nothing. 26 | 27 | ## 2.8.1 - 2019-10-16 28 | 29 | ### Added 30 | 31 | - [#27](https://github.com/zendframework/zend-server/pull/27) adds support for PHP 7.3. 32 | 33 | ### Changed 34 | 35 | - Nothing. 36 | 37 | ### Deprecated 38 | 39 | - Nothing. 40 | 41 | ### Removed 42 | 43 | - Nothing. 44 | 45 | ### Fixed 46 | 47 | - [#30](https://github.com/zendframework/zend-server/pull/30) provides fixes to ensure the various Reflection classes can be safely de/serialized under PHP 7.4. 48 | 49 | ## 2.8.0 - 2018-04-30 50 | 51 | ### Added 52 | 53 | - [#26](https://github.com/zendframework/zend-server/pull/26) adds support for PHP 7.1 and 7.2. 54 | 55 | - [#19](https://github.com/zendframework/zend-server/pull/19) adds the ability to register any PHP callable with `Zend\Server\Method\Callback`. 56 | 57 | ### Changed 58 | 59 | - Nothing. 60 | 61 | ### Deprecated 62 | 63 | - Nothing. 64 | 65 | ### Removed 66 | 67 | - [#26](https://github.com/zendframework/zend-server/pull/26) removes support for HHVM. 68 | 69 | ### Fixed 70 | 71 | - [#20](https://github.com/zendframework/zend-server/pull/20) fixes how `Cache::save()` works when `Server::getFunctions()` returns an 72 | associative array instead of a `Definition`, ensuring it will also skip 73 | any blacklisted methods when used in this way. 74 | 75 | ## 2.7.0 - 2016-06-20 76 | 77 | ### Added 78 | 79 | - [#13](https://github.com/zendframework/zend-server/pull/13) adds and publishes 80 | the documentation to https://zendframework.github.io/zend-server 81 | - [#14](https://github.com/zendframework/zend-server/pull/14) adds support for 82 | zend-code v3 (while retaining support for zend-code v2). 83 | 84 | ### Deprecated 85 | 86 | - [#14](https://github.com/zendframework/zend-server/pull/14) deprecates all 87 | underscore-prefixed methods of `AbstractServer`; they will be renamed in 88 | version 3 to remove the prefix (though, in the case of `_dispatch()`, it will 89 | be renamed entirely, likely to `performDispatch()`). 90 | 91 | ### Removed 92 | 93 | - [#14](https://github.com/zendframework/zend-server/pull/14) removes support 94 | for PHP 5.5; the new minimum supported version of PHP is 5.6. 95 | 96 | ### Fixed 97 | 98 | - Nothing. 99 | 100 | ## 2.6.1 - 2016-02-04 101 | 102 | ### Added 103 | 104 | - Nothing. 105 | 106 | ### Deprecated 107 | 108 | - Nothing. 109 | 110 | ### Removed 111 | 112 | - Nothing. 113 | 114 | ### Fixed 115 | 116 | - [#11](https://github.com/zendframework/zend-server/pull/11) updates the 117 | dependencies to use zend-stdlib `^2.5 || ^3.0`. 118 | 119 | ## 2.6.0 - 2015-12-17 120 | 121 | ### Added 122 | 123 | - [#3](https://github.com/zendframework/zend-server/pull/3) adds support for 124 | resolving `{@inheritdoc}` annotations to the original parent during 125 | reflection. 126 | 127 | ### Deprecated 128 | 129 | - Nothing. 130 | 131 | ### Removed 132 | 133 | - Nothing. 134 | 135 | ### Fixed 136 | 137 | - [#2](https://github.com/zendframework/zend-server/pull/2) fixes misleading 138 | exception in reflectFunction that referenced reflectClass. 139 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2005-2018, Zend Technologies USA, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | - Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | - Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | - Neither the name of Zend Technologies USA, Inc. nor the names of its 15 | contributors may be used to endorse or promote products derived from this 16 | software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zend-server 2 | 3 | > ## Repository abandoned 2020-01-01 4 | > 5 | > This repository has moved to [laminas/laminas-server](https://github.com/laminas/laminas-server). 6 | 7 | [![Build Status](https://secure.travis-ci.org/zendframework/zend-server.svg?branch=master)](https://secure.travis-ci.org/zendframework/zend-server) 8 | [![Coverage Status](https://coveralls.io/repos/github/zendframework/zend-server/badge.svg?branch=master)](https://coveralls.io/github/zendframework/zend-server?branch=master) 9 | 10 | The zend-server family of classes provides functionality for the various server 11 | classes, including `Zend\XmlRpc\Server` and `Zend\Json\Server`. 12 | `Zend\Server\Server` provides an interface that mimics PHP 5’s SoapServer class; 13 | all server classes should implement this interface in order to provide a standard 14 | server API. 15 | 16 | - File issues at https://github.com/zendframework/zend-server/issues 17 | - Documentation is at https://docs.zendframework.com/zend-server/ 18 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zendframework/zend-server", 3 | "description": "Create Reflection-based RPC servers", 4 | "license": "BSD-3-Clause", 5 | "keywords": [ 6 | "zf", 7 | "zendframework", 8 | "server" 9 | ], 10 | "support": { 11 | "docs": "https://docs.zendframework.com/zend-server/", 12 | "issues": "https://github.com/zendframework/zend-server/issues", 13 | "source": "https://github.com/zendframework/zend-server", 14 | "rss": "https://github.com/zendframework/zend-server/releases.atom", 15 | "chat": "https://zendframework-slack.herokuapp.com", 16 | "forum": "https://discourse.zendframework.com/c/questions/components" 17 | }, 18 | "require": { 19 | "php": "^5.6 || ^7.0", 20 | "zendframework/zend-code": "^2.5 || ^3.0", 21 | "zendframework/zend-stdlib": "^2.5 || ^3.0" 22 | }, 23 | "require-dev": { 24 | "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", 25 | "zendframework/zend-coding-standard": "~1.0.0" 26 | }, 27 | "autoload": { 28 | "psr-4": { 29 | "Zend\\Server\\": "src/" 30 | } 31 | }, 32 | "autoload-dev": { 33 | "psr-4": { 34 | "ZendTest\\Server\\": "test/" 35 | }, 36 | "files": [ 37 | "test/TestAsset/reflectionTestFunction.php" 38 | ] 39 | }, 40 | "config": { 41 | "sort-packages": true 42 | }, 43 | "extra": { 44 | "branch-alias": { 45 | "dev-master": "2.8.x-dev", 46 | "dev-develop": "2.9.x-dev" 47 | } 48 | }, 49 | "scripts": { 50 | "check": [ 51 | "@cs-check", 52 | "@test" 53 | ], 54 | "cs-check": "phpcs", 55 | "cs-fix": "phpcbf", 56 | "test": "phpunit --colors=always", 57 | "test-coverage": "phpunit --colors=always --coverage-clover clover.xml" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/AbstractServer.php: -------------------------------------------------------------------------------- 1 | table = new Definition(); 36 | $this->table->setOverwriteExistingMethods($this->overwriteExistingMethods); 37 | } 38 | 39 | /** 40 | * Returns a list of registered methods 41 | * 42 | * Returns an array of method definitions. 43 | * 44 | * @return Definition 45 | */ 46 | public function getFunctions() 47 | { 48 | return $this->table; 49 | } 50 | 51 | /** 52 | * Build callback for method signature 53 | * 54 | * @deprecated Since 2.7.0; method will have private visibility starting in 3.0. 55 | * @param Reflection\AbstractFunction $reflection 56 | * @return Method\Callback 57 | */ 58 | // @codingStandardsIgnoreStart 59 | protected function _buildCallback(Reflection\AbstractFunction $reflection) 60 | { 61 | // @codingStandardsIgnoreEnd 62 | $callback = new Method\Callback(); 63 | if ($reflection instanceof Reflection\ReflectionMethod) { 64 | $callback->setType($reflection->isStatic() ? 'static' : 'instance') 65 | ->setClass($reflection->getDeclaringClass()->getName()) 66 | ->setMethod($reflection->getName()); 67 | } elseif ($reflection instanceof Reflection\ReflectionFunction) { 68 | $callback->setType('function') 69 | ->setFunction($reflection->getName()); 70 | } 71 | return $callback; 72 | } 73 | 74 | /** 75 | * Build a method signature 76 | * 77 | * @deprecated Since 2.7.0; method will be renamed to remove underscore 78 | * prefix in 3.0. 79 | * @param Reflection\AbstractFunction $reflection 80 | * @param null|string|object $class 81 | * @return Method\Definition 82 | * @throws Exception\RuntimeException on duplicate entry 83 | */ 84 | // @codingStandardsIgnoreStart 85 | protected function _buildSignature(Reflection\AbstractFunction $reflection, $class = null) 86 | { 87 | // @codingStandardsIgnoreEnd 88 | $ns = $reflection->getNamespace(); 89 | $name = $reflection->getName(); 90 | $method = empty($ns) ? $name : $ns . '.' . $name; 91 | 92 | if (! $this->overwriteExistingMethods && $this->table->hasMethod($method)) { 93 | throw new Exception\RuntimeException('Duplicate method registered: ' . $method); 94 | } 95 | 96 | $definition = new Method\Definition(); 97 | $definition->setName($method) 98 | ->setCallback($this->_buildCallback($reflection)) 99 | ->setMethodHelp($reflection->getDescription()) 100 | ->setInvokeArguments($reflection->getInvokeArguments()); 101 | 102 | foreach ($reflection->getPrototypes() as $proto) { 103 | $prototype = new Method\Prototype(); 104 | $prototype->setReturnType($this->_fixType($proto->getReturnType())); 105 | foreach ($proto->getParameters() as $parameter) { 106 | $param = new Method\Parameter([ 107 | 'type' => $this->_fixType($parameter->getType()), 108 | 'name' => $parameter->getName(), 109 | 'optional' => $parameter->isOptional(), 110 | ]); 111 | if ($parameter->isDefaultValueAvailable()) { 112 | $param->setDefaultValue($parameter->getDefaultValue()); 113 | } 114 | $prototype->addParameter($param); 115 | } 116 | $definition->addPrototype($prototype); 117 | } 118 | if (is_object($class)) { 119 | $definition->setObject($class); 120 | } 121 | $this->table->addMethod($definition); 122 | return $definition; 123 | } 124 | 125 | /** 126 | * Dispatch method 127 | * 128 | * @deprecated Since 2.7.0; method will be renamed to remove underscore 129 | * prefix in 3.0. 130 | * @param Method\Definition $invokable 131 | * @param array $params 132 | * @return mixed 133 | */ 134 | // @codingStandardsIgnoreStart 135 | protected function _dispatch(Method\Definition $invokable, array $params) 136 | { 137 | // @codingStandardsIgnoreEnd 138 | $callback = $invokable->getCallback(); 139 | $type = $callback->getType(); 140 | 141 | if ('function' == $type) { 142 | $function = $callback->getFunction(); 143 | return call_user_func_array($function, $params); 144 | } 145 | 146 | $class = $callback->getClass(); 147 | $method = $callback->getMethod(); 148 | 149 | if ('static' == $type) { 150 | return call_user_func_array([$class, $method], $params); 151 | } 152 | 153 | $object = $invokable->getObject(); 154 | if (! is_object($object)) { 155 | $invokeArgs = $invokable->getInvokeArguments(); 156 | if (! empty($invokeArgs)) { 157 | $reflection = new ReflectionClass($class); 158 | $object = $reflection->newInstanceArgs($invokeArgs); 159 | } else { 160 | $object = new $class; 161 | } 162 | } 163 | return call_user_func_array([$object, $method], $params); 164 | } 165 | 166 | // @codingStandardsIgnoreStart 167 | /** 168 | * Map PHP type to protocol type 169 | * 170 | * @deprecated Since 2.7.0; method will be renamed to remove underscore 171 | * prefix in 3.0. 172 | * @param string $type 173 | * @return string 174 | */ 175 | abstract protected function _fixType($type); 176 | // @codingStandardsIgnoreEnd 177 | } 178 | -------------------------------------------------------------------------------- /src/Cache.php: -------------------------------------------------------------------------------- 1 | getFunctions()); 42 | 43 | ErrorHandler::start(); 44 | $test = file_put_contents($filename, serialize($methods)); 45 | ErrorHandler::stop(); 46 | if (0 === $test) { 47 | return false; 48 | } 49 | 50 | return true; 51 | } 52 | 53 | /** 54 | * Load server definition from a file 55 | * 56 | * Unserializes a stored server definition from $filename. Returns false if 57 | * it fails in any way, true on success. 58 | * 59 | * Useful to prevent needing to build the server definition on each 60 | * request. Sample usage: 61 | * 62 | * 63 | * if (!Zend\Server\Cache::get($filename, $server)) { 64 | * require_once 'Some/Service/ServiceClass.php'; 65 | * require_once 'Another/Service/ServiceClass.php'; 66 | * 67 | * // Attach Some\Service\ServiceClass with namespace 'some' 68 | * $server->attach('Some\Service\ServiceClass', 'some'); 69 | * 70 | * // Attach Another\Service\ServiceClass with namespace 'another' 71 | * $server->attach('Another\Service\ServiceClass', 'another'); 72 | * 73 | * Zend\Server\Cache::save($filename, $server); 74 | * } 75 | * 76 | * $response = $server->handle(); 77 | * echo $response; 78 | * 79 | * 80 | * @param string $filename 81 | * @param \Zend\Server\Server $server 82 | * @return bool 83 | */ 84 | public static function get($filename, Server $server) 85 | { 86 | if (! is_string($filename) || ! file_exists($filename) || ! is_readable($filename)) { 87 | return false; 88 | } 89 | 90 | ErrorHandler::start(); 91 | $dispatch = file_get_contents($filename); 92 | ErrorHandler::stop(); 93 | if (false === $dispatch) { 94 | return false; 95 | } 96 | 97 | ErrorHandler::start(E_NOTICE); 98 | $dispatchArray = unserialize($dispatch); 99 | ErrorHandler::stop(); 100 | if (false === $dispatchArray) { 101 | return false; 102 | } 103 | 104 | $server->loadFunctions($dispatchArray); 105 | 106 | return true; 107 | } 108 | 109 | /** 110 | * Remove a cache file 111 | * 112 | * @param string $filename 113 | * @return bool 114 | */ 115 | public static function delete($filename) 116 | { 117 | if (is_string($filename) && file_exists($filename)) { 118 | unlink($filename); 119 | return true; 120 | } 121 | 122 | return false; 123 | } 124 | 125 | /** 126 | * @var array|Definition $methods 127 | * @return array|Definition 128 | */ 129 | private static function createDefinition($methods) 130 | { 131 | if ($methods instanceof Definition) { 132 | return self::createDefinitionFromMethodsDefinition($methods); 133 | } 134 | 135 | if (is_array($methods)) { 136 | return self::createDefinitionFromMethodsArray($methods); 137 | } 138 | 139 | return $methods; 140 | } 141 | 142 | /** 143 | * @return Definition 144 | */ 145 | private static function createDefinitionFromMethodsDefinition(Definition $methods) 146 | { 147 | $definition = new Definition(); 148 | foreach ($methods as $method) { 149 | if (in_array($method->getName(), static::$skipMethods, true)) { 150 | continue; 151 | } 152 | $definition->addMethod($method); 153 | } 154 | return $definition; 155 | } 156 | 157 | /** 158 | * @return array 159 | */ 160 | private static function createDefinitionFromMethodsArray(array $methods) 161 | { 162 | foreach (array_keys($methods) as $methodName) { 163 | if (in_array($methodName, static::$skipMethods, true)) { 164 | unset($methods[$methodName]); 165 | } 166 | } 167 | return $methods; 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/Client.php: -------------------------------------------------------------------------------- 1 | setMethods($methods); 37 | } 38 | } 39 | 40 | /** 41 | * Set flag indicating whether or not overwriting existing methods is allowed 42 | * 43 | * @param mixed $flag 44 | * @return \Zend\Server\Definition 45 | */ 46 | public function setOverwriteExistingMethods($flag) 47 | { 48 | $this->overwriteExistingMethods = (bool) $flag; 49 | return $this; 50 | } 51 | 52 | /** 53 | * Add method to definition 54 | * 55 | * @param array|\Zend\Server\Method\Definition $method 56 | * @param null|string $name 57 | * @return \Zend\Server\Definition 58 | * @throws \Zend\Server\Exception\InvalidArgumentException if duplicate or invalid method provided 59 | */ 60 | public function addMethod($method, $name = null) 61 | { 62 | if (is_array($method)) { 63 | $method = new Method\Definition($method); 64 | } elseif (! $method instanceof Method\Definition) { 65 | throw new Exception\InvalidArgumentException('Invalid method provided'); 66 | } 67 | 68 | if (is_numeric($name)) { 69 | $name = null; 70 | } 71 | if (null !== $name) { 72 | $method->setName($name); 73 | } else { 74 | $name = $method->getName(); 75 | } 76 | if (null === $name) { 77 | throw new Exception\InvalidArgumentException('No method name provided'); 78 | } 79 | 80 | if (! $this->overwriteExistingMethods && array_key_exists($name, $this->methods)) { 81 | throw new Exception\InvalidArgumentException(sprintf('Method by name of "%s" already exists', $name)); 82 | } 83 | $this->methods[$name] = $method; 84 | return $this; 85 | } 86 | 87 | /** 88 | * Add multiple methods 89 | * 90 | * @param array $methods Array of \Zend\Server\Method\Definition objects or arrays 91 | * @return \Zend\Server\Definition 92 | */ 93 | public function addMethods(array $methods) 94 | { 95 | foreach ($methods as $key => $method) { 96 | $this->addMethod($method, $key); 97 | } 98 | return $this; 99 | } 100 | 101 | /** 102 | * Set all methods at once (overwrite) 103 | * 104 | * @param array $methods Array of \Zend\Server\Method\Definition objects or arrays 105 | * @return \Zend\Server\Definition 106 | */ 107 | public function setMethods(array $methods) 108 | { 109 | $this->clearMethods(); 110 | $this->addMethods($methods); 111 | return $this; 112 | } 113 | 114 | /** 115 | * Does the definition have the given method? 116 | * 117 | * @param string $method 118 | * @return bool 119 | */ 120 | public function hasMethod($method) 121 | { 122 | return array_key_exists($method, $this->methods); 123 | } 124 | 125 | /** 126 | * Get a given method definition 127 | * 128 | * @param string $method 129 | * @return null|\Zend\Server\Method\Definition 130 | */ 131 | public function getMethod($method) 132 | { 133 | if ($this->hasMethod($method)) { 134 | return $this->methods[$method]; 135 | } 136 | return false; 137 | } 138 | 139 | /** 140 | * Get all method definitions 141 | * 142 | * @return array Array of \Zend\Server\Method\Definition objects 143 | */ 144 | public function getMethods() 145 | { 146 | return $this->methods; 147 | } 148 | 149 | /** 150 | * Remove a method definition 151 | * 152 | * @param string $method 153 | * @return \Zend\Server\Definition 154 | */ 155 | public function removeMethod($method) 156 | { 157 | if ($this->hasMethod($method)) { 158 | unset($this->methods[$method]); 159 | } 160 | return $this; 161 | } 162 | 163 | /** 164 | * Clear all method definitions 165 | * 166 | * @return \Zend\Server\Definition 167 | */ 168 | public function clearMethods() 169 | { 170 | $this->methods = []; 171 | return $this; 172 | } 173 | 174 | /** 175 | * Cast definition to an array 176 | * 177 | * @return array 178 | */ 179 | public function toArray() 180 | { 181 | $methods = []; 182 | foreach ($this->getMethods() as $key => $method) { 183 | $methods[$key] = $method->toArray(); 184 | } 185 | return $methods; 186 | } 187 | 188 | /** 189 | * Countable: count of methods 190 | * 191 | * @return int 192 | */ 193 | public function count() 194 | { 195 | return count($this->methods); 196 | } 197 | 198 | /** 199 | * Iterator: current item 200 | * 201 | * @return Method\Definition 202 | */ 203 | public function current() 204 | { 205 | return current($this->methods); 206 | } 207 | 208 | /** 209 | * Iterator: current item key 210 | * 211 | * @return int|string 212 | */ 213 | public function key() 214 | { 215 | return key($this->methods); 216 | } 217 | 218 | /** 219 | * Iterator: advance to next method 220 | * 221 | * @return Method\Definition 222 | */ 223 | public function next() 224 | { 225 | return next($this->methods); 226 | } 227 | 228 | /** 229 | * Iterator: return to first method 230 | * 231 | * @return void 232 | */ 233 | public function rewind() 234 | { 235 | reset($this->methods); 236 | } 237 | 238 | /** 239 | * Iterator: is the current index valid? 240 | * 241 | * @return bool 242 | */ 243 | public function valid() 244 | { 245 | return (bool) $this->current(); 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /src/Exception/BadMethodCallException.php: -------------------------------------------------------------------------------- 1 | setOptions($options); 51 | } 52 | } 53 | 54 | /** 55 | * Set object state from array of options 56 | * 57 | * @param array $options 58 | * @return \Zend\Server\Method\Callback 59 | */ 60 | public function setOptions(array $options) 61 | { 62 | foreach ($options as $key => $value) { 63 | $method = 'set' . ucfirst($key); 64 | if (method_exists($this, $method)) { 65 | $this->$method($value); 66 | } 67 | } 68 | return $this; 69 | } 70 | 71 | /** 72 | * Set callback class 73 | * 74 | * @param string $class 75 | * @return \Zend\Server\Method\Callback 76 | */ 77 | public function setClass($class) 78 | { 79 | if (is_object($class)) { 80 | $class = get_class($class); 81 | } 82 | $this->class = $class; 83 | return $this; 84 | } 85 | 86 | /** 87 | * Get callback class 88 | * 89 | * @return string|null 90 | */ 91 | public function getClass() 92 | { 93 | return $this->class; 94 | } 95 | 96 | /** 97 | * Set callback function 98 | * 99 | * @param string|callable $function 100 | * @return \Zend\Server\Method\Callback 101 | */ 102 | public function setFunction($function) 103 | { 104 | $this->function = is_callable($function) ? $function : (string) $function; 105 | $this->setType('function'); 106 | return $this; 107 | } 108 | 109 | /** 110 | * Get callback function 111 | * 112 | * @return null|string|callable 113 | */ 114 | public function getFunction() 115 | { 116 | return $this->function; 117 | } 118 | 119 | /** 120 | * Set callback class method 121 | * 122 | * @param string $method 123 | * @return \Zend\Server\Method\Callback 124 | */ 125 | public function setMethod($method) 126 | { 127 | $this->method = $method; 128 | return $this; 129 | } 130 | 131 | /** 132 | * Get callback class method 133 | * 134 | * @return null|string 135 | */ 136 | public function getMethod() 137 | { 138 | return $this->method; 139 | } 140 | 141 | /** 142 | * Set callback type 143 | * 144 | * @param string $type 145 | * @return \Zend\Server\Method\Callback 146 | * @throws Server\Exception\InvalidArgumentException 147 | */ 148 | public function setType($type) 149 | { 150 | if (! in_array($type, $this->types)) { 151 | throw new Server\Exception\InvalidArgumentException(sprintf( 152 | 'Invalid method callback type "%s" passed to %s', 153 | $type, 154 | __METHOD__ 155 | )); 156 | } 157 | $this->type = $type; 158 | return $this; 159 | } 160 | 161 | /** 162 | * Get callback type 163 | * 164 | * @return string 165 | */ 166 | public function getType() 167 | { 168 | return $this->type; 169 | } 170 | 171 | /** 172 | * Cast callback to array 173 | * 174 | * @return array 175 | */ 176 | public function toArray() 177 | { 178 | $type = $this->getType(); 179 | $array = [ 180 | 'type' => $type, 181 | ]; 182 | if ('function' == $type) { 183 | $array['function'] = $this->getFunction(); 184 | } else { 185 | $array['class'] = $this->getClass(); 186 | $array['method'] = $this->getMethod(); 187 | } 188 | return $array; 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/Method/Definition.php: -------------------------------------------------------------------------------- 1 | setOptions($options); 56 | } 57 | } 58 | 59 | /** 60 | * Set object state from options 61 | * 62 | * @param array $options 63 | * @return \Zend\Server\Method\Definition 64 | */ 65 | public function setOptions(array $options) 66 | { 67 | foreach ($options as $key => $value) { 68 | $method = 'set' . ucfirst($key); 69 | if (method_exists($this, $method)) { 70 | $this->$method($value); 71 | } 72 | } 73 | return $this; 74 | } 75 | 76 | /** 77 | * Set method name 78 | * 79 | * @param string $name 80 | * @return \Zend\Server\Method\Definition 81 | */ 82 | public function setName($name) 83 | { 84 | $this->name = (string) $name; 85 | return $this; 86 | } 87 | 88 | /** 89 | * Get method name 90 | * 91 | * @return string 92 | */ 93 | public function getName() 94 | { 95 | return $this->name; 96 | } 97 | 98 | /** 99 | * Set method callback 100 | * 101 | * @param array|\Zend\Server\Method\Callback $callback 102 | * @throws Server\Exception\InvalidArgumentException 103 | * @return \Zend\Server\Method\Definition 104 | */ 105 | public function setCallback($callback) 106 | { 107 | if (is_array($callback)) { 108 | $callback = new Callback($callback); 109 | } elseif (! $callback instanceof Callback) { 110 | throw new Server\Exception\InvalidArgumentException('Invalid method callback provided'); 111 | } 112 | $this->callback = $callback; 113 | return $this; 114 | } 115 | 116 | /** 117 | * Get method callback 118 | * 119 | * @return \Zend\Server\Method\Callback 120 | */ 121 | public function getCallback() 122 | { 123 | return $this->callback; 124 | } 125 | 126 | /** 127 | * Add prototype to method definition 128 | * 129 | * @param array|\Zend\Server\Method\Prototype $prototype 130 | * @throws Server\Exception\InvalidArgumentException 131 | * @return \Zend\Server\Method\Definition 132 | */ 133 | public function addPrototype($prototype) 134 | { 135 | if (is_array($prototype)) { 136 | $prototype = new Prototype($prototype); 137 | } elseif (! $prototype instanceof Prototype) { 138 | throw new Server\Exception\InvalidArgumentException('Invalid method prototype provided'); 139 | } 140 | $this->prototypes[] = $prototype; 141 | return $this; 142 | } 143 | 144 | /** 145 | * Add multiple prototypes at once 146 | * 147 | * @param array $prototypes Array of \Zend\Server\Method\Prototype objects or arrays 148 | * @return \Zend\Server\Method\Definition 149 | */ 150 | public function addPrototypes(array $prototypes) 151 | { 152 | foreach ($prototypes as $prototype) { 153 | $this->addPrototype($prototype); 154 | } 155 | return $this; 156 | } 157 | 158 | /** 159 | * Set all prototypes at once (overwrites) 160 | * 161 | * @param array $prototypes Array of \Zend\Server\Method\Prototype objects or arrays 162 | * @return \Zend\Server\Method\Definition 163 | */ 164 | public function setPrototypes(array $prototypes) 165 | { 166 | $this->prototypes = []; 167 | $this->addPrototypes($prototypes); 168 | return $this; 169 | } 170 | 171 | /** 172 | * Get all prototypes 173 | * 174 | * @return array $prototypes Array of \Zend\Server\Method\Prototype objects or arrays 175 | */ 176 | public function getPrototypes() 177 | { 178 | return $this->prototypes; 179 | } 180 | 181 | /** 182 | * Set method help 183 | * 184 | * @param string $methodHelp 185 | * @return \Zend\Server\Method\Definition 186 | */ 187 | public function setMethodHelp($methodHelp) 188 | { 189 | $this->methodHelp = (string) $methodHelp; 190 | return $this; 191 | } 192 | 193 | /** 194 | * Get method help 195 | * 196 | * @return string 197 | */ 198 | public function getMethodHelp() 199 | { 200 | return $this->methodHelp; 201 | } 202 | 203 | /** 204 | * Set object to use with method calls 205 | * 206 | * @param object $object 207 | * @throws Server\Exception\InvalidArgumentException 208 | * @return \Zend\Server\Method\Definition 209 | */ 210 | public function setObject($object) 211 | { 212 | if (! is_object($object) && (null !== $object)) { 213 | throw new Server\Exception\InvalidArgumentException(sprintf( 214 | 'Invalid object passed to %s', 215 | __METHOD__ 216 | )); 217 | } 218 | $this->object = $object; 219 | return $this; 220 | } 221 | 222 | /** 223 | * Get object to use with method calls 224 | * 225 | * @return null|object 226 | */ 227 | public function getObject() 228 | { 229 | return $this->object; 230 | } 231 | 232 | /** 233 | * Set invoke arguments 234 | * 235 | * @param array $invokeArguments 236 | * @return \Zend\Server\Method\Definition 237 | */ 238 | public function setInvokeArguments(array $invokeArguments) 239 | { 240 | $this->invokeArguments = $invokeArguments; 241 | return $this; 242 | } 243 | 244 | /** 245 | * Retrieve invoke arguments 246 | * 247 | * @return array 248 | */ 249 | public function getInvokeArguments() 250 | { 251 | return $this->invokeArguments; 252 | } 253 | 254 | /** 255 | * Serialize to array 256 | * 257 | * @return array 258 | */ 259 | public function toArray() 260 | { 261 | $prototypes = $this->getPrototypes(); 262 | $signatures = []; 263 | foreach ($prototypes as $prototype) { 264 | $signatures[] = $prototype->toArray(); 265 | } 266 | 267 | return [ 268 | 'name' => $this->getName(), 269 | 'callback' => $this->getCallback()->toArray(), 270 | 'prototypes' => $signatures, 271 | 'methodHelp' => $this->getMethodHelp(), 272 | 'invokeArguments' => $this->getInvokeArguments(), 273 | 'object' => $this->getObject(), 274 | ]; 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /src/Method/Parameter.php: -------------------------------------------------------------------------------- 1 | setOptions($options); 59 | } 60 | } 61 | 62 | /** 63 | * Set object state from array of options 64 | * 65 | * @param array $options 66 | * @return \Zend\Server\Method\Parameter 67 | */ 68 | public function setOptions(array $options) 69 | { 70 | foreach ($options as $key => $value) { 71 | $method = 'set' . ucfirst($key); 72 | if (method_exists($this, $method)) { 73 | $this->$method($value); 74 | } 75 | } 76 | return $this; 77 | } 78 | 79 | /** 80 | * Set default value 81 | * 82 | * @param mixed $defaultValue 83 | * @return \Zend\Server\Method\Parameter 84 | */ 85 | public function setDefaultValue($defaultValue) 86 | { 87 | $this->defaultValue = $defaultValue; 88 | return $this; 89 | } 90 | 91 | /** 92 | * Retrieve default value 93 | * 94 | * @return mixed 95 | */ 96 | public function getDefaultValue() 97 | { 98 | return $this->defaultValue; 99 | } 100 | 101 | /** 102 | * Set description 103 | * 104 | * @param string $description 105 | * @return \Zend\Server\Method\Parameter 106 | */ 107 | public function setDescription($description) 108 | { 109 | $this->description = (string) $description; 110 | return $this; 111 | } 112 | 113 | /** 114 | * Retrieve description 115 | * 116 | * @return string 117 | */ 118 | public function getDescription() 119 | { 120 | return $this->description; 121 | } 122 | 123 | /** 124 | * Set name 125 | * 126 | * @param string $name 127 | * @return \Zend\Server\Method\Parameter 128 | */ 129 | public function setName($name) 130 | { 131 | $this->name = (string) $name; 132 | return $this; 133 | } 134 | 135 | /** 136 | * Retrieve name 137 | * 138 | * @return string 139 | */ 140 | public function getName() 141 | { 142 | return $this->name; 143 | } 144 | 145 | /** 146 | * Set optional flag 147 | * 148 | * @param bool $flag 149 | * @return \Zend\Server\Method\Parameter 150 | */ 151 | public function setOptional($flag) 152 | { 153 | $this->optional = (bool) $flag; 154 | return $this; 155 | } 156 | 157 | /** 158 | * Is the parameter optional? 159 | * 160 | * @return bool 161 | */ 162 | public function isOptional() 163 | { 164 | return $this->optional; 165 | } 166 | 167 | /** 168 | * Set parameter type 169 | * 170 | * @param string $type 171 | * @return \Zend\Server\Method\Parameter 172 | */ 173 | public function setType($type) 174 | { 175 | $this->type = (string) $type; 176 | return $this; 177 | } 178 | 179 | /** 180 | * Retrieve parameter type 181 | * 182 | * @return string 183 | */ 184 | public function getType() 185 | { 186 | return $this->type; 187 | } 188 | 189 | /** 190 | * Cast to array 191 | * 192 | * @return array 193 | */ 194 | public function toArray() 195 | { 196 | return [ 197 | 'type' => $this->getType(), 198 | 'name' => $this->getName(), 199 | 'optional' => $this->isOptional(), 200 | 'defaultValue' => $this->getDefaultValue(), 201 | 'description' => $this->getDescription(), 202 | ]; 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/Method/Prototype.php: -------------------------------------------------------------------------------- 1 | setOptions($options); 39 | } 40 | } 41 | 42 | /** 43 | * Set return value 44 | * 45 | * @param string $returnType 46 | * @return \Zend\Server\Method\Prototype 47 | */ 48 | public function setReturnType($returnType) 49 | { 50 | $this->returnType = $returnType; 51 | return $this; 52 | } 53 | 54 | /** 55 | * Retrieve return type 56 | * 57 | * @return string 58 | */ 59 | public function getReturnType() 60 | { 61 | return $this->returnType; 62 | } 63 | 64 | /** 65 | * Add a parameter 66 | * 67 | * @param string $parameter 68 | * @return \Zend\Server\Method\Prototype 69 | */ 70 | public function addParameter($parameter) 71 | { 72 | if ($parameter instanceof Parameter) { 73 | $this->parameters[] = $parameter; 74 | if (null !== ($name = $parameter->getName())) { 75 | $this->parameterNameMap[$name] = count($this->parameters) - 1; 76 | } 77 | } else { 78 | $parameter = new Parameter([ 79 | 'type' => (string) $parameter, 80 | ]); 81 | $this->parameters[] = $parameter; 82 | } 83 | return $this; 84 | } 85 | 86 | /** 87 | * Add parameters 88 | * 89 | * @param array $parameters 90 | * @return \Zend\Server\Method\Prototype 91 | */ 92 | public function addParameters(array $parameters) 93 | { 94 | foreach ($parameters as $parameter) { 95 | $this->addParameter($parameter); 96 | } 97 | return $this; 98 | } 99 | 100 | /** 101 | * Set parameters 102 | * 103 | * @param array $parameters 104 | * @return \Zend\Server\Method\Prototype 105 | */ 106 | public function setParameters(array $parameters) 107 | { 108 | $this->parameters = []; 109 | $this->parameterNameMap = []; 110 | $this->addParameters($parameters); 111 | return $this; 112 | } 113 | 114 | /** 115 | * Retrieve parameters as list of types 116 | * 117 | * @return array 118 | */ 119 | public function getParameters() 120 | { 121 | $types = []; 122 | foreach ($this->parameters as $parameter) { 123 | $types[] = $parameter->getType(); 124 | } 125 | return $types; 126 | } 127 | 128 | /** 129 | * Get parameter objects 130 | * 131 | * @return array 132 | */ 133 | public function getParameterObjects() 134 | { 135 | return $this->parameters; 136 | } 137 | 138 | /** 139 | * Retrieve a single parameter by name or index 140 | * 141 | * @param string|int $index 142 | * @return null|\Zend\Server\Method\Parameter 143 | */ 144 | public function getParameter($index) 145 | { 146 | if (! is_string($index) && ! is_numeric($index)) { 147 | return; 148 | } 149 | if (array_key_exists($index, $this->parameterNameMap)) { 150 | $index = $this->parameterNameMap[$index]; 151 | } 152 | if (array_key_exists($index, $this->parameters)) { 153 | return $this->parameters[$index]; 154 | } 155 | return; 156 | } 157 | 158 | /** 159 | * Set object state from array 160 | * 161 | * @param array $options 162 | * @return \Zend\Server\Method\Prototype 163 | */ 164 | public function setOptions(array $options) 165 | { 166 | foreach ($options as $key => $value) { 167 | $method = 'set' . ucfirst($key); 168 | if (method_exists($this, $method)) { 169 | $this->$method($value); 170 | } 171 | } 172 | return $this; 173 | } 174 | 175 | /** 176 | * Serialize to array 177 | * 178 | * @return array 179 | */ 180 | public function toArray() 181 | { 182 | return [ 183 | 'returnType' => $this->getReturnType(), 184 | 'parameters' => $this->getParameters(), 185 | ]; 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/Reflection.php: -------------------------------------------------------------------------------- 1 | reflection = $r; 102 | 103 | // Determine namespace 104 | if (null !== $namespace) { 105 | $this->setNamespace($namespace); 106 | } 107 | 108 | // Determine arguments 109 | if (is_array($argv)) { 110 | $this->argv = $argv; 111 | } 112 | 113 | // If method call, need to store some info on the class 114 | if ($r instanceof PhpReflectionMethod) { 115 | $this->class = $r->getDeclaringClass()->getName(); 116 | } 117 | 118 | $this->name = $r->getName(); 119 | 120 | // Perform some introspection 121 | $this->reflect(); 122 | } 123 | 124 | /** 125 | * Create signature node tree 126 | * 127 | * Recursive method to build the signature node tree. Increments through 128 | * each array in {@link $sigParams}, adding every value of the next level 129 | * to the current value (unless the current value is null). 130 | * 131 | * @param \Zend\Server\Reflection\Node $parent 132 | * @param int $level 133 | * @return void 134 | */ 135 | protected function addTree(Node $parent, $level = 0) 136 | { 137 | if ($level >= $this->sigParamsDepth) { 138 | return; 139 | } 140 | 141 | foreach ($this->sigParams[$level] as $value) { 142 | $node = new Node($value, $parent); 143 | if ((null !== $value) && ($this->sigParamsDepth > $level + 1)) { 144 | $this->addTree($node, $level + 1); 145 | } 146 | } 147 | } 148 | 149 | /** 150 | * Build the signature tree 151 | * 152 | * Builds a signature tree starting at the return values and descending 153 | * through each method argument. Returns an array of 154 | * {@link \Zend\Server\Reflection\Node}s. 155 | * 156 | * @return array 157 | */ 158 | protected function buildTree() 159 | { 160 | $returnTree = []; 161 | foreach ($this->return as $value) { 162 | $node = new Node($value); 163 | $this->addTree($node); 164 | $returnTree[] = $node; 165 | } 166 | 167 | return $returnTree; 168 | } 169 | 170 | /** 171 | * Build method signatures 172 | * 173 | * Builds method signatures using the array of return types and the array of 174 | * parameters types 175 | * 176 | * @param array $return Array of return types 177 | * @param string $returnDesc Return value description 178 | * @param array $paramTypes Array of arguments (each an array of types) 179 | * @param array $paramDesc Array of parameter descriptions 180 | * @return array 181 | */ 182 | protected function buildSignatures($return, $returnDesc, $paramTypes, $paramDesc) 183 | { 184 | $this->return = $return; 185 | $this->returnDesc = $returnDesc; 186 | $this->paramDesc = $paramDesc; 187 | $this->sigParams = $paramTypes; 188 | $this->sigParamsDepth = count($paramTypes); 189 | $signatureTrees = $this->buildTree(); 190 | $signatures = []; 191 | 192 | $endPoints = []; 193 | foreach ($signatureTrees as $root) { 194 | $tmp = $root->getEndPoints(); 195 | if (empty($tmp)) { 196 | $endPoints = array_merge($endPoints, [$root]); 197 | } else { 198 | $endPoints = array_merge($endPoints, $tmp); 199 | } 200 | } 201 | 202 | foreach ($endPoints as $node) { 203 | if (! $node instanceof Node) { 204 | continue; 205 | } 206 | 207 | $signature = []; 208 | do { 209 | array_unshift($signature, $node->getValue()); 210 | $node = $node->getParent(); 211 | } while ($node instanceof Node); 212 | 213 | $signatures[] = $signature; 214 | } 215 | 216 | // Build prototypes 217 | $params = $this->reflection->getParameters(); 218 | foreach ($signatures as $signature) { 219 | $return = new ReflectionReturnValue(array_shift($signature), $this->returnDesc); 220 | $tmp = []; 221 | foreach ($signature as $key => $type) { 222 | $param = new ReflectionParameter( 223 | $params[$key], 224 | $type, 225 | (isset($this->paramDesc[$key]) ? $this->paramDesc[$key] : null) 226 | ); 227 | $param->setPosition($key); 228 | $tmp[] = $param; 229 | } 230 | 231 | $this->prototypes[] = new Prototype($return, $tmp); 232 | } 233 | } 234 | 235 | /** 236 | * Use code reflection to create method signatures 237 | * 238 | * Determines the method help/description text from the function DocBlock 239 | * comment. Determines method signatures using a combination of 240 | * ReflectionFunction and parsing of DocBlock @param and @return values. 241 | * 242 | * @throws Exception\RuntimeException 243 | * @return array 244 | */ 245 | protected function reflect() 246 | { 247 | $function = $this->reflection; 248 | $paramCount = $function->getNumberOfParameters(); 249 | $parameters = $function->getParameters(); 250 | 251 | if (! $this->docComment) { 252 | $this->docComment = $function->getDocComment(); 253 | } 254 | 255 | $scanner = new DocBlockReflection(($this->docComment) ? : '/***/'); 256 | $helpText = $scanner->getLongDescription(); 257 | /* @var \Zend\Code\Reflection\DocBlock\Tag\ParamTag[] $paramTags */ 258 | $paramTags = $scanner->getTags('param'); 259 | /* @var \Zend\Code\Reflection\DocBlock\Tag\ReturnTag $returnTag */ 260 | $returnTag = $scanner->getTag('return'); 261 | 262 | if (empty($helpText)) { 263 | $helpText = $scanner->getShortDescription(); 264 | if (empty($helpText)) { 265 | $helpText = $function->getName(); 266 | } 267 | } 268 | $this->setDescription($helpText); 269 | 270 | if ($returnTag) { 271 | $return = []; 272 | $returnDesc = $returnTag->getDescription(); 273 | foreach ($returnTag->getTypes() as $type) { 274 | $return[] = $type; 275 | } 276 | } else { 277 | $return = ['void']; 278 | $returnDesc = ''; 279 | } 280 | 281 | $paramTypesTmp = []; 282 | $paramDesc = []; 283 | if (empty($paramTags)) { 284 | foreach ($parameters as $param) { 285 | $paramTypesTmp[] = [($param->isArray()) ? 'array' : 'mixed']; 286 | $paramDesc[] = ''; 287 | } 288 | } else { 289 | $paramDesc = []; 290 | foreach ($paramTags as $paramTag) { 291 | $paramTypesTmp[] = $paramTag->getTypes(); 292 | $paramDesc[] = ($paramTag->getDescription()) ? : ''; 293 | } 294 | } 295 | 296 | // Get all param types as arrays 297 | $nParamTypesTmp = count($paramTypesTmp); 298 | if ($nParamTypesTmp < $paramCount) { 299 | $start = $paramCount - $nParamTypesTmp; 300 | for ($i = $start; $i < $paramCount; ++$i) { 301 | $paramTypesTmp[$i] = ['mixed']; 302 | $paramDesc[$i] = ''; 303 | } 304 | } elseif ($nParamTypesTmp != $paramCount) { 305 | throw new Exception\RuntimeException( 306 | 'Variable number of arguments is not supported for services (except optional parameters). ' 307 | . 'Number of function arguments must correspond to actual number of arguments described in a docblock.' 308 | ); 309 | } 310 | 311 | $paramTypes = []; 312 | foreach ($paramTypesTmp as $i => $param) { 313 | if ($parameters[$i]->isOptional()) { 314 | array_unshift($param, null); 315 | } 316 | $paramTypes[] = $param; 317 | } 318 | 319 | $this->buildSignatures($return, $returnDesc, $paramTypes, $paramDesc); 320 | } 321 | 322 | /** 323 | * Proxy reflection calls 324 | * 325 | * @param string $method 326 | * @param array $args 327 | * @throws Exception\BadMethodCallException 328 | * @return mixed 329 | */ 330 | public function __call($method, $args) 331 | { 332 | if (method_exists($this->reflection, $method)) { 333 | return call_user_func_array([$this->reflection, $method], $args); 334 | } 335 | 336 | throw new Exception\BadMethodCallException('Invalid reflection method ("' . $method . '")'); 337 | } 338 | 339 | /** 340 | * Retrieve configuration parameters 341 | * 342 | * Values are retrieved by key from {@link $config}. Returns null if no 343 | * value found. 344 | * 345 | * @param string $key 346 | * @return mixed 347 | */ 348 | public function __get($key) 349 | { 350 | if (isset($this->config[$key])) { 351 | return $this->config[$key]; 352 | } 353 | 354 | return; 355 | } 356 | 357 | /** 358 | * Set configuration parameters 359 | * 360 | * Values are stored by $key in {@link $config}. 361 | * 362 | * @param string $key 363 | * @param mixed $value 364 | * @return void 365 | */ 366 | public function __set($key, $value) 367 | { 368 | $this->config[$key] = $value; 369 | } 370 | 371 | /** 372 | * Set method's namespace 373 | * 374 | * @param string $namespace 375 | * @throws Exception\InvalidArgumentException 376 | * @return void 377 | */ 378 | public function setNamespace($namespace) 379 | { 380 | if (empty($namespace)) { 381 | $this->namespace = ''; 382 | return; 383 | } 384 | 385 | if (! is_string($namespace) || ! preg_match('/[a-z0-9_\.]+/i', $namespace)) { 386 | throw new Exception\InvalidArgumentException('Invalid namespace'); 387 | } 388 | 389 | $this->namespace = $namespace; 390 | } 391 | 392 | /** 393 | * Return method's namespace 394 | * 395 | * @return string 396 | */ 397 | public function getNamespace() 398 | { 399 | return $this->namespace; 400 | } 401 | 402 | /** 403 | * Set the description 404 | * 405 | * @param string $string 406 | * @throws Exception\InvalidArgumentException 407 | * @return void 408 | */ 409 | public function setDescription($string) 410 | { 411 | if (! is_string($string)) { 412 | throw new Exception\InvalidArgumentException('Invalid description'); 413 | } 414 | 415 | $this->description = $string; 416 | } 417 | 418 | /** 419 | * Retrieve the description 420 | * 421 | * @return string 422 | */ 423 | public function getDescription() 424 | { 425 | return $this->description; 426 | } 427 | 428 | /** 429 | * Retrieve all prototypes as array of 430 | * {@link \Zend\Server\Reflection\Prototype}s 431 | * 432 | * @return Prototype[] 433 | */ 434 | public function getPrototypes() 435 | { 436 | return $this->prototypes; 437 | } 438 | 439 | /** 440 | * Retrieve additional invocation arguments 441 | * 442 | * @return array 443 | */ 444 | public function getInvokeArguments() 445 | { 446 | return $this->argv; 447 | } 448 | 449 | /** 450 | * @return string[] 451 | */ 452 | public function __sleep() 453 | { 454 | $serializable = []; 455 | foreach ($this as $name => $value) { 456 | if ($value instanceof PhpReflectionFunction 457 | || $value instanceof PhpReflectionMethod 458 | ) { 459 | continue; 460 | } 461 | 462 | $serializable[] = $name; 463 | } 464 | 465 | return $serializable; 466 | } 467 | 468 | /** 469 | * Wakeup from serialization 470 | * 471 | * Reflection needs explicit instantiation to work correctly. Re-instantiate 472 | * reflection object on wakeup. 473 | * 474 | * @return void 475 | */ 476 | public function __wakeup() 477 | { 478 | if ($this->reflection instanceof PhpReflectionMethod) { 479 | $class = new PhpReflectionClass($this->class); 480 | $this->reflection = new PhpReflectionMethod($class->newInstance(), $this->name); 481 | } else { 482 | $this->reflection = new PhpReflectionFunction($this->name); 483 | } 484 | } 485 | } 486 | -------------------------------------------------------------------------------- /src/Reflection/Exception/BadMethodCallException.php: -------------------------------------------------------------------------------- 1 | value = $value; 43 | if (null !== $parent) { 44 | $this->setParent($parent, true); 45 | } 46 | 47 | return $this; 48 | } 49 | 50 | /** 51 | * Set parent node 52 | * 53 | * @param \Zend\Server\Reflection\Node $node 54 | * @param bool $new Whether or not the child node is newly created 55 | * and should always be attached 56 | * @return void 57 | */ 58 | public function setParent(Node $node, $new = false) 59 | { 60 | $this->parent = $node; 61 | 62 | if ($new) { 63 | $node->attachChild($this); 64 | return; 65 | } 66 | } 67 | 68 | /** 69 | * Create and attach a new child node 70 | * 71 | * @param mixed $value 72 | * @access public 73 | * @return \Zend\Server\Reflection\Node New child node 74 | */ 75 | public function createChild($value) 76 | { 77 | $child = new static($value, $this); 78 | 79 | return $child; 80 | } 81 | 82 | /** 83 | * Attach a child node 84 | * 85 | * @param \Zend\Server\Reflection\Node $node 86 | * @return void 87 | */ 88 | public function attachChild(Node $node) 89 | { 90 | $this->children[] = $node; 91 | 92 | if ($node->getParent() !== $this) { 93 | $node->setParent($this); 94 | } 95 | } 96 | 97 | /** 98 | * Return an array of all child nodes 99 | * 100 | * @return array 101 | */ 102 | public function getChildren() 103 | { 104 | return $this->children; 105 | } 106 | 107 | /** 108 | * Does this node have children? 109 | * 110 | * @return bool 111 | */ 112 | public function hasChildren() 113 | { 114 | return count($this->children) > 0; 115 | } 116 | 117 | /** 118 | * Return the parent node 119 | * 120 | * @return null|\Zend\Server\Reflection\Node 121 | */ 122 | public function getParent() 123 | { 124 | return $this->parent; 125 | } 126 | 127 | /** 128 | * Return the node's current value 129 | * 130 | * @return mixed 131 | */ 132 | public function getValue() 133 | { 134 | return $this->value; 135 | } 136 | 137 | /** 138 | * Set the node value 139 | * 140 | * @param mixed $value 141 | * @return void 142 | */ 143 | public function setValue($value) 144 | { 145 | $this->value = $value; 146 | } 147 | 148 | /** 149 | * Retrieve the bottommost nodes of this node's tree 150 | * 151 | * Retrieves the bottommost nodes of the tree by recursively calling 152 | * getEndPoints() on all children. If a child is null, it returns the parent 153 | * as an end point. 154 | * 155 | * @return array 156 | */ 157 | public function getEndPoints() 158 | { 159 | $endPoints = []; 160 | if (! $this->hasChildren()) { 161 | return $endPoints; 162 | } 163 | 164 | foreach ($this->children as $child) { 165 | $value = $child->getValue(); 166 | 167 | if (null === $value) { 168 | $endPoints[] = $this; 169 | } elseif ((null !== $value) && $child->hasChildren()) { 170 | $childEndPoints = $child->getEndPoints(); 171 | if (! empty($childEndPoints)) { 172 | $endPoints = array_merge($endPoints, $childEndPoints); 173 | } 174 | } elseif ((null !== $value) && ! $child->hasChildren()) { 175 | $endPoints[] = $child; 176 | } 177 | } 178 | 179 | return $endPoints; 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/Reflection/Prototype.php: -------------------------------------------------------------------------------- 1 | return = $return; 30 | 31 | foreach ($params as $param) { 32 | if (! $param instanceof ReflectionParameter) { 33 | throw new Exception\InvalidArgumentException('One or more params are invalid'); 34 | } 35 | } 36 | 37 | $this->params = $params; 38 | } 39 | 40 | /** 41 | * Retrieve return type 42 | * 43 | * @return string 44 | */ 45 | public function getReturnType() 46 | { 47 | return $this->return->getType(); 48 | } 49 | 50 | /** 51 | * Retrieve the return value object 52 | * 53 | * @return \Zend\Server\Reflection\ReflectionReturnValue 54 | */ 55 | public function getReturnValue() 56 | { 57 | return $this->return; 58 | } 59 | 60 | /** 61 | * Retrieve method parameters 62 | * 63 | * @return ReflectionParameter[] Array of {@link \Zend\Server\Reflection\ReflectionParameter}s 64 | */ 65 | public function getParameters() 66 | { 67 | return $this->params; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Reflection/ReflectionClass.php: -------------------------------------------------------------------------------- 1 | reflection = $reflection; 64 | $this->name = $reflection->getName(); 65 | $this->setNamespace($namespace); 66 | 67 | foreach ($reflection->getMethods() as $method) { 68 | // Don't aggregate magic methods 69 | if ('__' == substr($method->getName(), 0, 2)) { 70 | continue; 71 | } 72 | 73 | if ($method->isPublic()) { 74 | // Get signatures and description 75 | $this->methods[] = new ReflectionMethod($this, $method, $this->getNamespace(), $argv); 76 | } 77 | } 78 | } 79 | 80 | /** 81 | * Proxy reflection calls 82 | * 83 | * @param string $method 84 | * @param array $args 85 | * @throws Exception\BadMethodCallException 86 | * @return mixed 87 | */ 88 | public function __call($method, $args) 89 | { 90 | if (method_exists($this->reflection, $method)) { 91 | return call_user_func_array([$this->reflection, $method], $args); 92 | } 93 | 94 | throw new Exception\BadMethodCallException('Invalid reflection method'); 95 | } 96 | 97 | /** 98 | * Retrieve configuration parameters 99 | * 100 | * Values are retrieved by key from {@link $config}. Returns null if no 101 | * value found. 102 | * 103 | * @param string $key 104 | * @return mixed 105 | */ 106 | public function __get($key) 107 | { 108 | if (isset($this->config[$key])) { 109 | return $this->config[$key]; 110 | } 111 | 112 | return; 113 | } 114 | 115 | /** 116 | * Set configuration parameters 117 | * 118 | * Values are stored by $key in {@link $config}. 119 | * 120 | * @param string $key 121 | * @param mixed $value 122 | * @return void 123 | */ 124 | public function __set($key, $value) 125 | { 126 | $this->config[$key] = $value; 127 | } 128 | 129 | /** 130 | * Return array of dispatchable {@link \Zend\Server\Reflection\ReflectionMethod}s. 131 | * 132 | * @access public 133 | * @return array 134 | */ 135 | public function getMethods() 136 | { 137 | return $this->methods; 138 | } 139 | 140 | /** 141 | * Get namespace for this class 142 | * 143 | * @return string 144 | */ 145 | public function getNamespace() 146 | { 147 | return $this->namespace; 148 | } 149 | 150 | /** 151 | * Set namespace for this class 152 | * 153 | * @param string $namespace 154 | * @throws Exception\InvalidArgumentException 155 | * @return void 156 | */ 157 | public function setNamespace($namespace) 158 | { 159 | if (empty($namespace)) { 160 | $this->namespace = ''; 161 | return; 162 | } 163 | 164 | if (! is_string($namespace) || ! preg_match('/[a-z0-9_\.]+/i', $namespace)) { 165 | throw new Exception\InvalidArgumentException('Invalid namespace'); 166 | } 167 | 168 | $this->namespace = $namespace; 169 | } 170 | 171 | /** 172 | * Wakeup from serialization 173 | * 174 | * Reflection needs explicit instantiation to work correctly. Re-instantiate 175 | * reflection object on wakeup. 176 | * 177 | * @return void 178 | */ 179 | public function __wakeup() 180 | { 181 | $this->reflection = new PhpReflectionClass($this->name); 182 | } 183 | 184 | /** 185 | * @return string[] 186 | */ 187 | public function __sleep() 188 | { 189 | return ['config', 'methods', 'namespace', 'name']; 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/Reflection/ReflectionFunction.php: -------------------------------------------------------------------------------- 1 | classReflection = $class; 43 | $this->reflection = $r; 44 | 45 | $classNamespace = $class->getNamespace(); 46 | 47 | // Determine namespace 48 | if (! empty($namespace)) { 49 | $this->setNamespace($namespace); 50 | } elseif (! empty($classNamespace)) { 51 | $this->setNamespace($classNamespace); 52 | } 53 | 54 | // Determine arguments 55 | if (is_array($argv)) { 56 | $this->argv = $argv; 57 | } 58 | 59 | // If method call, need to store some info on the class 60 | $this->class = $class->getName(); 61 | $this->name = $r->getName(); 62 | 63 | // Perform some introspection 64 | $this->reflect(); 65 | } 66 | 67 | /** 68 | * Return the reflection for the class that defines this method 69 | * 70 | * @return \Zend\Server\Reflection\ReflectionClass 71 | */ 72 | public function getDeclaringClass() 73 | { 74 | return $this->classReflection; 75 | } 76 | 77 | /** 78 | * Wakeup from serialization 79 | * 80 | * Reflection needs explicit instantiation to work correctly. Re-instantiate 81 | * reflection object on wakeup. 82 | * 83 | * @return void 84 | */ 85 | public function __wakeup() 86 | { 87 | $this->classReflection = new ReflectionClass( 88 | new \ReflectionClass($this->class), 89 | $this->getNamespace(), 90 | $this->getInvokeArguments() 91 | ); 92 | $this->reflection = new \ReflectionMethod($this->classReflection->getName(), $this->name); 93 | } 94 | 95 | /** 96 | * {@inheritdoc} 97 | */ 98 | protected function reflect() 99 | { 100 | $docComment = $this->reflection->getDocComment(); 101 | if (strpos($docComment, self::INHERIT_TAG) !== false) { 102 | $this->docComment = $this->fetchRecursiveDocComment(); 103 | } 104 | 105 | parent::reflect(); 106 | } 107 | 108 | /** 109 | * Fetch all doc comments for inherit values 110 | * 111 | * @return string 112 | */ 113 | private function fetchRecursiveDocComment() 114 | { 115 | $currentMethodName = $this->reflection->getName(); 116 | $docCommentList[] = $this->reflection->getDocComment(); 117 | 118 | // fetch all doc blocks for method from parent classes 119 | $docCommentFetched = $this->fetchRecursiveDocBlockFromParent($this->classReflection, $currentMethodName); 120 | if ($docCommentFetched) { 121 | $docCommentList = array_merge($docCommentList, $docCommentFetched); 122 | } 123 | 124 | 125 | // fetch doc blocks from interfaces 126 | $interfaceReflectionList = $this->classReflection->getInterfaces(); 127 | foreach ($interfaceReflectionList as $interfaceReflection) { 128 | if (! $interfaceReflection->hasMethod($currentMethodName)) { 129 | continue; 130 | } 131 | 132 | $docCommentList[] = $interfaceReflection->getMethod($currentMethodName)->getDocComment(); 133 | } 134 | 135 | $normalizedDocCommentList = array_map( 136 | function ($docComment) { 137 | $docComment = str_replace('/**', '', $docComment); 138 | $docComment = str_replace('*/', '', $docComment); 139 | 140 | return $docComment; 141 | }, 142 | $docCommentList 143 | ); 144 | 145 | $docComment = '/**' . implode(PHP_EOL, $normalizedDocCommentList) . '*/'; 146 | 147 | return $docComment; 148 | } 149 | 150 | /** 151 | * Fetch recursive doc blocks from parent classes 152 | * 153 | * @param \ReflectionClass $reflectionClass 154 | * @param string $methodName 155 | * 156 | * @return array|void 157 | */ 158 | private function fetchRecursiveDocBlockFromParent($reflectionClass, $methodName) 159 | { 160 | $docComment = []; 161 | $parentReflectionClass = $reflectionClass->getParentClass(); 162 | if (! $parentReflectionClass) { 163 | return; 164 | } 165 | 166 | if (! $parentReflectionClass->hasMethod($methodName)) { 167 | return; 168 | } 169 | 170 | $methodReflection = $parentReflectionClass->getMethod($methodName); 171 | $docCommentLast = $methodReflection->getDocComment(); 172 | $docComment[] = $docCommentLast; 173 | if ($this->isInherit($docCommentLast)) { 174 | if ($docCommentFetched = $this->fetchRecursiveDocBlockFromParent($parentReflectionClass, $methodName)) { 175 | $docComment = array_merge($docComment, $docCommentFetched); 176 | } 177 | } 178 | 179 | return $docComment; 180 | } 181 | 182 | /** 183 | * Return true if doc block inherit from parent or interface 184 | * 185 | * @param string $docComment 186 | * 187 | * @return bool 188 | */ 189 | private function isInherit($docComment) 190 | { 191 | $isInherit = strpos($docComment, self::INHERIT_TAG) !== false; 192 | 193 | return $isInherit; 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/Reflection/ReflectionParameter.php: -------------------------------------------------------------------------------- 1 | reflection = $r; 62 | 63 | // Store parameters needed for (un)serialization 64 | $this->name = $r->getName(); 65 | $this->functionName = $r->getDeclaringClass() 66 | ? [$r->getDeclaringClass()->getName(), $r->getDeclaringFunction()->getName()] 67 | : $r->getDeclaringFunction()->getName(); 68 | 69 | $this->setType($type); 70 | $this->setDescription($description); 71 | } 72 | 73 | /** 74 | * Proxy reflection calls 75 | * 76 | * @param string $method 77 | * @param array $args 78 | * @throws Exception\BadMethodCallException 79 | * @return mixed 80 | */ 81 | public function __call($method, $args) 82 | { 83 | if (method_exists($this->reflection, $method)) { 84 | return call_user_func_array([$this->reflection, $method], $args); 85 | } 86 | 87 | throw new Exception\BadMethodCallException('Invalid reflection method'); 88 | } 89 | 90 | /** 91 | * Retrieve parameter type 92 | * 93 | * @return string 94 | */ 95 | public function getType() 96 | { 97 | return $this->type; 98 | } 99 | 100 | /** 101 | * Set parameter type 102 | * 103 | * @param string|null $type 104 | * @throws Exception\InvalidArgumentException 105 | * @return void 106 | */ 107 | public function setType($type) 108 | { 109 | if (! is_string($type) && (null !== $type)) { 110 | throw new Exception\InvalidArgumentException('Invalid parameter type'); 111 | } 112 | 113 | $this->type = $type; 114 | } 115 | 116 | /** 117 | * Retrieve parameter description 118 | * 119 | * @return string 120 | */ 121 | public function getDescription() 122 | { 123 | return $this->description; 124 | } 125 | 126 | /** 127 | * Set parameter description 128 | * 129 | * @param string|null $description 130 | * @throws Exception\InvalidArgumentException 131 | * @return void 132 | */ 133 | public function setDescription($description) 134 | { 135 | if (! is_string($description) && (null !== $description)) { 136 | throw new Exception\InvalidArgumentException('Invalid parameter description'); 137 | } 138 | 139 | $this->description = $description; 140 | } 141 | 142 | /** 143 | * Set parameter position 144 | * 145 | * @param int $index 146 | * @return void 147 | */ 148 | public function setPosition($index) 149 | { 150 | $this->position = (int) $index; 151 | } 152 | 153 | /** 154 | * Return parameter position 155 | * 156 | * @return int 157 | */ 158 | public function getPosition() 159 | { 160 | return $this->position; 161 | } 162 | 163 | /** 164 | * @return string[] 165 | */ 166 | public function __sleep() 167 | { 168 | return ['position', 'type', 'description', 'name', 'functionName']; 169 | } 170 | 171 | public function __wakeup() 172 | { 173 | $this->reflection = new \ReflectionParameter($this->functionName, $this->name); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/Reflection/ReflectionReturnValue.php: -------------------------------------------------------------------------------- 1 | setType($type); 38 | $this->setDescription($description); 39 | } 40 | 41 | /** 42 | * Retrieve parameter type 43 | * 44 | * @return string 45 | */ 46 | public function getType() 47 | { 48 | return $this->type; 49 | } 50 | 51 | /** 52 | * Set parameter type 53 | * 54 | * @param string|null $type 55 | * @throws Exception\InvalidArgumentException 56 | * @return void 57 | */ 58 | public function setType($type) 59 | { 60 | if (! is_string($type) && (null !== $type)) { 61 | throw new Exception\InvalidArgumentException('Invalid parameter type'); 62 | } 63 | 64 | $this->type = $type; 65 | } 66 | 67 | /** 68 | * Retrieve parameter description 69 | * 70 | * @return string 71 | */ 72 | public function getDescription() 73 | { 74 | return $this->description; 75 | } 76 | 77 | /** 78 | * Set parameter description 79 | * 80 | * @param string|null $description 81 | * @throws Exception\InvalidArgumentException 82 | * @return void 83 | */ 84 | public function setDescription($description) 85 | { 86 | if (! is_string($description) && (null !== $description)) { 87 | throw new Exception\InvalidArgumentException('Invalid parameter description'); 88 | } 89 | 90 | $this->description = $description; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/Server.php: -------------------------------------------------------------------------------- 1 |