示例#1
0
 public function storeField(ParsedField $field, ParsedType $type)
 {
     $n = strtolower($type->getName());
     if (empty($this->fields[$n])) {
         $this->fields[$n] = [];
     }
     $this->fields[$n][$field->getName()] = $field;
 }
示例#2
0
 protected function doParse(SourceStream $code, SourceBuffer $buffer, SourceStorageInterface $storage = NULL)
 {
     $parsedTypes = [];
     $parsedFields = [];
     $parsedMethods = [];
     $state = 0;
     $td = -1;
     $nd = -1;
     $namespace = '';
     $imports = [];
     $ct = NULL;
     while ($tok = $code->next($buffer)) {
         if (is_array($tok)) {
             switch ($tok[0]) {
                 case T_NAMESPACE:
                     if ($nd > -1) {
                         $buffer->append(' } ');
                         $nd = -1;
                     }
                     $buffer->append($tok);
                     $namespace = $code->nextName($buffer);
                     $imports = [];
                     $buffer->append($namespace);
                     $tok = $code->next($buffer);
                     if ($tok == ';') {
                         $buffer->append(' { ');
                         $nd = $code->depth();
                     }
                     break;
                 case T_USE:
                     $buffer->append($tok);
                     if ($state & self::WITHIN_IMPL) {
                         $trait = $code->nextName($buffer);
                         if ($trait !== false) {
                             $buffer->append($trait);
                             $ct->addTrait($this->lookup($trait, $namespace, $imports));
                         }
                         continue 2;
                     }
                     while (true) {
                         $name = trim($code->nextName($buffer), '\\');
                         $buffer->append($name);
                         $tok = $code->next($buffer);
                         $isFunc = false;
                         if (is_array($tok)) {
                             // Import function detected, not needed as of now...
                             if ($tok[0] == T_FUNCTION) {
                                 $isFunc = true;
                             }
                         }
                         if (is_array($tok) && $tok[0] == T_AS) {
                             $buffer->append($tok);
                             $alias = $code->nextName($buffer);
                             $buffer->append($alias);
                             $tok = $code->next($buffer);
                             $buffer->append($tok);
                             if (!$isFunc) {
                                 $imports[strtolower($alias)] = trim($name, '\\');
                             }
                         } else {
                             if ($tok == ';') {
                                 if (!$isFunc) {
                                     $imports[strtolower(preg_replace("'^.*\\\\'", '', $name))] = trim($name, '\\');
                                 }
                             }
                             $buffer->append($tok);
                         }
                         if ($tok == ',') {
                             continue;
                         }
                         break;
                     }
                     break;
                 case T_CLASS:
                     // Class scalar lookup in PHP 5.5+
                     if ($code->preceededBy(T_DOUBLE_COLON)) {
                         $buffer->append($tok);
                         continue 2;
                     }
                     $inner = new SourceBuffer();
                     $modifiers = $this->parseTypeModifiers($code->preceedingTokens([T_ABSTRACT, T_FINAL]));
                     if ($modifiers & MetaInfo::IS_ABSTRACT) {
                         $inner->append(' abstract ');
                     } elseif ($modifiers & MetaInfo::IS_FINAL) {
                         $inner->append(' final ');
                     }
                     $inner->append($tok);
                     $state |= self::WITHIN_CLASS;
                     $td = $code->depth() + 1;
                     $name = ltrim($namespace . '\\' . $code->nextName($inner), '\\');
                     $ct = $parsedTypes[] = new ParsedType($name, $code->getFile(), new NamespaceContext($namespace, $imports), $modifiers, $code->comment());
                     if ('' === $code->comment()) {
                         $buffer->typeMarker($ct, ParsedType::MARKER_INTRODUCED_PREPENDED_CODE);
                     } else {
                         if (!$buffer->typeMarkerBeforeDocComment($ct, ParsedType::MARKER_INTRODUCED_PREPENDED_CODE)) {
                             $buffer->typeMarker($ct, ParsedType::MARKER_INTRODUCED_PREPENDED_CODE);
                         }
                     }
                     $buffer->append((string) $inner);
                     $buffer->typeMarker($ct, ParsedType::MARKER_INTRODUCED_NAME);
                     $tok = $code->next($buffer);
                     if (is_array($tok) && $tok[0] == T_EXTENDS) {
                         $buffer->append($tok);
                         $name = $code->nextName($buffer);
                         $ct->addExtends($this->lookup($name, $namespace, $imports));
                         $buffer->typeMarker($ct, ParsedType::MARKER_INTRODUCED_SUPERCLASS);
                         $tok = $code->next($buffer);
                     } else {
                         $buffer->typeMarker($ct, ParsedType::MARKER_INTRODUCED_EXTENDS);
                     }
                     if (is_array($tok) && $tok[0] == T_IMPLEMENTS) {
                         $buffer->append($tok);
                         $name = $code->nextName($buffer);
                         $buffer->append($name);
                         $ct->addImplements($this->lookup($name, $namespace, $imports));
                         $tok = $code->next($buffer);
                         while ($tok == ',') {
                             $buffer->append($tok);
                             $name = $code->nextName($buffer);
                             $buffer->append($name);
                             $ct->addImplements($this->lookup($name, $namespace, $imports));
                             $tok = $code->next($buffer);
                         }
                         $buffer->append(',');
                         $buffer->typeMarker($ct, ParsedType::MARKER_INTRODUCED_INTERFACES);
                         $buffer->append($tok);
                     } else {
                         $buffer->append(' implements ');
                         $buffer->typeMarker($ct, ParsedType::MARKER_INTRODUCED_INTERFACES);
                         $buffer->append($tok);
                     }
                     break;
                 case T_INTERFACE:
                     $inner = new SourceBuffer();
                     $inner->append($tok);
                     $modifiers = MetaInfo::IS_INTERFACE | MetaInfo::IS_ABSTRACT;
                     $state |= self::WITHIN_INTERFACE;
                     $td = $code->depth() + 1;
                     $name = $code->nextName($inner);
                     $inner->append($name);
                     $name = ltrim($namespace . '\\' . $name, '\\');
                     $ct = $parsedTypes[] = new ParsedType($name, $code->getFile(), new NamespaceContext($namespace, $imports), $modifiers, $code->comment());
                     $buffer->typeMarker($ct, ParsedType::MARKER_INTRODUCED_PREPENDED_CODE);
                     $buffer->append((string) $inner);
                     $tok = $code->next($buffer);
                     $buffer->append($tok);
                     if (is_array($tok) && $tok[0] == T_EXTENDS) {
                         $name = $code->nextName($buffer);
                         $buffer->append($name);
                         $ct->addExtends($this->lookup($name, $namespace, $imports));
                         $tok = $code->next($buffer);
                         while ($tok == ',') {
                             $buffer->append($tok);
                             $name = $code->nextName($buffer);
                             $buffer->append($name);
                             $ct->addExtends($this->lookup($name, $namespace, $imports));
                             $tok = $code->next($buffer);
                         }
                         $buffer->append($tok);
                     }
                     break;
                 case T_TRAIT:
                     $inner = new SourceBuffer();
                     $inner->append($tok);
                     $modifiers = MetaInfo::IS_TRAIT | MetaInfo::IS_ABSTRACT;
                     $state |= self::WITHIN_TRAIT;
                     $td = $code->depth() + 1;
                     $name = $code->nextName($inner);
                     $inner->append($name);
                     $name = ltrim($namespace . '\\' . $name, '\\');
                     $ct = $parsedTypes[] = new ParsedType($name, $code->getFile(), new NamespaceContext($namespace, $imports), $modifiers, $code->comment());
                     $buffer->typeMarker($ct, ParsedType::MARKER_INTRODUCED_PREPENDED_CODE);
                     $buffer->append((string) $inner);
                     break;
                 case T_VARIABLE:
                     $buffer->append($tok);
                     if (!($state & self::WITHIN_IMPL) || $td != $code->depth()) {
                         continue 2;
                     }
                     $tmp = $code->preceedingTokens([T_PUBLIC, T_PRIVATE, T_PROTECTED, T_STATIC, T_VAR]);
                     $modifiers = $this->parseFieldModifiers($tmp);
                     $cf = new ParsedField(ltrim($tok[1], '$'), $modifiers, $code->comment());
                     $parsedFields[] = [$ct, $cf];
                     $defaultValueCode = [];
                     while (false !== ($tok = $code->next($buffer)) && $tok != ';') {
                         $buffer->append($tok);
                         $defaultValueCode[] = $tok;
                     }
                     if (count($defaultValueCode) > 1) {
                         array_shift($defaultValueCode);
                         $cf->setDefaultValueCode($defaultValueCode);
                     }
                     $buffer->append($tok);
                     break;
                 case T_FUNCTION:
                     if (!($state & self::WITHIN_TYPE) || $td != $code->depth()) {
                         $buffer->append($tok);
                         continue 2;
                     }
                     $tmp = $code->preceedingTokens([T_ABSTRACT, T_FINAL, T_STATIC, T_PUBLIC, T_PRIVATE, T_PROTECTED]);
                     $modifiers = $this->parseMethodModifiers($tmp);
                     if ($modifiers & MetaInfo::IS_ABSTRACT) {
                         $buffer->append(' abstract ');
                     } elseif ($modifiers & MetaInfo::IS_FINAL) {
                         $buffer->append(' final ');
                     }
                     $buffer->append($tok);
                     if ($state & self::WITHIN_INTERFACE) {
                         $modifiers |= MetaInfo::IS_ABSTRACT;
                     }
                     $tok = $code->next($buffer);
                     $buffer->append($tok);
                     if ($tok == '&') {
                         $modifiers |= MetaInfo::IS_RETURN_REFERENCE;
                         $tok = $code->next($buffer);
                         $buffer->append($tok);
                     }
                     if (!is_array($tok) || $tok[0] != T_STRING) {
                         $buffer->append($tok);
                         continue 2;
                     }
                     $cm = new ParsedMethod($tok[1], $modifiers, $code->comment());
                     $parsedMethods[] = [$ct, $cm];
                     $buffer->append($code->next($buffer));
                     $depth = 1;
                     while (false !== ($tok = $code->next($buffer))) {
                         $modifiers = 0;
                         $hint = NULL;
                         if (is_array($tok)) {
                             switch ($tok[0]) {
                                 case T_CALLABLE:
                                     $hint = 'callable';
                                     $buffer->append($tok);
                                     $tok = $code->next($buffer);
                                     break;
                                 case T_ARRAY:
                                     $hint = 'array';
                                     $buffer->append($tok);
                                     $tok = $code->next($buffer);
                                     break;
                                 case T_STRING:
                                 case T_NS_SEPARATOR:
                                     $name = $tok[1] . $code->nextName($buffer, false);
                                     $buffer->append($name);
                                     $hint = $name === false ? '' : $name;
                                     if ('self' == strtolower($hint)) {
                                         $hint = '\\' . $ct->getName();
                                     } else {
                                         $hint = $this->lookup($hint, $namespace, $imports);
                                     }
                                     $tok = $code->next($buffer);
                             }
                         } else {
                             switch ($tok) {
                                 case ')':
                                     $buffer->append($tok);
                                     $tok = $code->next($buffer);
                                     $buffer->append($tok);
                                     break 2;
                             }
                         }
                         if ($tok == '&') {
                             $modifiers |= MetaInfo::IS_PASSED_BY_REFERENCE;
                             $buffer->append($tok);
                             $tok = $code->next($buffer);
                         }
                         if (is_array($tok) && $tok[0] == T_ELLIPSIS) {
                             $modifiers |= MetaInfo::IS_VARIADIC;
                             $buffer->append($tok);
                             $tok = $code->next($buffer);
                         }
                         $pname = substr($tok[1], 1);
                         $buffer->append($tok);
                         $defaultValueCode = [];
                         $pdepth = $depth;
                         while (false !== ($tok = $code->next($buffer)) && $depth != 0) {
                             $buffer->append($tok);
                             switch ($tok) {
                                 case '(':
                                 case '[':
                                     $depth++;
                                     break;
                                 case ')':
                                 case ']':
                                     $depth--;
                                     break;
                                 case ',':
                                     if ($depth == $pdepth) {
                                         $dc = '';
                                         if (!empty($defaultValueCode)) {
                                             $modifiers |= MetaInfo::IS_OPTIONAL;
                                             $dc = $this->expandNames(array_slice($defaultValueCode, 1), $ct->getName(), $namespace, $imports);
                                         }
                                         $cp = new ParsedParameter($pname, $modifiers);
                                         $cp->setRequiredType($hint);
                                         $cp->setDefaultValueCode($dc);
                                         $cm->addParameter($cp);
                                         continue 3;
                                     }
                                     break;
                             }
                             $defaultValueCode[] = $tok;
                         }
                         $dc = '';
                         if (count($defaultValueCode) > 2) {
                             $modifiers |= MetaInfo::IS_OPTIONAL;
                             $dc = $this->expandNames(array_slice($defaultValueCode, 1, count($defaultValueCode) - 2), $ct->getName(), $namespace, $imports);
                         }
                         $cp = new ParsedParameter($pname, $modifiers);
                         $cp->setRequiredType($hint);
                         $cp->setDefaultValueCode($dc);
                         $cm->addParameter($cp);
                         $buffer->append($tok);
                         break;
                     }
                     // Parse return type declarations introduced in PHP 7.
                     if (':' === $code->peek(0)) {
                         $rfqn = NULL;
                         $rtt = $code->peek(1);
                         if (is_array($rtt)) {
                             switch ($rtt[0]) {
                                 case T_CALLABLE:
                                 case T_ARRAY:
                                     $rfqn = strtolower($rtt[1]);
                                     $buffer->append($code->next($buffer));
                                     $buffer->append($code->next($buffer));
                                     break;
                                 case T_STRING:
                                 case T_NS_SEPARATOR:
                                     $rttn = $code->nextName($buffer);
                                     $rfqn = $this->lookup($rttn, $namespace, $imports);
                                     $buffer->append($rttn);
                                     $buffer->append($code->next($buffer));
                                     break;
                             }
                         }
                         $cm->setReturnType($rfqn);
                     }
                     if (!$cm->isAbstract()) {
                         $buffer->methodMarker($ct, $cm, ParsedMethod::MARKER_PREPENDED_CODE);
                         $this->parseMethodBody($code, $buffer, $cm);
                         $buffer->methodMarker($ct, $cm, ParsedMethod::MARKER_APPENDED_CODE);
                     }
                     break;
                 default:
                     $buffer->append($tok);
             }
         } else {
             if ($td > -1 && $code->depth() == $td - 1 && $tok == '}') {
                 if ($ct->isClass()) {
                     $buffer->typeMarker($ct, ParsedType::MARKER_INTRODUCED_CODE);
                 }
                 $buffer->append($tok);
                 $buffer->typeMarker($ct, ParsedType::MARKER_INTRODUCED_EXTERNAL_CODE);
             } else {
                 $buffer->append($tok);
             }
         }
     }
     if ($nd > -1) {
         $buffer->append(' } ');
     }
     if ($storage !== NULL) {
         foreach ($parsedTypes as $type) {
             $storage->storeType($type);
         }
         foreach ($parsedFields as $data) {
             $storage->storeField($data[1], $data[0]);
         }
         foreach ($parsedMethods as $data) {
             $storage->storeMethod($data[1], $data[0]);
         }
     }
 }
示例#3
0
 public function __construct(TypeInfoInterface $type, ParsedField $field)
 {
     $this->key = $type->getKey() . ':field:' . strtolower($field->getName());
     $this->type = $type;
     $this->field = $field;
 }