diff --git a/composer.json b/composer.json index b71e4f989..783e4df55 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,8 @@ "orchestra/testbench": "^9.2", "phpunit/phpunit": "^10.5", "spatie/phpunit-snapshot-assertions": "^4 || ^5", - "vimeo/psalm": "^5.4" + "vimeo/psalm": "^5.4", + "vlucas/phpdotenv": "^5" }, "suggest": { "illuminate/events": "Required for automatic helper generation (^6|^7|^8|^9|^10|^11)." diff --git a/php-templates/auth.php b/php-templates/auth.php new file mode 100644 index 000000000..d26a69441 --- /dev/null +++ b/php-templates/auth.php @@ -0,0 +1,42 @@ +map(function ($policy, $key) { + $reflection = new ReflectionFunction($policy); + + $policyClass = null; + + if (get_class($reflection->getClosureThis()) === Illuminate\Auth\Access\Gate::class) { + $vars = $reflection->getClosureUsedVariables(); + + if (isset($vars['callback'])) { + [$policyClass, $method] = explode('@', $vars['callback']); + + $reflection = new ReflectionMethod($policyClass, $method); + } + } + return [ + 'key' => $key, + 'uri' => $reflection->getFileName(), + 'policy_class' => $policyClass, + 'lineNumber' => $reflection->getStartLine(), + ]; + }) + ->merge( + collect(Illuminate\Support\Facades\Gate::policies())->flatMap(function ($policy, $model) { + $methods = (new ReflectionClass($policy))->getMethods(); + + return collect($methods)->map(function (ReflectionMethod $method) use ($policy) { + return [ + 'key' => $method->getName(), + 'uri' => $method->getFileName(), + 'policy_class' => $policy, + 'lineNumber' => $method->getStartLine(), + ]; + })->filter(function ($ability) { + return !in_array($ability['key'], ['allow', 'deny']); + }); + }), + ) + ->values() + ->groupBy('key'); diff --git a/php-templates/middleware.php b/php-templates/middleware.php new file mode 100644 index 000000000..96045756a --- /dev/null +++ b/php-templates/middleware.php @@ -0,0 +1,64 @@ +getMiddlewareGroups()) + ->merge(app("Illuminate\Contracts\Http\Kernel")->getRouteMiddleware()) + ->map(function ($middleware, $key) { + $result = [ + 'class' => null, + 'uri' => null, + 'startLine' => null, + 'parameters' => null, + 'groups' => [], + ]; + + if (is_array($middleware)) { + $result['groups'] = collect($middleware)->map(function ($m) { + if (!class_exists($m)) { + return [ + 'class' => $m, + 'uri' => null, + 'startLine' => null, + ]; + } + + $reflected = new ReflectionClass($m); + $reflectedMethod = $reflected->getMethod('handle'); + + return [ + 'class' => $m, + 'uri' => $reflected->getFileName(), + 'startLine' => + $reflectedMethod->getFileName() === $reflected->getFileName() + ? $reflectedMethod->getStartLine() + : null, + ]; + })->all(); + + return $result; + } + + $reflected = new ReflectionClass($middleware); + $reflectedMethod = $reflected->getMethod('handle'); + + $result = array_merge($result, [ + 'class' => $middleware, + 'uri' => $reflected->getFileName(), + 'startLine' => $reflectedMethod->getStartLine(), + ]); + + $parameters = collect($reflectedMethod->getParameters()) + ->filter(function ($rc) { + return $rc->getName() !== 'request' && $rc->getName() !== 'next'; + }) + ->map(function ($rc) { + return $rc->getName() . ($rc->isVariadic() ? '...' : ''); + }); + + if ($parameters->isEmpty()) { + return $result; + } + + return array_merge($result, [ + 'parameters' => $parameters->implode(','), + ]); + }); diff --git a/resources/views/meta.php b/resources/views/meta.php index 3735218c2..ea934d022 100644 --- a/resources/views/meta.php +++ b/resources/views/meta.php @@ -88,8 +88,19 @@ - $arguments) : ?> - $argumentSet) : ?> + + + expectedArguments(, , argumentsSet('')); diff --git a/src/Console/MetaCommand.php b/src/Console/MetaCommand.php index 07dcf84c0..a5a15f576 100644 --- a/src/Console/MetaCommand.php +++ b/src/Console/MetaCommand.php @@ -12,6 +12,8 @@ namespace Barryvdh\LaravelIdeHelper\Console; use Barryvdh\LaravelIdeHelper\Factories; +use Dotenv\Parser\Entry; +use Dotenv\Parser\Parser; use Illuminate\Console\Command; use Illuminate\Contracts\Config\Repository; use Illuminate\Contracts\View\Factory; @@ -68,9 +70,7 @@ class MetaCommand extends Command protected $configMethods = [ '\config()', '\Illuminate\Config\Repository::get()', - '\Illuminate\Config\Repository::set()', '\Illuminate\Support\Facades\Config::get()', - '\Illuminate\Support\Facades\Config::set()', ]; protected $userMethods = [ @@ -202,59 +202,105 @@ protected function registerClassAutoloadExceptions(): callable protected function getExpectedArgumentSets() { return [ + 'auth' => $this->loadTemplate('auth')->keys()->filter()->toArray(), 'configs' => $this->loadTemplate('configs')->pluck('name')->filter()->toArray(), + 'middleware' => $this->loadTemplate('middleware')->keys()->filter()->toArray(), 'routes' => $this->loadTemplate('routes')->pluck('name')->filter()->toArray(), 'views' => $this->loadTemplate('views')->pluck('key')->filter()->map(function ($value) { return (string) $value; })->toArray(), 'translations' => $this->loadTemplate('translations')->filter()->keys()->toArray(), + 'env' => $this->getEnv(), ]; } protected function getExpectedArguments() { return [ - '\config()' => [ - 0 => 'configs', + [ + 'class' => '\Illuminate\Support\Facades\Gate', + 'method' => [ + 'has', + 'allows', + 'denies', + 'check', + 'any', + 'none', + 'authorize', + 'inspect', + ], + 'argumentSet' => 'auth', ], - '\Illuminate\Config\Repository::get()' => [ - 0 => 'configs', + [ + 'class' => ['\Illuminate\Support\Facades\Route', '\Illuminate\Support\Facades\Auth'], + 'method' => ['can', 'cannot'], + 'argumentSet' => 'auth', ], - '\Illuminate\Config\Repository::set()' => [ - 0 => 'configs', + [ + 'method' => 'config', + 'argumentSet' => 'configs', ], - '\Illuminate\Support\Facades\Config::get()' => [ - 0 => 'configs', + [ + 'class' => ['\Illuminate\Config\Repository', '\Illuminate\Support\Facades\Config'], + 'method' => [ + 'get', + 'getMany', + 'set', + 'string', + 'integer', + 'boolean', + 'float', + 'array', + 'prepend', + 'push', + ], + 'argumentSet' => 'configs', ], - '\Illuminate\Support\Facades\Config::set()' => [ - 0 => 'configs', + [ + 'class' => ['\Illuminate\Support\Facades\Route', '\Illuminate\Routing\Router'], + 'method' => ['middleware', 'withoutMiddleware'], + 'argumentSet' => 'middleware', ], - '\route()' => [ - 0 => 'routes', + [ + 'method' => ['route', 'to_route', 'signedRoute'], + 'argumentSet' => 'routes', ], - '\Illuminate\Support\Facades\Route::get()' => [ - 0 => 'routes', + [ + 'class' => [ + '\Illuminate\Support\Facades\Redirect', + '\Illuminate\Support\Facades\URL', + '\Illuminate\Routing\Redirector', + '\Illuminate\Routing\UrlGenerator', + ], + 'method' => ['route', 'signedRoute', 'temporarySignedRoute'], + 'argumentSet' => 'routes', ], - '\Illuminate\Routing\Router::get()' => [ - 0 => 'routes', + [ + 'method' => 'view', + 'argumentSet' => 'views', ], - '\view()' => [ - 0 => 'views', + [ + 'class' => ['\Illuminate\Support\Facades\View', '\Illuminate\View\Factory'], + 'method' => 'make', + 'argumentSet' => 'views', ], - '\Illuminate\Support\Facades\View::make()' => [ - 0 => 'views', + [ + 'method' => ['__', 'trans'], + 'argumentSet' => 'translations', ], - '\Illuminate\View\Factory::make()' => [ - 0 => 'views', + [ + 'class' => ['\Illuminate\Contracts\Translation\Translator'], + 'method' => ['get'], + 'argumentSet' => 'translations', ], - '\__()' => [ - 0 => 'translations', + [ + 'method' => 'env', + 'argumentSet' => 'env', ], - '\trans()' => [ - 0 => 'translations', - ], - '\Illuminate\Contracts\Translation\Translator::get()' => [ - 0 => 'translations', + [ + 'class' => '\Illuminate\Support\Env', + 'method' => 'get', + 'argumentSet' => 'env', ], ]; } @@ -290,6 +336,21 @@ protected function getOptions() ]; } + protected function getEnv() + { + $envPath = base_path('.env'); + if (!file_exists($envPath)) { + return []; + } + + $parser = new Parser(); + $entries = $parser->parse(file_get_contents($envPath)); + + return collect($entries)->map(function (Entry $entry) { + return $entry->getName(); + }); + } + /** * Remove our custom autoloader that we pushed onto the autoload stack *