public function testIsFollowedWorks() { $code = '<?php class A { public function foo(){} }'; $tokens = new TokenCollection(token_get_all($code)); $searcher = new Searcher(); $this->assertTrue($searcher->isFollowedBy(T_FUNCTION, 7, $tokens)); }
/** * Extract infos from file * * @param $filename * @return Result */ public function extract($filename) { $result = new Result(); $tokens = $this->tokenizer->tokenize($filename); $nameResolver = new NameResolver(); // default current values $class = $interface = $function = $method = null; // $mapOfAliases = array(); $len = sizeof($tokens, COUNT_NORMAL); for ($n = 0; $n < $len; $n++) { $token = $tokens[$n]; switch ($token->getType()) { case T_USE: $alias = $this->extractors->alias->extract($n, $tokens); if (null !== $alias->name && null !== $alias->alias) { $nameResolver->pushAlias($alias); } break; case T_NAMESPACE: $namespace = '\\' . $this->searcher->getFollowingName($n, $tokens); $this->extractors->class->setNamespace($namespace); $this->extractors->interface->setNamespace($namespace); break; case T_INTERFACE: $class = $this->extractors->interface->extract($n, $tokens); $class->setNameResolver($nameResolver); // push class AND in global AND in local class map $this->result->pushClass($class); $result->pushClass($class); break; case T_EXTENDS: $i = $n; $parent = $this->searcher->getFollowingName($i, $tokens); $class->setParent(trim($parent)); break; case T_CLASS: $class = $this->extractors->class->extract($n, $tokens); $class->setNameResolver($nameResolver); // push class AND in global AND in local class map $this->result->pushClass($class); $result->pushClass($class); break; case T_FUNCTION: if ($class) { // avoid closure $next = $tokens[$n + 1]; if (T_WHITESPACE != $next->getType()) { continue; } $method = $this->extractors->method->extract($n, $tokens); $method->setNameResolver($nameResolver); $class->pushMethod($method); } break; } } return $result; }
/** * Extract class from position * * @param $n * @param TokenCollection $tokens * @return ReflectedClass */ public function extract(&$n, TokenCollection $tokens) { // is abstract ? $prev = $this->searcher->getPrevious($n, $tokens); $isAbstract = $prev && T_ABSTRACT === $prev->getType(); $classname = $this->searcher->getFollowingName($n, $tokens); $class = new ReflectedClass($this->namespace, trim($classname)); $class->setAbstract($isAbstract); return $class; }
/** * Extract alias from position * * @param $n * @param TokenCollection $tokens * @return ReflectedMethod */ public function extract(&$n, TokenCollection $tokens) { $alias = $real = null; $expression = $this->searcher->getUnder(array(';'), $n, $tokens); if (preg_match('!use\\s+(.*)\\s+as\\s+(.*)!i', $expression, $matches)) { list(, $real, $alias) = $matches; } else { if (preg_match('!use\\s+([^\\s\\(]+)\\s*!i', $expression, $matches)) { list(, $real) = $matches; $alias = $real; } } return (object) array('name' => $real, 'alias' => $alias); }
/** * Extract dependency from call * * @param $n * @param TokenCollection $tokens * @return string * @throws \LogicException */ public function extract(&$n, TokenCollection $tokens) { $token = $tokens[$n]; switch ($token->getType()) { case T_PAAMAYIM_NEKUDOTAYIM: $prev = $n - 1; return $this->searcher->getUnder(array('::'), $prev, $tokens); break; case T_NEW: return $this->searcher->getFollowingName($n, $tokens); break; } throw new \LogicException('Classname of call not found'); }
/** * Extract the list of returned values * * @param ReflectedMethod $method * @return $this */ private function extractReturns(ReflectedMethod $method, $n, TokenCollection $tokens) { $resolver = new TypeResolver(); // PHP 7 // we cannot use specific token. The ":" delimiter is a T_STRING token $following = $this->searcher->getUnder(array('{', ';'), $n, $tokens); if (preg_match('!:(.*)!', $following, $matches)) { $type = trim($matches[1]); if (empty($type)) { return $this; } $return = new ReflectedReturn($type, ReflectedReturn::VALUE_UNKNOW, ReflectedReturn::STRICT_TYPE_HINT); $method->pushReturn($return); return $this; } // array of available values based on code if (preg_match_all('!([\\s;]return\\s|^return\\s+)(.*?);!', $method->getContent(), $matches)) { foreach ($matches[2] as $m) { $value = trim($m, ";\t\n\r\v"); $return = new ReflectedReturn($resolver->resolve($m), $value, ReflectedReturn::ESTIMATED_TYPE_HINT); $method->pushReturn($return); } } return $this; }
/** * Extract class from position * * @param $n * @param TokenCollection $tokens * @return ReflectedClass */ public function extract(&$n, TokenCollection $tokens) { // is PHP7 ? $previous = $tokens->get($n - 2); if ($previous && T_NEW === $previous->getType()) { // anonymous class $class = new ReflectedAnonymousClass($this->namespace, 'class@anonymous'); return $class; } // is abstract ? $prev = $this->searcher->getPrevious($n, $tokens); $isAbstract = $prev && T_ABSTRACT === $prev->getType(); $classname = $this->searcher->getFollowingName($n, $tokens); $class = new ReflectedClass($this->namespace, trim($classname)); $class->setAbstract($isAbstract); return $class; }
/** * Extracts content of method * * @param ReflectedMethod $method * @param integer $n * @param TokenCollection $tokens * @return $this */ private function extractContent(ReflectedMethod $method, $n, TokenCollection $tokens) { $end = $this->searcher->getPositionOfClosingBrace($n, $tokens); if ($end > 0) { $collection = $tokens->extract($n, $end); $method->setContent($collection->asString()); } return $this; }
/** * Extract dependency from call * * @param $n * @param TokenCollection $tokens * @return string * @throws \LogicException */ public function extract(&$n, TokenCollection $tokens) { $token = $tokens[$n]; switch ($token->getType()) { case T_PAAMAYIM_NEKUDOTAYIM: $prev = $n - 1; $value = $this->searcher->getUnder(array('::'), $prev, $tokens); if ($value === 'parent') { $extendPosition = $this->searcher->getExtendPostition($tokens); $parentName = $this->searcher->getFollowingName($extendPosition, $tokens); return $parentName; } if ($value === 'self' || $value === 'static') { $extendPosition = $this->searcher->getClassNamePosition($tokens); $className = $this->searcher->getFollowingName($extendPosition, $tokens); return $className; } return $value; case T_NEW: return $this->searcher->getFollowingName($n, $tokens); } throw new \LogicException('Classname of call not found'); }
/** * Extract dependency from call * * @param $n * @param TokenCollection $tokens * @return string * @throws \LogicException */ public function extract(&$n, TokenCollection $tokens) { $token = $tokens[$n]; $call = null; switch ($token->getType()) { case T_PAAMAYIM_NEKUDOTAYIM: $prev = $n - 1; $value = $this->searcher->getUnder(array('::'), $prev, $tokens); switch (true) { case $value == 'parent' && $this->currentClass: return $this->currentClass->getParent(); case $value == 'parent': // we try to get the name of the parent class $extendPosition = $this->searcher->getExtendPosition($tokens, $n); return $this->searcher->getFollowingName($extendPosition, $tokens); case ($value == 'static' || $value === 'self') && $this->currentClass: return $this->currentClass->getFullname(); case $value == 'static' || $value === 'self': $extendPosition = $this->searcher->getClassNamePosition($tokens); return $this->searcher->getFollowingName($extendPosition, $tokens); default: return $value; } case T_NEW: $call = $this->searcher->getFollowingName($n, $tokens); if (preg_match('!^(\\w+)!', $call, $matches)) { // fixes PHP 5.4: (new MyClass)->foo() $call = $matches[1]; } break; } if (null === $call) { throw new \LogicException('Classname of call not found'); } return $call; }
/** * Extract infos from file * * @param TokenCollection $tokens * @return Result */ public function extract($tokens) { $result = new Result(); $nameResolver = new NameResolver(); // default current values $class = $interface = $function = $namespace = $method = null; $len = sizeof($tokens, COUNT_NORMAL); $endAnonymous = 0; $mainContextClass = null; // class containing a anonymous class for ($n = 0; $n < $len; $n++) { if ($mainContextClass && $n > $endAnonymous) { // anonymous class is finished. We back to parent class // methods will be added to the main class now $class = $mainContextClass; $mainContextClass = null; } $token = $tokens[$n]; switch ($token->getType()) { case T_USE: $alias = $this->extractors->alias->extract($n, $tokens); if (null !== $alias->name && null !== $alias->alias) { $nameResolver->pushAlias($alias); } break; case T_NAMESPACE: $namespace = '\\' . $this->searcher->getFollowingName($n, $tokens); $this->extractors->class->setNamespace($namespace); $this->extractors->interface->setNamespace($namespace); break; case T_INTERFACE: $class = $this->extractors->interface->extract($n, $tokens); $class->setNameResolver($nameResolver); // push class AND in global AND in local class map $this->result->pushClass($class); $result->pushClass($class); break; case T_EXTENDS: $i = $n; $parent = $this->searcher->getFollowingName($i, $tokens); $class->setParent(trim($parent)); break; case T_IMPLEMENTS: $i = $n + 1; $contracts = $this->searcher->getUnder(array('{'), $i, $tokens); $contracts = explode(',', $contracts); $contracts = array_map('trim', $contracts); $class->setInterfaces($contracts); break; case T_CLASS: $i = $n; // avoid MyClass::class syntax if ($this->searcher->isPrecededBy(T_DOUBLE_COLON, $i, $tokens, 1)) { continue; } $c = $this->extractors->class->extract($n, $tokens); $c->setNameResolver($nameResolver); // push class AND in global AND in local class map $this->result->pushClass($c); $result->pushClass($c); // PHP 7 and inner classes if ($c instanceof ReflectedAnonymousClass) { // avoid to consider anonymous class as main class $p = $n; $endAnonymous = $this->searcher->getPositionOfClosingBrace($p, $tokens); $mainContextClass = $class; // add anonymous class in method if ($method) { $method->pushAnonymousClass($c); } } $class = $c; break; case T_FUNCTION: if ($class) { // avoid closure $next = $tokens[$n + 1]; if (T_WHITESPACE != $next->getType()) { continue; } $method = $this->extractors->method->extract($n, $tokens, $class); $method->setNamespace($namespace); $class->pushMethod($method); } break; } } return $result; }
/** * Extract class from position * * @param $n * @param TokenCollection $tokens * @return ReflectedInterface */ public function extract(&$n, TokenCollection $tokens) { $classname = $this->searcher->getFollowingName($n, $tokens); return new ReflectedInterface($this->namespace, trim($classname)); }