/**
  * Parses child reflection objects from the token stream.
  *
  * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream
  * @param \TokenReflection\IReflection $parent Parent reflection object
  * @return \TokenReflection\ReflectionFileNamespace
  * @throws \TokenReflection\Exception\ParseException If child elements could not be parsed.
  */
 protected function parseChildren(Stream $tokenStream, IReflection $parent)
 {
     static $skipped = array(T_WHITESPACE => true, T_COMMENT => true, T_DOC_COMMENT => true);
     $depth = 0;
     $firstChild = null;
     while (true) {
         switch ($tokenStream->getType()) {
             case T_USE:
                 while (true) {
                     $namespaceName = '';
                     $alias = null;
                     $tokenStream->skipWhitespaces(true);
                     while (true) {
                         switch ($tokenStream->getType()) {
                             case T_STRING:
                             case T_NS_SEPARATOR:
                                 $namespaceName .= $tokenStream->getTokenValue();
                                 break;
                             case T_FUNCTION:
                                 // "use function" in 5.6+
                                 break;
                             default:
                                 break 2;
                         }
                         $tokenStream->skipWhitespaces(true);
                     }
                     $namespaceName = ltrim($namespaceName, '\\');
                     if (empty($namespaceName)) {
                         throw new Exception\ParseException($this, $tokenStream, 'Imported namespace name could not be determined.', Exception\ParseException::LOGICAL_ERROR);
                     } elseif ('\\' === substr($namespaceName, -1)) {
                         throw new Exception\ParseException($this, $tokenStream, sprintf('Invalid namespace name "%s".', $namespaceName), Exception\ParseException::LOGICAL_ERROR);
                     }
                     if ($tokenStream->is(T_AS)) {
                         // Alias defined
                         $tokenStream->skipWhitespaces(true);
                         if (!$tokenStream->is(T_STRING)) {
                             throw new Exception\ParseException($this, $tokenStream, sprintf('The imported namespace "%s" seems aliased but the alias name could not be determined.', $namespaceName), Exception\ParseException::LOGICAL_ERROR);
                         }
                         $alias = $tokenStream->getTokenValue();
                         $tokenStream->skipWhitespaces(true);
                     } else {
                         // No explicit alias
                         if (false !== ($pos = strrpos($namespaceName, '\\'))) {
                             $alias = substr($namespaceName, $pos + 1);
                         } else {
                             $alias = $namespaceName;
                         }
                     }
                     if (isset($this->aliases[$alias])) {
                         throw new Exception\ParseException($this, $tokenStream, sprintf('Namespace alias "%s" already defined.', $alias), Exception\ParseException::LOGICAL_ERROR);
                     }
                     $this->aliases[$alias] = $namespaceName;
                     $type = $tokenStream->getType();
                     if (';' === $type) {
                         $tokenStream->skipWhitespaces();
                         break 2;
                     } elseif (',' === $type) {
                         // Next namespace in the current "use" definition
                         continue;
                     }
                     throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN);
                 }
             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 '{':
                 $tokenStream->next();
                 $depth++;
                 break;
             case '}':
                 if (0 === $depth--) {
                     break 2;
                 }
                 $tokenStream->next();
                 break;
             case null:
             case T_NAMESPACE:
                 break 2;
             case T_ABSTRACT:
             case T_FINAL:
             case T_CLASS:
             case T_TRAIT:
             case T_INTERFACE:
                 $class = new ReflectionClass($tokenStream, $this->getBroker(), $this);
                 $firstChild = $firstChild ?: $class;
                 $className = $class->getName();
                 if (isset($this->classes[$className])) {
                     if (!$this->classes[$className] instanceof Invalid\ReflectionClass) {
                         $this->classes[$className] = new Invalid\ReflectionClass($className, $this->classes[$className]->getFileName(), $this->getBroker());
                     }
                     if (!$this->classes[$className]->hasReasons()) {
                         $this->classes[$className]->addReason(new Exception\ParseException($this, $tokenStream, sprintf('Class %s is defined multiple times in the file.', $className), Exception\ParseException::ALREADY_EXISTS));
                     }
                 } else {
                     $this->classes[$className] = $class;
                 }
                 $tokenStream->next();
                 break;
             case T_CONST:
                 $tokenStream->skipWhitespaces(true);
                 do {
                     $constant = new ReflectionConstant($tokenStream, $this->getBroker(), $this);
                     $firstChild = $firstChild ?: $constant;
                     $constantName = $constant->getName();
                     if (isset($this->constants[$constantName])) {
                         if (!$this->constants[$constantName] instanceof Invalid\ReflectionConstant) {
                             $this->constants[$constantName] = new Invalid\ReflectionConstant($constantName, $this->constants[$constantName]->getFileName(), $this->getBroker());
                         }
                         if (!$this->constants[$constantName]->hasReasons()) {
                             $this->constants[$constantName]->addReason(new Exception\ParseException($this, $tokenStream, sprintf('Constant %s is defined multiple times in the file.', $constantName), Exception\ParseException::ALREADY_EXISTS));
                         }
                     } else {
                         $this->constants[$constantName] = $constant;
                     }
                     if ($tokenStream->is(',')) {
                         $tokenStream->skipWhitespaces(true);
                     } else {
                         $tokenStream->next();
                     }
                 } while ($tokenStream->is(T_STRING));
                 break;
             case T_STRING:
                 switch ($tokenStream->getTokenValue()) {
                     case 'define':
                         // definition case
                     // definition case
                     default:
                         break;
                 }
                 $tokenStream->next();
                 break;
             case T_FUNCTION:
                 $position = $tokenStream->key() + 1;
                 while (isset($skipped[$type = $tokenStream->getType($position)])) {
                     $position++;
                 }
                 if ('(' === $type) {
                     // Skipping anonymous functions
                     $tokenStream->seek($position)->findMatchingBracket()->skipWhiteSpaces(true);
                     if ($tokenStream->is(T_USE)) {
                         $tokenStream->skipWhitespaces(true)->findMatchingBracket()->skipWhitespaces(true);
                     }
                     $tokenStream->findMatchingBracket()->next();
                     continue;
                 }
                 $function = new ReflectionFunction($tokenStream, $this->getBroker(), $this);
                 $firstChild = $firstChild ?: $function;
                 $functionName = $function->getName();
                 if (isset($this->functions[$functionName])) {
                     if (!$this->functions[$functionName] instanceof Invalid\ReflectionFunction) {
                         $this->functions[$functionName] = new Invalid\ReflectionFunction($functionName, $this->functions[$functionName]->getFileName(), $this->getBroker());
                     }
                     if (!$this->functions[$functionName]->hasReasons()) {
                         $this->functions[$functionName]->addReason(new Exception\ParseException($this, $tokenStream, sprintf('Function %s is defined multiple times in the file.', $functionName), Exception\ParseException::ALREADY_EXISTS));
                     }
                 } else {
                     $this->functions[$functionName] = $function;
                 }
                 $tokenStream->next();
                 break;
             default:
                 $tokenStream->next();
                 break;
         }
     }
     if ($firstChild) {
         $this->startPosition = min($this->startPosition, $firstChild->getStartPosition());
     }
     return $this;
 }
