Skip to content

Commit

Permalink
add find class/trait/interface
Browse files Browse the repository at this point in the history
  • Loading branch information
liuyong committed Sep 6, 2018
1 parent eed87e2 commit 8f98b3b
Show file tree
Hide file tree
Showing 11 changed files with 453 additions and 54 deletions.
6 changes: 3 additions & 3 deletions ROADMAP.md
Expand Up @@ -26,9 +26,9 @@

## class

- [ ] class
- [ ] trait
- [ ] interface
- [x] class
- [x] trait
- [x] interface

## method

Expand Down
8 changes: 8 additions & 0 deletions src/Finder/Position.php
Expand Up @@ -41,6 +41,14 @@ public function find($file, $line, $column, $keyword, $root, $autoload)
return $finder->find();
}

if ($finder instanceof \PhpCTags\Finder\Position\Class_) {
$finder->file = $file;
$finder->root = $root;
$finder->autoload = $autoload;

return $finder->find();
}

if ($finder instanceof \PhpCTags\Finder\Position\Const_) {
$finder->file = $file;
$finder->root = $root;
Expand Down
81 changes: 81 additions & 0 deletions src/Finder/Position/Class_.php
@@ -0,0 +1,81 @@
<?php

namespace PhpCTags\Finder\Position;

class Class_ implements Finder
{
public $root;
public $namespace;
public $name;
public $file;
public $autoload;

public function validate()
{
if (! $this->getRoot()) {
throw new \Exception('Class Finder root is invalid');
}
if (! $this->name) {
throw new \Exception('Class Finder name is invalid');
}
}

public function getRoot()
{
if (! file_exists($this->file)) {
throw new \Exception("Class Finder file not found: {$this->file}");
}
if ($this->root) {
return $this->root;
}
if (! $this->autoload) {
throw new \Exception('Class Finder autoload is invalid');
}

$parser = new \PhpCTags\Parser\Root();
$this->root = $parser->parse($this->file, $this->autoload);
if (! $this->root) {
throw new \Exception('Class Finder root is invalid');
}

return $this->root;
}

public function getAutoloadFile()
{
$autoload = realpath($this->getRoot().DIRECTORY_SEPARATOR.$this->autoload);
if (! file_exists($autoload)) {
throw new \Exception('Class Finder autoload file is not exist');
}

return $autoload;
}

public function find()
{
$this->validate();

require_once $this->getAutoloadFile();

$class = $this->namespace ? $this->namespace.'\\'.$this->name : $this->name;
try {
$refClass = new \ReflectionClass($class);
} catch (\Exception $e) {
throw new \Exception('Reflection Class: '.$e->getMessage());
}

$file = $refClass->getFileName();
if (! file_exists($file)) {
throw new \Exception("Class Finder file not found: $file");
}
$line = $refClass->getStartLine();
if ($line <= 0) {
throw new \Exception("Class Finder line is invalid: $line");
}

$rows = file($file);
$raw = isset($rows[$line - 1]) ? $rows[$line - 1] : null;

return new \PhpCTags\Position($file, $line, stripos($raw, $this->name) + 1);
}
}
46 changes: 3 additions & 43 deletions src/Finder/Position/Method.php
Expand Up @@ -2,23 +2,14 @@

namespace PhpCTags\Finder\Position;

class Method implements Finder
class Method extends Class_ implements Finder
{
public $root;
public $namespace;
public $class;
public $name;
public $file;
public $autoload;

public function validate()
{
if (! $this->getRoot()) {
throw new \Exception('Method Finder root is invalid');
}
if (! $this->name) {
throw new \Exception('Method Finder name is invalid');
}
parent::validate();

if (! $this->class) {
throw new \Exception('Method Finder class is invalid');
}
Expand Down Expand Up @@ -51,35 +42,4 @@ public function find()

return new \PhpCTags\Position($file, $line, stripos($raw, $this->name) + 1);
}

public function getRoot()
{
if (! file_exists($this->file)) {
throw new \Exception("Method Finder file not found: {$this->file}");
}
if ($this->root) {
return $this->root;
}
if (! $this->autoload) {
throw new \Exception('Method Finder autoload is invalid');
}

$parser = new \PhpCTags\Parser\Root();
$this->root = $parser->parse($this->file, $this->autoload);
if (! $this->root) {
throw new \Exception('Method Finder root is invalid');
}

return $this->root;
}

public function getAutoloadFile()
{
$autoload = realpath($this->getRoot().DIRECTORY_SEPARATOR.$this->autoload);
if (! file_exists($autoload)) {
throw new \Exception('Method Finder autoload file is not exist');
}

return $autoload;
}
}
22 changes: 16 additions & 6 deletions src/Parser/Namespace_.php
Expand Up @@ -113,20 +113,20 @@ public function parseConst($name, $namespace, $content, $line)
return $this->parseType($types, $name, $namespace, $content, $line);
}

public function parseMethod($name, $class, $content, $line)
public function parseClass($class, $content, $line)
{
$root = 0 == strncmp($class, '\\', 1);
if ($class) {
$class = trim($class, '\\');
}
if (! $class) {
return [false, null, null, null];
return [false, null, null];
}

if ($root) {
$parts = explode('\\', $class);

return [true, $name, array_pop($parts), count($parts) > 0 ? implode('\\', $parts) : null];
return [true, array_pop($parts), count($parts) > 0 ? implode('\\', $parts) : null];
}

$nsFinder = new \PhpCTags\Finder\Namespace_();
Expand All @@ -146,18 +146,28 @@ public function parseMethod($name, $class, $content, $line)

if (! $use) {
if (! $namespace) {
return [true, $name, array_pop($parts), count($parts) > 0 ? implode('\\', $parts) : null];
return [true, array_pop($parts), count($parts) > 0 ? implode('\\', $parts) : null];
}
$class = array_pop($parts);
array_unshift($parts, $namespace);

return [true, $name, $class, implode('\\', $parts)];
return [true, $class, implode('\\', $parts)];
}

$ps = explode('\\', trim($use[0], '\\'));
array_shift($parts);
$parts = array_merge($ps, $parts);

return [true, $name, array_pop($parts), count($parts) > 0 ? implode('\\', $parts) : null];
return [true, array_pop($parts), count($parts) > 0 ? implode('\\', $parts) : null];
}

