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
|
### 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 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 `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
|
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.
|
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.
|
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.
|
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.
|
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 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) : ?>
|
<?php if ($helpers) : ?>
|
||||||
namespace {
|
namespace {
|
||||||
<?= $helpers ?>
|
<?= $helpers ?>
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ use Illuminate\Support\Collection;
|
|||||||
use Illuminate\Support\Facades\Facade;
|
use Illuminate\Support\Facades\Facade;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Illuminate\Support\Traits\Macroable;
|
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 ReflectionClass;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
@@ -76,6 +80,7 @@ class Generator
|
|||||||
return $this->view->make('helper')
|
return $this->view->make('helper')
|
||||||
->with('namespaces_by_extends_ns', $this->getAliasesByExtendsNamespace())
|
->with('namespaces_by_extends_ns', $this->getAliasesByExtendsNamespace())
|
||||||
->with('namespaces_by_alias_ns', $this->getAliasesByAliasNamespace())
|
->with('namespaces_by_alias_ns', $this->getAliasesByAliasNamespace())
|
||||||
|
->with('real_time_facades', $this->getRealTimeFacades())
|
||||||
->with('helpers', $this->helpers)
|
->with('helpers', $this->helpers)
|
||||||
->with('version', $app->version())
|
->with('version', $app->version())
|
||||||
->with('include_fluent', $this->config->get('ide-helper.include_fluent', true))
|
->with('include_fluent', $this->config->get('ide-helper.include_fluent', true))
|
||||||
@@ -174,6 +179,50 @@ class Generator
|
|||||||
return $aliases;
|
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
|
* 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