mirror of
https://github.com/barryvdh/laravel-ide-helper.git
synced 2026-08-18 01:57:13 +00:00
Fix Relative class names are not converted to fully-qualified class names (FQCNs) (#1005)
* Fix https://github.com/barryvdh/laravel-ide-helper/issues/627 * Fix codestyle * Init * composer fix-style * Actualize * Fix * normalize composer.json * Fix * composer fix-style * Replace NamespaceUses with UsesResolver * Add UsesResolver tests * Add MethodTest::testClassAliases * Fix code style * Get rid of laravel support helpers * Make class UsesResolver stateless, update tests for it * composer fix-style Co-authored-by: Andrii Savluk <[email protected]> Co-authored-by: laravel-ide-helper <[email protected]> Co-authored-by: SavKS <[email protected]> Co-authored-by: Taras Fomin <[email protected]>
This commit is contained in:
co-authored by
Andrii Savluk
laravel-ide-helper
SavKS
Taras Fomin
parent
6c4dcd9049
commit
c5e18beff1
@@ -28,6 +28,7 @@
|
|||||||
"illuminate/console": "^8",
|
"illuminate/console": "^8",
|
||||||
"illuminate/filesystem": "^8",
|
"illuminate/filesystem": "^8",
|
||||||
"illuminate/support": "^8",
|
"illuminate/support": "^8",
|
||||||
|
"nikic/php-parser": "^4.7",
|
||||||
"phpdocumentor/type-resolver": "^1.1.0"
|
"phpdocumentor/type-resolver": "^1.1.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
|||||||
+10
-6
@@ -40,6 +40,7 @@ class Alias
|
|||||||
protected $magicMethods = [];
|
protected $magicMethods = [];
|
||||||
protected $interfaces = [];
|
protected $interfaces = [];
|
||||||
protected $phpdoc = null;
|
protected $phpdoc = null;
|
||||||
|
protected $classAliases = [];
|
||||||
|
|
||||||
/** @var ConfigRepository */
|
/** @var ConfigRepository */
|
||||||
protected $config;
|
protected $config;
|
||||||
@@ -78,10 +79,11 @@ class Alias
|
|||||||
$this->detectExtendsNamespace();
|
$this->detectExtendsNamespace();
|
||||||
|
|
||||||
if (!empty($this->namespace)) {
|
if (!empty($this->namespace)) {
|
||||||
//Create a DocBlock and serializer instance
|
$this->classAliases = (new UsesResolver())->loadFromClass($this->root);
|
||||||
$this->phpdoc = new DocBlock(new ReflectionClass($alias), new Context($this->namespace));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
//Create a DocBlock and serializer instance
|
||||||
|
$this->phpdoc = new DocBlock(new ReflectionClass($alias), new Context($this->namespace, $this->classAliases));
|
||||||
|
}
|
||||||
|
|
||||||
if ($facade === '\Illuminate\Database\Eloquent\Model') {
|
if ($facade === '\Illuminate\Database\Eloquent\Model') {
|
||||||
$this->usedMethods = ['decrement', 'increment'];
|
$this->usedMethods = ['decrement', 'increment'];
|
||||||
@@ -330,7 +332,7 @@ class Alias
|
|||||||
|
|
||||||
if (!in_array($magic, $this->usedMethods)) {
|
if (!in_array($magic, $this->usedMethods)) {
|
||||||
if ($class !== $this->root) {
|
if ($class !== $this->root) {
|
||||||
$this->methods[] = new Method($method, $this->alias, $class, $magic, $this->interfaces);
|
$this->methods[] = new Method($method, $this->alias, $class, $magic, $this->interfaces, $this->classAliases);
|
||||||
}
|
}
|
||||||
$this->usedMethods[] = $magic;
|
$this->usedMethods[] = $magic;
|
||||||
}
|
}
|
||||||
@@ -359,7 +361,8 @@ class Alias
|
|||||||
$this->alias,
|
$this->alias,
|
||||||
$reflection,
|
$reflection,
|
||||||
$method->name,
|
$method->name,
|
||||||
$this->interfaces
|
$this->interfaces,
|
||||||
|
$this->classAliases
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
$this->usedMethods[] = $method->name;
|
$this->usedMethods[] = $method->name;
|
||||||
@@ -381,7 +384,8 @@ class Alias
|
|||||||
$this->alias,
|
$this->alias,
|
||||||
$reflection,
|
$reflection,
|
||||||
$macro_name,
|
$macro_name,
|
||||||
$this->interfaces
|
$this->interfaces,
|
||||||
|
$this->classAliases
|
||||||
);
|
);
|
||||||
$this->usedMethods[] = $macro_name;
|
$this->usedMethods[] = $macro_name;
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -17,15 +17,17 @@ class Macro extends Method
|
|||||||
* @param \ReflectionClass $class
|
* @param \ReflectionClass $class
|
||||||
* @param null $methodName
|
* @param null $methodName
|
||||||
* @param array $interfaces
|
* @param array $interfaces
|
||||||
|
* @param array $classAliases
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
$method,
|
$method,
|
||||||
$alias,
|
$alias,
|
||||||
$class,
|
$class,
|
||||||
$methodName = null,
|
$methodName = null,
|
||||||
$interfaces = []
|
$interfaces = [],
|
||||||
|
$classAliases = []
|
||||||
) {
|
) {
|
||||||
parent::__construct($method, $alias, $class, $methodName, $interfaces);
|
parent::__construct($method, $alias, $class, $methodName, $interfaces, $classAliases);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+6
-3
@@ -38,6 +38,7 @@ class Method
|
|||||||
protected $real_name;
|
protected $real_name;
|
||||||
protected $return = null;
|
protected $return = null;
|
||||||
protected $root;
|
protected $root;
|
||||||
|
protected $classAliases;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \ReflectionMethod|\ReflectionFunctionAbstract $method
|
* @param \ReflectionMethod|\ReflectionFunctionAbstract $method
|
||||||
@@ -45,11 +46,13 @@ class Method
|
|||||||
* @param \ReflectionClass $class
|
* @param \ReflectionClass $class
|
||||||
* @param string|null $methodName
|
* @param string|null $methodName
|
||||||
* @param array $interfaces
|
* @param array $interfaces
|
||||||
|
* @param array $classAliases
|
||||||
*/
|
*/
|
||||||
public function __construct($method, $alias, $class, $methodName = null, $interfaces = [])
|
public function __construct($method, $alias, $class, $methodName = null, $interfaces = [], array $classAliases = [])
|
||||||
{
|
{
|
||||||
$this->method = $method;
|
$this->method = $method;
|
||||||
$this->interfaces = $interfaces;
|
$this->interfaces = $interfaces;
|
||||||
|
$this->classAliases = $classAliases;
|
||||||
$this->name = $methodName ?: $method->name;
|
$this->name = $methodName ?: $method->name;
|
||||||
$this->real_name = $method->isClosure() ? $this->name : $method->name;
|
$this->real_name = $method->isClosure() ? $this->name : $method->name;
|
||||||
$this->initClassDefinedProperties($method, $class);
|
$this->initClassDefinedProperties($method, $class);
|
||||||
@@ -80,7 +83,7 @@ class Method
|
|||||||
*/
|
*/
|
||||||
protected function initPhpDoc($method)
|
protected function initPhpDoc($method)
|
||||||
{
|
{
|
||||||
$this->phpdoc = new DocBlock($method, new Context($this->namespace));
|
$this->phpdoc = new DocBlock($method, new Context($this->namespace, $this->classAliases));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -363,7 +366,7 @@ class Method
|
|||||||
}
|
}
|
||||||
if ($method) {
|
if ($method) {
|
||||||
$namespace = $method->getDeclaringClass()->getNamespaceName();
|
$namespace = $method->getDeclaringClass()->getNamespaceName();
|
||||||
$phpdoc = new DocBlock($method, new Context($namespace));
|
$phpdoc = new DocBlock($method, new Context($namespace, $this->classAliases));
|
||||||
|
|
||||||
if (strpos($phpdoc->getText(), '{@inheritdoc}') !== false) {
|
if (strpos($phpdoc->getText(), '{@inheritdoc}') !== false) {
|
||||||
//Not at the end yet, try another parent/interface..
|
//Not at the end yet, try another parent/interface..
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Laravel IDE Helper Generator
|
||||||
|
*
|
||||||
|
* @author Barry vd. Heuvel <[email protected]>
|
||||||
|
* @copyright 2014 Barry vd. Heuvel / Fruitcake Studio (http://www.fruitcakestudio.nl)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link https://github.com/barryvdh/laravel-ide-helper
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\LaravelIdeHelper;
|
||||||
|
|
||||||
|
use PhpParser\Node\Stmt\GroupUse;
|
||||||
|
use PhpParser\Node\Stmt\Namespace_;
|
||||||
|
use PhpParser\Node\Stmt\Use_;
|
||||||
|
use PhpParser\Node\Stmt\UseUse;
|
||||||
|
use PhpParser\ParserFactory;
|
||||||
|
|
||||||
|
class UsesResolver
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string $classFQN
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function loadFromClass(string $classFQN): array
|
||||||
|
{
|
||||||
|
return $this->loadFromFile(
|
||||||
|
$classFQN,
|
||||||
|
(new \ReflectionClass($classFQN))->getFileName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $classFQN
|
||||||
|
* @param string $filename
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function loadFromFile(string $classFQN, string $filename): array
|
||||||
|
{
|
||||||
|
return $this->loadFromCode(
|
||||||
|
$classFQN,
|
||||||
|
file_get_contents(
|
||||||
|
$filename
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $classFQN
|
||||||
|
* @param string $code
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function loadFromCode(string $classFQN, string $code): array
|
||||||
|
{
|
||||||
|
$classFQN = ltrim($classFQN, '\\');
|
||||||
|
|
||||||
|
$namespace = rtrim(
|
||||||
|
preg_replace(
|
||||||
|
'/([^\\\\]+)$/',
|
||||||
|
'',
|
||||||
|
$classFQN
|
||||||
|
),
|
||||||
|
'\\'
|
||||||
|
);
|
||||||
|
|
||||||
|
$parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7);
|
||||||
|
$namespaceData = null;
|
||||||
|
|
||||||
|
foreach ($parser->parse($code) as $node) {
|
||||||
|
if ($node instanceof Namespace_ && $node->name->toCodeString() === $namespace) {
|
||||||
|
$namespaceData = $node;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($namespaceData === null) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var Namespace_ $namespaceData */
|
||||||
|
|
||||||
|
$aliases = [];
|
||||||
|
|
||||||
|
foreach ($namespaceData->stmts as $stmt) {
|
||||||
|
if ($stmt instanceof Use_) {
|
||||||
|
if ($stmt->type !== Use_::TYPE_NORMAL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($stmt->uses as $use) {
|
||||||
|
/** @var UseUse $use */
|
||||||
|
|
||||||
|
$alias = $use->alias ?
|
||||||
|
$use->alias->name :
|
||||||
|
self::classBasename($use->name->toCodeString());
|
||||||
|
|
||||||
|
$aliases[$alias] = '\\' . $use->name->toCodeString();
|
||||||
|
}
|
||||||
|
} elseif ($stmt instanceof GroupUse) {
|
||||||
|
foreach ($stmt->uses as $use) {
|
||||||
|
/** @var UseUse $use */
|
||||||
|
|
||||||
|
$alias = $use->alias ?
|
||||||
|
$use->alias->name :
|
||||||
|
self::classBasename($use->name->toCodeString());
|
||||||
|
|
||||||
|
$aliases[$alias] = '\\' . $stmt->prefix->toCodeString() . '\\' . $use->name->toCodeString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $aliases;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $classFQN
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected static function classBasename(string $classFQN): string
|
||||||
|
{
|
||||||
|
return preg_replace('/^.*\\\\([^\\\\]+)$/', '$1', $classFQN);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -97,6 +97,37 @@ DOC;
|
|||||||
$this->assertSame('$chars = \'$\\\'\\\\\'', $method->getParamsWithDefault(true));
|
$this->assertSame('$chars = \'$\\\'\\\\\'', $method->getParamsWithDefault(true));
|
||||||
$this->assertSame(['$chars = \'$\\\'\\\\\''], $method->getParamsWithDefault(false));
|
$this->assertSame(['$chars = \'$\\\'\\\\\''], $method->getParamsWithDefault(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the output of a class when using class aliases for it
|
||||||
|
*/
|
||||||
|
public function testClassAliases()
|
||||||
|
{
|
||||||
|
$reflectionClass = new \ReflectionClass(ExampleClass::class);
|
||||||
|
$reflectionMethod = $reflectionClass->getMethod('getApplication');
|
||||||
|
|
||||||
|
$method = new Method($reflectionMethod, 'Example', $reflectionClass, null, [], [
|
||||||
|
'Application' => '\\Illuminate\\Foundation\\Application',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$output = <<<'DOC'
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Foundation\Application
|
||||||
|
* @static
|
||||||
|
*/
|
||||||
|
DOC;
|
||||||
|
|
||||||
|
$this->assertSame($output, $method->getDocComment(''));
|
||||||
|
$this->assertSame('getApplication', $method->getName());
|
||||||
|
$this->assertSame('\\' . ExampleClass::class, $method->getDeclaringClass());
|
||||||
|
$this->assertSame('', $method->getParams(true));
|
||||||
|
$this->assertSame([], $method->getParams(false));
|
||||||
|
$this->assertSame('', $method->getParamsWithDefault(true));
|
||||||
|
$this->assertSame([], $method->getParamsWithDefault(false));
|
||||||
|
$this->assertTrue($method->shouldReturn());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExampleClass
|
class ExampleClass
|
||||||
@@ -115,4 +146,12 @@ class ExampleClass
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Application
|
||||||
|
*/
|
||||||
|
public function getApplication()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Barryvdh\LaravelIdeHelper\Tests;
|
||||||
|
|
||||||
|
use Barryvdh\LaravelIdeHelper\UsesResolver;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class UsesResolverTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Test that we can correctly load uses from supplied code
|
||||||
|
*/
|
||||||
|
public function testLoadFromCode()
|
||||||
|
{
|
||||||
|
$usesResolver = new UsesResolver();
|
||||||
|
|
||||||
|
$code = <<<'DOC'
|
||||||
|
<?php
|
||||||
|
namespace Barryvdh\LaravelIdeHelper\Tests;
|
||||||
|
|
||||||
|
use Barryvdh\LaravelIdeHelper\UsesResolver as MyUsesResolver;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class UsesResolverTest extends TestCase
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
DOC;
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
$usesResolver->loadFromCode('Barryvdh\\LaravelIdeHelper\\Tests\\UsesResolverTest', $code),
|
||||||
|
[
|
||||||
|
'MyUsesResolver' => '\\Barryvdh\\LaravelIdeHelper\\UsesResolver',
|
||||||
|
'TestCase' => '\\PHPUnit\Framework\TestCase',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that we can correctly load uses from a class
|
||||||
|
*/
|
||||||
|
public function testLoadFromClass()
|
||||||
|
{
|
||||||
|
$usesResolver = new UsesResolver();
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
$usesResolver->loadFromClass(self::class),
|
||||||
|
[
|
||||||
|
'UsesResolver' => '\\Barryvdh\\LaravelIdeHelper\\UsesResolver',
|
||||||
|
'TestCase' => '\\PHPUnit\Framework\TestCase',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user