public function parseMethod($name, $class, $content, $line)
{
list($ok, $class, $namespace) = $this->parseClass($class, $content, $line);
if ($ok) {
return [$ok, $name, $class, $namespace];
}

return [$ok, null, null, null];
}
}
10 changes: 10 additions & 0 deletions src/Parser/Type.php
Expand Up @@ -55,6 +55,16 @@ public function parse($content, $line, $column, $keyword)
return $finder;
}

$classParser = new \PhpCTags\Parser\Type\Class_();
list($ok, $name, $namespace) = $classParser->parse($tokens, $idx, $content, $line);
if ($ok) {
$finder = new \PhpCTags\Finder\Position\Class_();
$finder->name = $name;
$finder->namespace = $namespace;

return $finder;
}

$constParser = new \PhpCTags\Parser\Type\Const_();
list($ok, $name, $namespace) = $constParser->parse($tokens, $idx, $content, $line);
if ($ok) {
Expand Down
123 changes: 123 additions & 0 deletions src/Parser/Type/Class_.php
@@ -0,0 +1,123 @@
<?php

namespace PhpCTags\Parser\Type;

class Class_ implements Parser
{
public function parse($tokens, $idx, $content, $line)
{
$name = is_array($tokens[$idx]) ? $tokens[$idx][0] : null;
if (T_STRING !== $name && T_STATIC !== $name) {
return [false, null, null];
}

$isClass = false;
$class = null;
$l = count($tokens);

for ($i = $idx; $i >= 0; --$i) {
$token = $tokens[$i];
$name = is_array($token) ? $token[0] : null;
$data = is_array($token) ? $token[1] : $token;

if ('' === trim($data)) {
continue;
}

if (T_NEW == $name || T_INSTANCEOF == $name ||
T_USE == $name || T_CLASS == $name ||
T_TRAIT == $name || T_INTERFACE == $name ||
T_EXTENDS == $name || T_IMPLEMENTS === $name) {
$isClass = true;
if (T_USE == $name) {
$class = '\\'.$class;
}
break;
}

if ('\\' == $data || preg_match('/^[A-Za-z_]+[A-Za-z0-9_]*$/', $data)) {
$class = $data.$class;
} else {
break;
}
}

if (! $isClass) {
$raws = [];
for (; $i >= 0; --$i) {
$token = $tokens[$i];
$name = is_array($token) ? $token[0] : null;
$data = is_array($token) ? $token[1] : $token;

if (T_COMMENT == $name || T_DOC_COMMENT == $name) {
continue;
}

if (T_CLASS == $name) {
break;
}

if (T_IMPLEMENTS == $name) {
$raw = implode('', $raws);
if (preg_match('/^\s*([A-Za-z_][A-Za-z0-9_]*\s*\,?\s*)+$/s', $raw)) {
$isClass = true;
}
break;
}

$raws[] = $data;
}
}

for ($i = $idx + 1; $i < $l; ++$i) {
$token = $tokens[$i];
$name = is_array($token) ? $token[0] : null;
$data = is_array($token) ? $token[1] : $token;

if ('' === trim($data)) {
continue;
}

if (T_PAAMAYIM_NEKUDOTAYIM == $name || T_EXTENDS == $name || T_IMPLEMENTS == $name) {
$isClass = true;
break;
}

if ('\\' == $data || preg_match('/^[A-Za-z_]+[A-Za-z0-9_]*$/', $data)) {
$class = $class.$data;
} else {
break;
}
}

if (! $isClass || ! $class) {
return [false, null, null];
}

return $this->parseNamespace($class, $content, $line);
}

public function parseNamespace($name, $content, $line)
{
$class = null;
$classes = \PhpCTags\Pool\Class_::getInstance()->fromContent($content);
for ($j = count($classes) - 1; $j >= 0; --$j) {
if ($classes[$j][2] <= $line) {
$class = $classes[$j];
break;
}
}

if ('self' == $name || 'static' == $name || 'parent' == $name) {
if ($class) {
return [true, $class[0], $class[1]];
}

return [false, null, null];
}

$nsParser = new \PhpCTags\Parser\Namespace_();

return $nsParser->parseClass($name, $content, $class ? $class[2] : $line);
}
}
4 changes: 2 additions & 2 deletions src/Parser/Use_.php
Expand Up @@ -88,15 +88,15 @@ public function parseUse($raw, $line)
$uses[$alias][] = [$name, $t, $line];
} elseif (3 == $cn) {
if ('as' !== $cs[1]) {
throw new \Exception("invalid use statement: $u");
continue;
}
$name = $prefix ? $prefix.$cs[0] : $cs[0];
$alias = strtolower($cs[2]);
$t = null !== $type ? $type : self::TYPE_NORMAL;
$uses[$alias][] = [$name, $t, $line];
} elseif (4 == $cn) {
if ('as' !== $cs[2]) {
throw new \Exception("invalid use statement: $u");
continue;
}
$name = $prefix ? $prefix.$cs[1] : $cs[1];
$alias = strtolower($cs[3]);
Expand Down

0 comments on commit 8f98b3b

Please sign in to comment.