mirror of
https://github.com/barryvdh/laravel-ide-helper.git
synced 2026-08-17 17:47: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/filesystem": "^8",
|
||||
"illuminate/support": "^8",
|
||||
"nikic/php-parser": "^4.7",
|
||||
"phpdocumentor/type-resolver": "^1.1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
|
||||
+10
-6
@@ -40,6 +40,7 @@ class Alias
|
||||
protected $magicMethods = [];
|
||||
protected $interfaces = [];
|
||||
protected $phpdoc = null;
|
||||
protected $classAliases = [];
|
||||
|
||||
/** @var ConfigRepository */
|
||||
protected $config;
|
||||
@@ -78,10 +79,11 @@ class Alias
|
||||
$this->detectExtendsNamespace();
|
||||
|
||||
if (!empty($this->namespace)) {
|
||||
//Create a DocBlock and serializer instance
|
||||
$this->phpdoc = new DocBlock(new ReflectionClass($alias), new Context($this->namespace));
|
||||
}
|
||||
$this->classAliases = (new UsesResolver())->loadFromClass($this->root);
|
||||
|
||||
//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') {
|
||||
$this->usedMethods = ['decrement', 'increment'];
|
||||
@@ -330,7 +332,7 @@ class Alias
|
||||
|
||||
if (!in_array($magic, $this->usedMethods)) {
|
||||
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;
|
||||
}
|
||||
@@ -359,7 +361,8 @@ class Alias
|
||||
$this->alias,
|
||||
$reflection,
|
||||
$method->name,
|
||||
$this->interfaces
|
||||
$this->interfaces,
|
||||
$this->classAliases
|
||||
);
|
||||
}
|
||||
$this->usedMethods[] = $method->name;
|
||||
@@ -381,7 +384,8 @@ class Alias
|
||||
$this->alias,
|
||||
$reflection,
|
||||
$macro_name,
|
||||
$this->interfaces
|
||||
$this->interfaces,
|
||||
$this->classAliases
|
||||
);
|
||||
$this->usedMethods[] = $macro_name;
|
||||
}
|
||||
|
||||
+4
-2
@@ -17,15 +17,17 @@ class Macro extends Method
|
||||
* @param \ReflectionClass $class
|
||||
* @param null $methodName
|
||||
* @param array $interfaces
|
||||
* @param array $classAliases
|
||||
*/
|
||||
public function __construct(
|
||||
$method,
|
||||
$alias,
|
||||
$class,
|
||||
$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 $return = null;
|
||||
protected $root;
|
||||
protected $classAliases;
|
||||
|
||||
/**
|
||||
* @param \ReflectionMethod|\ReflectionFunctionAbstract $method
|
||||
@@ -45,11 +46,13 @@ class Method
|
||||
* @param \ReflectionClass $class
|
||||
* @param string|null $methodName
|
||||
* @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->interfaces = $interfaces;
|
||||
$this->classAliases = $classAliases;
|
||||
$this->name = $methodName ?: $method->name;
|
||||
$this->real_name = $method->isClosure() ? $this->name : $method->name;
|
||||
$this->initClassDefinedProperties($method, $class);
|
||||
@@ -80,7 +83,7 @@ class 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) {
|
||||
$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) {
|
||||
//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(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
|
||||
@@ -115,4 +146,12 @@ class ExampleClass
|
||||
{
|
||||
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