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; }
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]); } } }
public function __construct(TypeInfoInterface $type, ParsedField $field) { $this->key = $type->getKey() . ':field:' . strtolower($field->getName()); $this->type = $type; $this->field = $field; }