Example #1
0
 /**
  * Tries to parse a definition of a class/method/property/constant/function and returns the appropriate instance if successful.
  *
  * @param string $definition Definition
  * @param \ApiGen\ReflectionElement|\ApiGen\ReflectionParameter $context Link context
  * @param string $expectedName Expected element name
  * @return \ApiGen\Reflection\ReflectionElement|null
  */
 public function resolveElement($definition, $context, &$expectedName = null)
 {
     // No simple type resolving
     static $types = array('boolean' => 1, 'integer' => 1, 'float' => 1, 'string' => 1, 'array' => 1, 'object' => 1, 'resource' => 1, 'callback' => 1, 'callable' => 1, 'null' => 1, 'false' => 1, 'true' => 1, 'mixed' => 1);
     if (empty($definition) || isset($types[$definition])) {
         return null;
     }
     $originalContext = $context;
     if ($context instanceof Reflection\ReflectionParameter && null === $context->getDeclaringClassName()) {
         // Parameter of function in namespace or global space
         $context = $this->getFunction($context->getDeclaringFunctionName());
     } elseif ($context instanceof Reflection\ReflectionMethod || $context instanceof Reflection\ReflectionParameter || $context instanceof Reflection\ReflectionConstant && null !== $context->getDeclaringClassName() || $context instanceof Reflection\ReflectionProperty) {
         // Member of a class
         $context = $this->getClass($context->getDeclaringClassName());
     }
     if (null === $context) {
         return null;
     }
     // self, $this references
     if ('self' === $definition || '$this' === $definition) {
         return $context instanceof ReflectionClass ? $context : null;
     }
     $definitionBase = substr($definition, 0, strcspn($definition, '\\:'));
     $namespaceAliases = $context->getNamespaceAliases();
     if (!empty($definitionBase) && isset($namespaceAliases[$definitionBase]) && $definition !== ($className = \TokenReflection\Resolver::resolveClassFQN($definition, $namespaceAliases, $context->getNamespaceName()))) {
         // Aliased class
         $expectedName = $className;
         if (false === strpos($className, ':')) {
             return $this->getClass($className, $context->getNamespaceName());
         } else {
             $definition = $className;
         }
     } elseif ($class = $this->getClass($definition, $context->getNamespaceName())) {
         // Class
         return $class;
     } elseif ($constant = $this->getConstant($definition, $context->getNamespaceName())) {
         // Constant
         return $constant;
     } elseif (($function = $this->getFunction($definition, $context->getNamespaceName())) || '()' === substr($definition, -2) && ($function = $this->getFunction(substr($definition, 0, -2), $context->getNamespaceName()))) {
         // Function
         return $function;
     }
     if (($pos = strpos($definition, '::')) || ($pos = strpos($definition, '->'))) {
         // Class::something or Class->something
         if (0 === strpos($definition, 'parent::') && ($parentClassName = $context->getParentClassName())) {
             $context = $this->getClass($parentClassName);
         } elseif (0 !== strpos($definition, 'self::')) {
             $class = $this->getClass(substr($definition, 0, $pos), $context->getNamespaceName());
             if (null === $class) {
                 $class = $this->getClass(\TokenReflection\Resolver::resolveClassFQN(substr($definition, 0, $pos), $context->getNamespaceAliases(), $context->getNamespaceName()));
             }
             $context = $class;
         }
         $definition = substr($definition, $pos + 2);
     } elseif ($originalContext instanceof Reflection\ReflectionParameter) {
         return null;
     }
     // No usable context
     if (null === $context || $context instanceof Reflection\ReflectionConstant || $context instanceof Reflection\ReflectionFunction) {
         return null;
     }
     if ($context->hasProperty($definition)) {
         // Class property
         return $context->getProperty($definition);
     } elseif ('$' === $definition[0] && $context->hasProperty(substr($definition, 1))) {
         // Class $property
         return $context->getProperty(substr($definition, 1));
     } elseif ($context->hasMethod($definition)) {
         // Class method
         return $context->getMethod($definition);
     } elseif ('()' === substr($definition, -2) && $context->hasMethod(substr($definition, 0, -2))) {
         // Class method()
         return $context->getMethod(substr($definition, 0, -2));
     } elseif ($context->hasConstant($definition)) {
         // Class constant
         return $context->getConstant($definition);
     }
     return null;
 }
