PageRenderTime 49ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/Front_End/vendor/phpdocumentor/reflection-docblock/src/DocBlock/StandardTagFactory.php

https://gitlab.com/Sigpot/AirSpot
PHP | 314 lines | 145 code | 34 blank | 135 comment | 9 complexity | ca85676e19ceec84553b44498f92e1ff MD5 | raw file
  1. <?php
  2. /**
  3. * This file is part of phpDocumentor.
  4. *
  5. * For the full copyright and license information, please view the LICENSE
  6. * file that was distributed with this source code.
  7. *
  8. * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
  9. * @license http://www.opensource.org/licenses/mit-license.php MIT
  10. * @link http://phpdoc.org
  11. */
  12. namespace phpDocumentor\Reflection\DocBlock;
  13. use phpDocumentor\Reflection\DocBlock\Tags\Factory\StaticMethod;
  14. use phpDocumentor\Reflection\DocBlock\Tags\Generic;
  15. use phpDocumentor\Reflection\FqsenResolver;
  16. use phpDocumentor\Reflection\Types\Context as TypeContext;
  17. use Webmozart\Assert\Assert;
  18. /**
  19. * Creates a Tag object given the contents of a tag.
  20. *
  21. * This Factory is capable of determining the appropriate class for a tag and instantiate it using its `create`
  22. * factory method. The `create` factory method of a Tag can have a variable number of arguments; this way you can
  23. * pass the dependencies that you need to construct a tag object.
  24. *
  25. * > Important: each parameter in addition to the body variable for the `create` method must default to null, otherwise
  26. * > it violates the constraint with the interface; it is recommended to use the {@see Assert::notNull()} method to
  27. * > verify that a dependency is actually passed.
  28. *
  29. * This Factory also features a Service Locator component that is used to pass the right dependencies to the
  30. * `create` method of a tag; each dependency should be registered as a service or as a parameter.
  31. *
  32. * When you want to use a Tag of your own with custom handling you need to call the `registerTagHandler` method, pass
  33. * the name of the tag and a Fully Qualified Class Name pointing to a class that implements the Tag interface.
  34. */
  35. final class StandardTagFactory implements TagFactory
  36. {
  37. /** PCRE regular expression matching a tag name. */
  38. const REGEX_TAGNAME = '[\w\-\_\\\\]+';
  39. /**
  40. * @var string[] An array with a tag as a key, and an FQCN to a class that handles it as an array value.
  41. */
  42. private $tagHandlerMappings = [
  43. 'author' => '\phpDocumentor\Reflection\DocBlock\Tags\Author',
  44. 'covers' => '\phpDocumentor\Reflection\DocBlock\Tags\Covers',
  45. 'deprecated' => '\phpDocumentor\Reflection\DocBlock\Tags\Deprecated',
  46. // 'example' => '\phpDocumentor\Reflection\DocBlock\Tags\Example',
  47. 'link' => '\phpDocumentor\Reflection\DocBlock\Tags\Link',
  48. 'method' => '\phpDocumentor\Reflection\DocBlock\Tags\Method',
  49. 'param' => '\phpDocumentor\Reflection\DocBlock\Tags\Param',
  50. 'property-read' => '\phpDocumentor\Reflection\DocBlock\Tags\PropertyRead',
  51. 'property' => '\phpDocumentor\Reflection\DocBlock\Tags\Property',
  52. 'property-write' => '\phpDocumentor\Reflection\DocBlock\Tags\PropertyWrite',
  53. 'return' => '\phpDocumentor\Reflection\DocBlock\Tags\Return_',
  54. 'see' => '\phpDocumentor\Reflection\DocBlock\Tags\See',
  55. 'since' => '\phpDocumentor\Reflection\DocBlock\Tags\Since',
  56. 'source' => '\phpDocumentor\Reflection\DocBlock\Tags\Source',
  57. 'throw' => '\phpDocumentor\Reflection\DocBlock\Tags\Throws',
  58. 'throws' => '\phpDocumentor\Reflection\DocBlock\Tags\Throws',
  59. 'uses' => '\phpDocumentor\Reflection\DocBlock\Tags\Uses',
  60. 'var' => '\phpDocumentor\Reflection\DocBlock\Tags\Var_',
  61. 'version' => '\phpDocumentor\Reflection\DocBlock\Tags\Version'
  62. ];
  63. /**
  64. * @var \ReflectionParameter[][] a lazy-loading cache containing parameters for each tagHandler that has been used.
  65. */
  66. private $tagHandlerParameterCache = [];
  67. /**
  68. * @var FqsenResolver
  69. */
  70. private $fqsenResolver;
  71. /**
  72. * @var mixed[] an array representing a simple Service Locator where we can store parameters and
  73. * services that can be inserted into the Factory Methods of Tag Handlers.
  74. */
  75. private $serviceLocator = [];
  76. /**
  77. * Initialize this tag factory with the means to resolve an FQSEN and optionally a list of tag handlers.
  78. *
  79. * If no tag handlers are provided than the default list in the {@see self::$tagHandlerMappings} property
  80. * is used.
  81. *
  82. * @param FqsenResolver $fqsenResolver
  83. * @param string[] $tagHandlers
  84. *
  85. * @see self::registerTagHandler() to add a new tag handler to the existing default list.
  86. */
  87. public function __construct(FqsenResolver $fqsenResolver, array $tagHandlers = null)
  88. {
  89. $this->fqsenResolver = $fqsenResolver;
  90. if ($tagHandlers !== null) {
  91. $this->tagHandlerMappings = $tagHandlers;
  92. }
  93. $this->addService($fqsenResolver, FqsenResolver::class);
  94. }
  95. /**
  96. * {@inheritDoc}
  97. */
  98. public function create($tagLine, TypeContext $context = null)
  99. {
  100. if (! $context) {
  101. $context = new TypeContext('');
  102. }
  103. list($tagName, $tagBody) = $this->extractTagParts($tagLine);
  104. return $this->createTag($tagBody, $tagName, $context);
  105. }
  106. /**
  107. * {@inheritDoc}
  108. */
  109. public function addParameter($name, $value)
  110. {
  111. $this->serviceLocator[$name] = $value;
  112. }
  113. /**
  114. * {@inheritDoc}
  115. */
  116. public function addService($service, $alias = null)
  117. {
  118. $this->serviceLocator[$alias ?: get_class($service)] = $service;
  119. }
  120. /**
  121. * {@inheritDoc}
  122. */
  123. public function registerTagHandler($tagName, $handler)
  124. {
  125. Assert::stringNotEmpty($tagName);
  126. Assert::stringNotEmpty($handler);
  127. Assert::classExists($handler);
  128. Assert::implementsInterface($handler, StaticMethod::class);
  129. if (strpos($tagName, '\\') && $tagName[0] !== '\\') {
  130. throw new \InvalidArgumentException(
  131. 'A namespaced tag must have a leading backslash as it must be fully qualified'
  132. );
  133. }
  134. $this->tagHandlerMappings[$tagName] = $handler;
  135. }
  136. /**
  137. * Extracts all components for a tag.
  138. *
  139. * @param string $tagLine
  140. *
  141. * @return string[]
  142. */
  143. private function extractTagParts($tagLine)
  144. {
  145. $matches = array();
  146. if (! preg_match('/^@(' . self::REGEX_TAGNAME . ')(?:\s*([^\s].*)|$)?/us', $tagLine, $matches)) {
  147. throw new \InvalidArgumentException(
  148. 'The tag "' . $tagLine . '" does not seem to be wellformed, please check it for errors'
  149. );
  150. }
  151. if (count($matches) < 3) {
  152. $matches[] = '';
  153. }
  154. return array_slice($matches, 1);
  155. }
  156. /**
  157. * Creates a new tag object with the given name and body or returns null if the tag name was recognized but the
  158. * body was invalid.
  159. *
  160. * @param string $body
  161. * @param string $name
  162. * @param TypeContext $context
  163. *
  164. * @return Tag|null
  165. */
  166. private function createTag($body, $name, TypeContext $context)
  167. {
  168. $handlerClassName = $this->findHandlerClassName($name, $context);
  169. $arguments = $this->getArgumentsForParametersFromWiring(
  170. $this->fetchParametersForHandlerFactoryMethod($handlerClassName),
  171. $this->getServiceLocatorWithDynamicParameters($context, $name, $body)
  172. )
  173. ;
  174. return call_user_func_array([$handlerClassName, 'create'], $arguments);
  175. }
  176. /**
  177. * Determines the Fully Qualified Class Name of the Factory or Tag (containing a Factory Method `create`).
  178. *
  179. * @param string $tagName
  180. * @param TypeContext $context
  181. *
  182. * @return string
  183. */
  184. private function findHandlerClassName($tagName, TypeContext $context)
  185. {
  186. $handlerClassName = Generic::class;
  187. if (isset($this->tagHandlerMappings[$tagName])) {
  188. $handlerClassName = $this->tagHandlerMappings[$tagName];
  189. } elseif ($this->isAnnotation($tagName)) {
  190. // TODO: Annotation support is planned for a later stage and as such is disabled for now
  191. // $tagName = (string)$this->fqsenResolver->resolve($tagName, $context);
  192. // if (isset($this->annotationMappings[$tagName])) {
  193. // $handlerClassName = $this->annotationMappings[$tagName];
  194. // }
  195. }
  196. return $handlerClassName;
  197. }
  198. /**
  199. * Retrieves the arguments that need to be passed to the Factory Method with the given Parameters.
  200. *
  201. * @param \ReflectionParameter[] $parameters
  202. * @param mixed[] $locator
  203. *
  204. * @return mixed[] A series of values that can be passed to the Factory Method of the tag whose parameters
  205. * is provided with this method.
  206. */
  207. private function getArgumentsForParametersFromWiring($parameters, $locator)
  208. {
  209. $arguments = [];
  210. foreach ($parameters as $index => $parameter) {
  211. $typeHint = $parameter->getClass() ? $parameter->getClass()->getName() : null;
  212. if (isset($locator[$typeHint])) {
  213. $arguments[] = $locator[$typeHint];
  214. continue;
  215. }
  216. $parameterName = $parameter->getName();
  217. if (isset($locator[$parameterName])) {
  218. $arguments[] = $locator[$parameterName];
  219. continue;
  220. }
  221. $arguments[] = null;
  222. }
  223. return $arguments;
  224. }
  225. /**
  226. * Retrieves a series of ReflectionParameter objects for the static 'create' method of the given
  227. * tag handler class name.
  228. *
  229. * @param string $handlerClassName
  230. *
  231. * @return \ReflectionParameter[]
  232. */
  233. private function fetchParametersForHandlerFactoryMethod($handlerClassName)
  234. {
  235. if (! isset($this->tagHandlerParameterCache[$handlerClassName])) {
  236. $methodReflection = new \ReflectionMethod($handlerClassName, 'create');
  237. $this->tagHandlerParameterCache[$handlerClassName] = $methodReflection->getParameters();
  238. }
  239. return $this->tagHandlerParameterCache[$handlerClassName];
  240. }
  241. /**
  242. * Returns a copy of this class' Service Locator with added dynamic parameters, such as the tag's name, body and
  243. * Context.
  244. *
  245. * @param TypeContext $context The Context (namespace and aliasses) that may be passed and is used to resolve FQSENs.
  246. * @param string $tagName The name of the tag that may be passed onto the factory method of the Tag class.
  247. * @param string $tagBody The body of the tag that may be passed onto the factory method of the Tag class.
  248. *
  249. * @return mixed[]
  250. */
  251. private function getServiceLocatorWithDynamicParameters(TypeContext $context, $tagName, $tagBody)
  252. {
  253. $locator = array_merge(
  254. $this->serviceLocator,
  255. [
  256. 'name' => $tagName,
  257. 'body' => $tagBody,
  258. TypeContext::class => $context
  259. ]
  260. );
  261. return $locator;
  262. }
  263. /**
  264. * Returns whether the given tag belongs to an annotation.
  265. *
  266. * @param string $tagContent
  267. *
  268. * @todo this method should be populated once we implement Annotation notation support.
  269. *
  270. * @return bool
  271. */
  272. private function isAnnotation($tagContent)
  273. {
  274. // 1. Contains a namespace separator
  275. // 2. Contains parenthesis
  276. // 3. Is present in a list of known annotations (make the algorithm smart by first checking is the last part
  277. // of the annotation class name matches the found tag name
  278. return false;
  279. }
  280. }