diff --git a/CHANGELOG.md b/CHANGELOG.md index 83c8a0f..6ff1fcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. ### Added - Support Laravel 8 [\#1022 / barryvdh](https://github.com/barryvdh/laravel-ide-helper/pull/1022) - Add option to force usage of FQN [\#1031 / edvordo](https://github.com/barryvdh/laravel-ide-helper/pull/1031) +- Add support for macros of all macroable classes [\#1006 / domkrm](https://github.com/barryvdh/laravel-ide-helper/pull/1006) 2020-08-11, 2.8.0 ----------------- diff --git a/README.md b/README.md index 7142569..5cb9e45 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,18 @@ You can use an in-memory SQLite driver by adding the `-M` 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. +### Automatic PHPDoc generation for macros and mixins + +This package can generate PHPDocs for macros and mixins which will be added to the `_ide_helper.php` file. + +But this only works if you use type hinting when declaring a macro. + +```php +Str::macro('concat', function(string $str1, string $str2) : string { + return $str1 . $str2; +}); +``` + ### Automatic PHPDocs for models If you don't want to write your properties yourself, you can use the command `php artisan ide-helper:models` to generate diff --git a/src/Generator.php b/src/Generator.php index d1b4517..877e915 100644 --- a/src/Generator.php +++ b/src/Generator.php @@ -13,6 +13,9 @@ namespace Barryvdh\LaravelIdeHelper; use Illuminate\Foundation\AliasLoader; use Illuminate\Support\Collection; +use Illuminate\Support\Str; +use Illuminate\Support\Traits\Macroable; +use ReflectionClass; use Symfony\Component\Console\Output\OutputInterface; class Generator @@ -220,7 +223,11 @@ class Generator */ protected function getAliasesByExtendsNamespace() { - return $this->getValidAliases()->groupBy(function (Alias $alias) { + $aliases = $this->getValidAliases(); + + $this->addMacroableClasses($aliases); + + return $aliases->groupBy(function (Alias $alias) { return $alias->getExtendsNamespace(); }); } @@ -291,4 +298,55 @@ class Generator echo $string . "\r\n"; } } + + /** + * Add all macroable classes which are not already loaded as an alias and have defined macros. + * + * @param Collection $aliases + */ + protected function addMacroableClasses(Collection $aliases) + { + $macroable = $this->getMacroableClasses($aliases); + + foreach ($macroable as $class) { + $reflection = new ReflectionClass($class); + + if (!$reflection->getStaticProperties()['macros']) { + continue; + } + + $aliases[] = new Alias($this->config, $class, $class, [], $this->interfaces); + } + } + + /** + * Get all loaded macroable classes which are not loaded as an alias. + * + * @param Collection $aliases + * @return Collection + */ + protected function getMacroableClasses(Collection $aliases) + { + return (new Collection(get_declared_classes())) + ->filter(function ($class) { + $reflection = new ReflectionClass($class); + + // Filter out internal classes and class aliases + return !$reflection->isInternal() && $reflection->getName() === $class; + }) + ->filter(function ($class) { + $traits = class_uses($class); + + // Filter only classes with the macroable trait + return isset($traits[Macroable::class]); + }) + ->filter(function ($class) use ($aliases) { + $class = Str::start($class, '\\'); + + // Filter out aliases + return !$aliases->first(function (Alias $alias) use ($class) { + return $alias->getExtends() === $class; + }); + }); + } } diff --git a/src/Macro.php b/src/Macro.php index 2dd9e20..55132fe 100644 --- a/src/Macro.php +++ b/src/Macro.php @@ -3,6 +3,7 @@ namespace Barryvdh\LaravelIdeHelper; use Barryvdh\Reflection\DocBlock; +use Barryvdh\Reflection\DocBlock\Tag; class Macro extends Method { @@ -30,7 +31,26 @@ class Macro extends Method */ protected function initPhpDoc($method) { - $this->phpdoc = new DocBlock($method); + $this->phpdoc = new DocBlock('/** */'); + + // Add macro parameters + foreach ($method->getParameters() as $parameter) { + $type = $parameter->hasType() ? $parameter->getType()->getName() : 'mixed'; + $type .= $parameter->hasType() && $parameter->getType()->allowsNull() ? '|null' : ''; + + $name = $parameter->isVariadic() ? '...' : ''; + $name .= '$' . $parameter->getName(); + + $this->phpdoc->appendTag(Tag::createInstance("@param {$type} {$name}")); + } + + // Add macro return type + if ($method->hasReturnType()) { + $type = $method->getReturnType()->getName(); + $type .= $method->getReturnType()->allowsNull() ? '|null' : ''; + + $this->phpdoc->appendTag(Tag::createInstance("@return {$type}")); + } } /**