Example #2
0
 /**
  * Parses child reflection objects from the token stream.
  *
  * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream
  * @param \TokenReflection\IReflection $parent Parent reflection object
  * @return \TokenReflection\ReflectionClass
  * @throws \TokenReflection\Exception\ParseException If a parse error was detected.
  */
 protected function parseChildren(Stream $tokenStream, IReflection $parent)
 {
     while (true) {
         switch ($type = $tokenStream->getType()) {
             case null:
                 break 2;
             case T_COMMENT:
             case T_DOC_COMMENT:
                 $docblock = $tokenStream->getTokenValue();
                 if (preg_match('~^' . preg_quote(self::DOCBLOCK_TEMPLATE_START, '~') . '~', $docblock)) {
                     array_unshift($this->docblockTemplates, new ReflectionAnnotation($this, $docblock));
                 } elseif (self::DOCBLOCK_TEMPLATE_END === $docblock) {
                     array_shift($this->docblockTemplates);
                 }
                 $tokenStream->next();
                 break;
             case '}':
                 break 2;
             case T_PUBLIC:
             case T_PRIVATE:
             case T_PROTECTED:
             case T_STATIC:
             case T_VAR:
             case T_VARIABLE:
                 static $searching = array(T_VARIABLE => true, T_FUNCTION => true);
                 if (T_VAR !== $tokenStream->getType()) {
                     $position = $tokenStream->key();
                     while (null !== ($type = $tokenStream->getType($position)) && !isset($searching[$type])) {
                         $position++;
                     }
                 }
                 if (T_VARIABLE === $type || T_VAR === $type) {
                     $property = new ReflectionProperty($tokenStream, $this->getBroker(), $this);
                     $this->properties[$property->getName()] = $property;
                     $tokenStream->next();
                     break;
                 }
                 // Break missing on purpose
             // Break missing on purpose
             case T_FINAL:
             case T_ABSTRACT:
             case T_FUNCTION:
                 $method = new ReflectionMethod($tokenStream, $this->getBroker(), $this);
                 $this->methods[$method->getName()] = $method;
                 $tokenStream->next();
                 break;
             case T_CONST:
                 $tokenStream->skipWhitespaces(true);
                 while ($tokenStream->is(T_STRING)) {
                     $constant = new ReflectionConstant($tokenStream, $this->getBroker(), $this);
                     $this->constants[$constant->getName()] = $constant;
                     if ($tokenStream->is(',')) {
                         $tokenStream->skipWhitespaces(true);
                     } else {
                         $tokenStream->next();
                     }
                 }
                 break;
             case T_USE:
                 $tokenStream->skipWhitespaces(true);
                 while (true) {
                     $traitName = '';
                     $type = $tokenStream->getType();
                     while (T_STRING === $type || T_NS_SEPARATOR === $type) {
                         $traitName .= $tokenStream->getTokenValue();
                         $type = $tokenStream->skipWhitespaces(true)->getType();
                     }
                     if ('' === trim($traitName, '\\')) {
                         throw new Exception\ParseException($this, $tokenStream, 'An empty trait name found.', Exception\ParseException::LOGICAL_ERROR);
                     }
                     $this->traits[] = Resolver::resolveClassFQN($traitName, $this->aliases, $this->namespaceName);
                     if (';' === $type) {
                         // End of "use"
                         $tokenStream->skipWhitespaces();
                         break;
                     } elseif (',' === $type) {
                         // Next trait name follows
                         $tokenStream->skipWhitespaces();
                         continue;
                     } elseif ('{' !== $type) {
                         // Unexpected token
                         throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found: "%s".', Exception\ParseException::UNEXPECTED_TOKEN);
                     }
                     // Aliases definition
                     $type = $tokenStream->skipWhitespaces(true)->getType();
                     while (true) {
                         if ('}' === $type) {
                             $tokenStream->skipWhitespaces();
                             break 2;
                         }
                         $leftSide = '';
                         $rightSide = array('', null);
                         $alias = true;
                         while (T_STRING === $type || T_NS_SEPARATOR === $type || T_DOUBLE_COLON === $type) {
                             $leftSide .= $tokenStream->getTokenValue();
                             $type = $tokenStream->skipWhitespaces(true)->getType();
                         }
                         if (T_INSTEADOF === $type) {
                             $alias = false;
                         } elseif (T_AS !== $type) {
                             throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN);
                         }
                         $type = $tokenStream->skipWhitespaces(true)->getType();
                         if (T_PUBLIC === $type || T_PROTECTED === $type || T_PRIVATE === $type) {
                             if (!$alias) {
                                 throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN);
                             }
                             switch ($type) {
                                 case T_PUBLIC:
                                     $type = InternalReflectionMethod::IS_PUBLIC;
                                     break;
                                 case T_PROTECTED:
                                     $type = InternalReflectionMethod::IS_PROTECTED;
                                     break;
                                 case T_PRIVATE:
                                     $type = InternalReflectionMethod::IS_PRIVATE;
                                     break;
                                 default:
                                     break;
                             }
                             $rightSide[1] = $type;
                             $type = $tokenStream->skipWhitespaces(true)->getType();
                         }
                         while (T_STRING === $type || T_NS_SEPARATOR === $type && !$alias) {
                             $rightSide[0] .= $tokenStream->getTokenValue();
                             $type = $tokenStream->skipWhitespaces(true)->getType();
                         }
                         if (empty($leftSide)) {
                             throw new Exception\ParseException($this, $tokenStream, 'An empty method name was found.', Exception\ParseException::LOGICAL_ERROR);
                         }
                         if ($alias) {
                             // Alias
                             if ($pos = strpos($leftSide, '::')) {
                                 $methodName = substr($leftSide, $pos + 2);
                                 $className = Resolver::resolveClassFQN(substr($leftSide, 0, $pos), $this->aliases, $this->namespaceName);
                                 $leftSide = $className . '::' . $methodName;
                                 $this->traitAliases[$rightSide[0]] = $leftSide;
                             } else {
                                 $this->traitAliases[$rightSide[0]] = '(null)::' . $leftSide;
                             }
                             $this->traitImports[$leftSide][] = $rightSide;
                         } else {
                             // Insteadof
                             if ($pos = strpos($leftSide, '::')) {
                                 $methodName = substr($leftSide, $pos + 2);
                             } else {
                                 throw new Exception\ParseException($this, $tokenStream, 'A T_DOUBLE_COLON has to be present when using T_INSTEADOF.', Exception\ParseException::UNEXPECTED_TOKEN);
                             }
                             $this->traitImports[Resolver::resolveClassFQN($rightSide[0], $this->aliases, $this->namespaceName) . '::' . $methodName][] = null;
                         }
                         if (',' === $type) {
                             $tokenStream->skipWhitespaces(true);
                             continue;
                         } elseif (';' !== $type) {
                             throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN);
                         }
                         $type = $tokenStream->skipWhitespaces()->getType();
                     }
                 }
                 break;
             default:
                 $tokenStream->next();
                 break;
         }
     }
     return $this;
 }
 /**
  * Returns static variables.
  *
  * @return array
  */
 public function getStaticVariables()
 {
     if (empty($this->staticVariables) && !empty($this->staticVariablesDefinition)) {
         foreach ($this->staticVariablesDefinition as $variableName => $variableDefinition) {
             $this->staticVariables[$variableName] = Resolver::getValueDefinition($variableDefinition, $this);
         }
     }
     return $this->staticVariables;
 }
 /**
  * Returns the required class name of the value.
  *
  * @return string|null
  * @throws \TokenReflection\Exception\RuntimeException If the type hint class FQN could not be determined.
  */
 public function getClassName()
 {
     if ($this->isArray() || $this->isCallable()) {
         return null;
     }
     if (null === $this->typeHint && null !== $this->originalTypeHint) {
         if (null !== $this->declaringClassName) {
             $parent = $this->getDeclaringClass();
             if (null === $parent) {
                 throw new Exception\RuntimeException('Could not load class reflection.', Exception\RuntimeException::DOES_NOT_EXIST, $this);
             }
         } else {
             $parent = $this->getDeclaringFunction();
             if (null === $parent || !$parent->isTokenized()) {
                 throw new Exception\RuntimeException('Could not load function reflection.', Exception\RuntimeException::DOES_NOT_EXIST, $this);
             }
         }
         $lTypeHint = strtolower($this->originalTypeHint);
         if ('parent' === $lTypeHint || 'self' === $lTypeHint) {
             if (null === $this->declaringClassName) {
                 throw new Exception\RuntimeException('Parameter type hint cannot be "self" nor "parent" when not a method.', Exception\RuntimeException::UNSUPPORTED, $this);
             }
             if ('parent' === $lTypeHint) {
                 if ($parent->isInterface() || null === $parent->getParentClassName()) {
                     throw new Exception\RuntimeException('Class has no parent.', Exception\RuntimeException::DOES_NOT_EXIST, $this);
                 }
                 $this->typeHint = $parent->getParentClassName();
             } else {
                 $this->typeHint = $this->declaringClassName;
             }
         } else {
             $this->typeHint = ltrim(Resolver::resolveClassFQN($this->originalTypeHint, $parent->getNamespaceAliases(), $parent->getNamespaceName()), '\\');
         }
     }
     return $this->typeHint;
 }
 /**
  * @param string $definition
  * @param int $pos
  * @param ElementReflectionInterface $reflectionElement
  * @return ClassReflectionInterface
  */
 private function resolveContextForSelfProperty($definition, $pos, ElementReflectionInterface $reflectionElement)
 {
     $class = $this->getClass(substr($definition, 0, $pos), $reflectionElement->getNamespaceName());
     if ($class === null) {
         $fqnName = Resolver::resolveClassFqn(substr($definition, 0, $pos), $reflectionElement->getNamespaceAliases(), $reflectionElement->getNamespaceName());
         $class = $this->getClass($fqnName);
     }
     return $class;
 }
 /**
  * Returns the part of the source code defining the property default value.
  *
  * @return string
  */
 public function getDefaultValueDefinition()
 {
     return is_array($this->defaultValueDefinition) ? Resolver::getSourceCode($this->defaultValueDefinition) : $this->defaultValueDefinition;
 }