Пример #2
0
    /**
     * Creates definition for trait method body
     *
     * @param ReflectionFunction|ParsedFunction $function Method reflection
     *
     * @return string new method body
     */
    protected function getJoinpointInvocationBody($function)
    {
        $class = '\\' . __CLASS__;
        $dynamicArgs = false;
        $hasOptionals = false;
        $hasReferences = false;
        $argValues = array_map(function ($param) use(&$dynamicArgs, &$hasOptionals, &$hasReferences) {
            /** @var $param Parameter|ParsedParameter */
            $byReference = $param->isPassedByReference();
            $dynamicArg = $param->name == '...';
            $dynamicArgs = $dynamicArgs || $dynamicArg;
            $hasOptionals = $hasOptionals || $param->isOptional() && !$param->isDefaultValueAvailable();
            $hasReferences = $hasReferences || $byReference;
            return ($byReference ? '&' : '') . '$' . $param->name;
        }, $function->getParameters());
        if ($dynamicArgs) {
            // Remove last '...' argument
            array_pop($argValues);
        }
        $args = join(', ', $argValues);
        if ($dynamicArgs) {
            $args = $hasReferences ? "array({$args}) + \\func_get_args()" : '\\func_get_args()';
        } elseif ($hasOptionals) {
            $args = "\\array_slice(array({$args}), 0, \\func_num_args())";
        } else {
            $args = "array({$args})";
        }
        return <<<BODY
static \$__joinPoint = null;
if (!\$__joinPoint) {
    \$__joinPoint = {$class}::getJoinPoint('{$function->name}', __NAMESPACE__);
}
return \$__joinPoint->__invoke({$args});
BODY;
    }
 /**
  * Tests export.
  */
 public function testToString()
 {
     $tests = array('lines', 'docComment', 'noComment', 'invoke', 'noParameters', 'parameters', 'reference', 'noReference', 'noNamespace', 'userDefined', 'noClosure');
     foreach ($tests as $test) {
         $rfl = $this->getFunctionReflection($test);
         $this->assertSame($rfl->internal->__toString(), $rfl->token->__toString());
         $this->assertSame(InternalReflectionFunction::export($this->getFunctionName($test), true), ReflectionFunction::export($this->getBroker(), $this->getFunctionName($test), true));
         // Test loading from a string
         $rfl = $this->getFunctionReflection($test, true);
         $this->assertSame($rfl->internal->__toString(), $rfl->token->__toString());
     }
     $this->assertSame(InternalReflectionFunction::export('strpos', true), ReflectionFunction::export($this->getBroker(), 'strpos', true));
 }