diff --git a/CHANGELOG.md b/CHANGELOG.md index b4bc66106..e95e95f99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. ### Fixed - Fix date and datetime handling for attributes that set a serialization format option for the Carbon instance [#1324 / FLeudts](https://github.com/barryvdh/laravel-ide-helper/pull/1324) +- Add support for protected methods using Laravel 8.77 Attributes syntax [#1325 / mridul89](https://github.com/barryvdh/laravel-ide-helper/pull/1325) 2022-02-08, 2.12.2 ------------------ diff --git a/src/Console/ModelsCommand.php b/src/Console/ModelsCommand.php index fd8dd0150..c95eb3365 100644 --- a/src/Console/ModelsCommand.php +++ b/src/Console/ModelsCommand.php @@ -569,13 +569,24 @@ public function getPropertiesFromTable($model) */ public function getPropertiesFromMethods($model) { - $methods = get_class_methods($model); + $methods = (new ReflectionClass($model))->getMethods(); + $methods = array_map(function (\ReflectionMethod $method) { + return $method->getName(); + }, array_filter($methods, function (\ReflectionMethod $method) { + // private methods should not be documented because they are inaccessible outside the class + return !$method->isPrivate(); + })); if ($methods) { sort($methods); foreach ($methods as $method) { $reflection = new \ReflectionMethod($model, $method); $type = $this->getReturnTypeFromReflection($reflection); $isAttribute = is_a($type, '\Illuminate\Database\Eloquent\Casts\Attribute', true); + if ($reflection->isProtected() && !$isAttribute) { + // only the accessors/mutators protected methods should be documented + // without this condition, we will unintentionally include laravel protected methods like setClassCastableAttribute() + continue; + } if ( Str::startsWith($method, 'get') && Str::endsWith( $method, @@ -592,7 +603,7 @@ public function getPropertiesFromMethods($model) } } elseif ($isAttribute) { $name = Str::snake($method); - $types = $this->getAttributeReturnType($model, $method); + $types = $this->getAttributeReturnType($model, $reflection); if ($types->has('get')) { $type = $this->getTypeInModel($model, $types['get']); @@ -1082,10 +1093,11 @@ protected function hasCamelCaseModelProperties() return $this->laravel['config']->get('ide-helper.model_camel_case_properties', false); } - protected function getAttributeReturnType(Model $model, string $method): Collection + protected function getAttributeReturnType(Model $model, \ReflectionMethod $method): Collection { + $method->setAccessible(true); /** @var Attribute $attribute */ - $attribute = $model->{$method}(); + $attribute = $method->invoke($model); return collect([ 'get' => $attribute->get ? optional(new \ReflectionFunction($attribute->get))->getReturnType() : null, diff --git a/tests/Console/ModelsCommand/Attributes/Models/Simple.php b/tests/Console/ModelsCommand/Attributes/Models/Simple.php index 0d1eff89e..fa6ccdf0a 100644 --- a/tests/Console/ModelsCommand/Attributes/Models/Simple.php +++ b/tests/Console/ModelsCommand/Attributes/Models/Simple.php @@ -9,7 +9,14 @@ class Simple extends Model { - public function name(): Attribute + /** + * The method should be protected as per laravel documentation. + * Generally, public works too, but it is not the correct way per laravel documentation and thus intentionally skipped during tests. + * Private Methods are not supported by laravel and will also not be documented by ide-helper. + * + * @return Attribute + */ + protected function name(): Attribute { return new Attribute( function (?string $name): ?string { diff --git a/tests/Console/ModelsCommand/Attributes/__snapshots__/Test__test__1.php b/tests/Console/ModelsCommand/Attributes/__snapshots__/Test__test__1.php index eabeffcbb..1b1d1d1e3 100644 --- a/tests/Console/ModelsCommand/Attributes/__snapshots__/Test__test__1.php +++ b/tests/Console/ModelsCommand/Attributes/__snapshots__/Test__test__1.php @@ -20,7 +20,14 @@ */ class Simple extends Model { - public function name(): Attribute + /** + * The method should be protected as per laravel documentation. + * Generally, public works too, but it is not the correct way per laravel documentation and thus intentionally skipped during tests. + * Private Methods are not supported by laravel and will also not be documented by ide-helper. + * + * @return Attribute + */ + protected function name(): Attribute { return new Attribute( function (?string $name): ?string {