Example #7
0
 /**
  * Processes a function/method and adds classes from annotations to the overall class array.
  *
  * @param array $declared Array of declared classes
  * @param array $allClasses Array with all classes parsed so far
  * @param \ApiGen\Reflection\ReflectionFunction|\TokenReflection\IReflectionFunctionBase $function Function/method reflection
  * @return array
  */
 private function processFunction(array $declared, array $allClasses, $function)
 {
     static $parsedAnnotations = array('param', 'return', 'throws');
     $annotations = $function->getAnnotations();
     foreach ($parsedAnnotations as $annotation) {
         if (!isset($annotations[$annotation])) {
             continue;
         }
         foreach ($annotations[$annotation] as $doc) {
             foreach (explode('|', preg_replace('~\\s.*~', '', $doc)) as $name) {
                 if ($name) {
                     $name = Resolver::resolveClassFQN(rtrim($name, '[]'), $function->getNamespaceAliases(), $function->getNamespaceName());
                     $allClasses = $this->addClass($declared, $allClasses, $name);
                 }
             }
         }
     }
     foreach ($function->getParameters() as $param) {
         if ($hint = $param->getClassName()) {
             $allClasses = $this->addClass($declared, $allClasses, $hint);
         }
     }
     return $allClasses;
 }
 /**
  * @param string $name
  * @param ReflectionClass|ReflectionMethod $reflection
  * @return string
  */
 private function getClassFqn($name, $reflection)
 {
     return Resolver::resolveClassFqn($name, $reflection->getNamespaceAliases(), $reflection->getNamespaceName());
 }
 /**
  * Returns the default value.
  *
  * @return mixed
  * @throws \TokenReflection\Exception\RuntimeException If the property is not optional.
  * @throws \TokenReflection\Exception\RuntimeException If the property has no default value.
  */
 public function getDefaultValue()
 {
     if (!$this->isOptional()) {
         throw new Exception\RuntimeException('Property is not optional.', Exception\RuntimeException::UNSUPPORTED, $this);
     }
     if (is_array($this->defaultValueDefinition)) {
         if (0 === count($this->defaultValueDefinition)) {
             throw new Exception\RuntimeException('Property has no default value.', Exception\RuntimeException::DOES_NOT_EXIST, $this);
         }
         $this->defaultValue = Resolver::getValueDefinition($this->defaultValueDefinition, $this);
         $this->defaultValueDefinition = Resolver::getSourceCode($this->defaultValueDefinition);
     }
     return $this->defaultValue;
 }