mirror of
https://github.com/barryvdh/laravel-ide-helper.git
synced 2026-08-17 17:47:13 +00:00
Support generating helper files for real-time facades (#1455)
* Support generating helper files for real-time facades Add changelog entry for real-time facades support Mention real-time facades support in readme Fix code style Import * Remove useless assertions We cannot 100% control the output file has only the real-time facades, anything else can be in the final output
This commit is contained in:
@@ -26,6 +26,7 @@ All notable changes to this project will be documented in this file.
|
||||
### Added
|
||||
- Add support for nikic/php-parser 5 (next to 4) [#1502 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1502)
|
||||
- Add support for `immutable_date:*` and `immutable_datetime:*` casts. [#1380 / thekonz](https://github.com/barryvdh/laravel-ide-helper/pull/1380)
|
||||
- Add support for real-time facades in the helper file. [pending PR](#)
|
||||
|
||||
2023-02-04, 2.13.0
|
||||
------------------
|
||||
|
||||
@@ -113,6 +113,10 @@ The generator tries to identify the real class, but if it cannot be found, you c
|
||||
Some classes need a working database connection. If you do not have a default working connection, some facades will not be included.
|
||||
You can use an in-memory SQLite driver by adding the `-M` option.
|
||||
|
||||
If you use [real-time facades](https://laravel.com/docs/master/facades#real-time-facades) in your app, those will also be included in the generated file using a `@mixin` annotation and extending the original class underneath the facade.
|
||||
|
||||
**Note**: this feature uses the generated real-time facades files in the `storage/framework/cache` folder. Those files are generated on-demand as you use the real-time facade, so if the framework has not generated that first, it will not be included in the helper file. Run the route/command/code first and then regenerate the helper file and this time the real-time facade will be included in it.
|
||||
|
||||
You can choose to include helper files. This is not enabled by default, but you can override it with the `--helpers (-H)` option.
|
||||
The `Illuminate/Support/helpers.php` is already set up, but you can add/remove your own files in the config file.
|
||||
|
||||
|
||||
@@ -69,6 +69,16 @@ namespace <?= $namespace == '__root' ? '' : trim($namespace, '\\') ?> {
|
||||
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php foreach($real_time_facades as $name): ?>
|
||||
<?php $nested = explode('\\', str_replace('\\'.class_basename($name), '', $name)); ?>
|
||||
namespace <?php echo implode('\\', $nested); ?> {
|
||||
/**
|
||||
* @mixin <?= str_replace('Facades', '', $name) ?>
|
||||
*/
|
||||
class <?= class_basename($name) ?> extends <?= str_replace('Facades', '', $name) ?> {}
|
||||
}
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php if ($helpers) : ?>
|
||||
namespace {
|
||||
<?= $helpers ?>
|
||||
|
||||
@@ -16,6 +16,10 @@ use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Traits\Macroable;
|
||||
use PhpParser\Lexer\Emulative;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use PhpParser\Parser\Php7;
|
||||
use ReflectionClass;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
@@ -76,6 +80,7 @@ class Generator
|
||||
return $this->view->make('helper')
|
||||
->with('namespaces_by_extends_ns', $this->getAliasesByExtendsNamespace())
|
||||
->with('namespaces_by_alias_ns', $this->getAliasesByAliasNamespace())
|
||||
->with('real_time_facades', $this->getRealTimeFacades())
|
||||
->with('helpers', $this->helpers)
|
||||
->with('version', $app->version())
|
||||
->with('include_fluent', $this->config->get('ide-helper.include_fluent', true))
|
||||
@@ -174,6 +179,50 @@ class Generator
|
||||
return $aliases;
|
||||
}
|
||||
|
||||
protected function getRealTimeFacades()
|
||||
{
|
||||
$facades = [];
|
||||
$realTimeFacadeFiles = glob(storage_path('framework/cache/facade-*.php'));
|
||||
foreach($realTimeFacadeFiles as $file) {
|
||||
try {
|
||||
$name = $this->getFullyQualifiedClassNameInFile($file);
|
||||
$facades[$name] = $name;
|
||||
} catch (\Exception $e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $facades;
|
||||
}
|
||||
|
||||
protected function getFullyQualifiedClassNameInFile(string $path) {
|
||||
$contents = file_get_contents($path);
|
||||
|
||||
$parsers = new Php7(new Emulative);
|
||||
|
||||
$parsed = collect($parsers->parse($contents) ?: []);
|
||||
|
||||
$namespace = $parsed->first(function ($node) {
|
||||
return $node instanceof Namespace_;
|
||||
});
|
||||
|
||||
if($namespace) {
|
||||
$name = $namespace->name->toString();
|
||||
|
||||
$class = collect($namespace->stmts)->first(function ($node) {
|
||||
return $node instanceof Class_;
|
||||
});
|
||||
|
||||
if($class) {
|
||||
$name .= '\\' . $class->name->toString();
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Regroup aliases by namespace of extended classes
|
||||
*
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper\Tests;
|
||||
|
||||
use Barryvdh\LaravelIdeHelper\Generator;
|
||||
use Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider;
|
||||
use Illuminate\Foundation\AliasLoader;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use PhpParser\Lexer\Emulative;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use PhpParser\Parser\Php7;
|
||||
|
||||
class RealTimeFacadesTest extends TestCase
|
||||
{
|
||||
public function testRealTimeFacades()
|
||||
{
|
||||
// Add the views path to the view finder so the generator actually generates the file
|
||||
View::addLocation(__DIR__ . '/../resources/views');
|
||||
|
||||
// Clear cached real-time facades
|
||||
$cachedFacades = glob(storage_path('framework/cache/facade-*.php'));
|
||||
foreach ($cachedFacades as $cachedFacade) {
|
||||
unlink($cachedFacade);
|
||||
}
|
||||
|
||||
// Copy stubs to storage path as if the real-time facades were cached by the framework
|
||||
copy(
|
||||
__DIR__ . '/stubs/facade-0e0385307adf5db34c7986ecbd11646061356ec8.php',
|
||||
storage_path('framework/cache/facade-0e0385307adf5db34c7986ecbd11646061356ec8.php')
|
||||
);
|
||||
copy(
|
||||
__DIR__ . '/stubs/facade-9431b04ec1494fc71a1bc848f020044aba2af7b1.php',
|
||||
storage_path('framework/cache/facade-9431b04ec1494fc71a1bc848f020044aba2af7b1.php')
|
||||
);
|
||||
|
||||
// new instance of the generator which we test
|
||||
$generator = new Generator($this->app['config'], $this->app['view'], null, false);
|
||||
|
||||
// Clear aliases and macros to have a small output file
|
||||
AliasLoader::getInstance()->setAliases([]);
|
||||
Request::flushMacros();
|
||||
|
||||
// Generate the helper file and return the content
|
||||
$content = $generator->generate();
|
||||
|
||||
$this->assertStringContainsString('namespace Facades\Illuminate\Foundation\Exceptions {', $content, 'Could not find Facades\Illuminate\Foundation\Exceptions namespace in the generated helper file.');
|
||||
$this->assertStringContainsString('namespace Facades\App\Exceptions {', $content, 'Could not find Facades\App\Exceptions namespace in the generated helper file.');
|
||||
|
||||
$parsed = collect((new Php7(new Emulative()))->parse($content) ?: []);
|
||||
|
||||
// test the Facades\Illuminate\Foundation\Exceptions namespace in the generated helper file
|
||||
$frameworkExceptionsNamespace = $parsed->first(function ($stmt) {
|
||||
return ($stmt instanceof Namespace_) && $stmt->name->toString() === 'Facades\Illuminate\Foundation\Exceptions';
|
||||
});
|
||||
$this->assertNotNull($frameworkExceptionsNamespace, 'Could not find Facades\Illuminate\Foundation\Exceptions namespace');
|
||||
$this->assertSame('Facades\Illuminate\Foundation\Exceptions', $frameworkExceptionsNamespace->name->toString());
|
||||
$this->verifyNamespace($frameworkExceptionsNamespace, 'Illuminate\Foundation\Exceptions\Handler');
|
||||
|
||||
// test the Facades\App\Exceptions namespace in the generated helper file
|
||||
$appExceptionsNamespace = $parsed->first(function ($stmt) {
|
||||
return ($stmt instanceof Namespace_) && $stmt->name->toString() === 'Facades\App\Exceptions';
|
||||
});
|
||||
$this->assertNotNull($appExceptionsNamespace, 'Could not find Facades\App\Exceptions namespace');
|
||||
$this->assertSame('Facades\App\Exceptions', $appExceptionsNamespace->name->toString());
|
||||
$this->verifyNamespace($appExceptionsNamespace, 'App\Exceptions\Handler');
|
||||
}
|
||||
|
||||
private function verifyNamespace(Namespace_ $namespace, $target)
|
||||
{
|
||||
$stmts = collect($namespace->stmts);
|
||||
|
||||
$this->assertInstanceOf(Class_::class, $stmts[0], 'Expected instance of Class_');
|
||||
|
||||
$statement = $stmts[0];
|
||||
$this->assertArrayHasKey('comments', $statement->getAttributes());
|
||||
|
||||
$this->assertStringContainsString('@mixin \\' . $target, $statement->getAttributes()['comments'][0]->getText(), 'Mixin comment not found');
|
||||
$this->assertSame(class_basename($target), $statement->name->toString(), 'Class name not found');
|
||||
$this->assertSame($target, $statement->extends->toString(), 'Class extends not found');
|
||||
}
|
||||
|
||||
protected function getPackageProviders($app)
|
||||
{
|
||||
return [IdeHelperServiceProvider::class];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Facades\Illuminate\Foundation\Exceptions;
|
||||
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
|
||||
/**
|
||||
* @see \Illuminate\Foundation\Exceptions\Handler
|
||||
*/
|
||||
class Handler extends Facade
|
||||
{
|
||||
/**
|
||||
* Get the registered name of the component.
|
||||
*/
|
||||
protected static function getFacadeAccessor(): string
|
||||
{
|
||||
return 'Illuminate\Foundation\Exceptions\Handler';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Facades\App\Exceptions;
|
||||
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
|
||||
/**
|
||||
* @see \App\Exceptions\Handler
|
||||
*/
|
||||
class Handler extends Facade
|
||||
{
|
||||
/**
|
||||
* Get the registered name of the component.
|
||||
*/
|
||||
protected static function getFacadeAccessor(): string
|
||||
{
|
||||
return 'App\Exceptions\Handler';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user