Skip to content

Commit

Permalink
Add support for protected Attribute accessors (barryvdh#1339)
Browse files Browse the repository at this point in the history
* Add support for protected Attribute accessors

* Fix formatting

* Prevent accessors methods marked as private from being added

* Exclude specific accessors based on trait instead of class

* Add changelog entry

* Add clarifying comment

* Fix accessor attributes not working on PHP < 8.1

* Reintroduce method variable to reduce PR clutter

* Change variable name to reduce PR clutter

* Update CHANGELOG.md

---------

Co-authored-by: Barry vd. Heuvel <barry@fruitcake.nl>
Co-authored-by: Barry vd. Heuvel <barryvdh@gmail.com>
  • Loading branch information
3 people authored and d3v2a committed Feb 16, 2024
1 parent 67dc1db commit b882f79
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 25 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
[Next release](https://github.com/barryvdh/laravel-ide-helper/compare/v2.13.0...master)
--------------


### Fixed
- Add support for attribute accessors marked as protected. [#1339 / pindab0ter](https://github.com/barryvdh/laravel-ide-helper/pull/1339)

### Added
- Add support for `immutable_date:*` and `immutable_datetime:*` casts. [#1380 / thekonz](https://github.com/barryvdh/laravel-ide-helper/pull/1380)

Expand All @@ -14,7 +18,7 @@ All notable changes to this project will be documented in this file.
2023-02-04, 2.13.0
------------------

### Fixes
### Fixed
- Fix return type of methods provided by `SoftDeletes` [#1345 / KentarouTakeda](https://github.com/barryvdh/laravel-ide-helper/pull/1345)
- Handle PHP 8.1 deprecation warnings when passing `null` to `new \ReflectionClass` [#1351 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1351)
- Fix issue where \Eloquent is not included when using write_mixin [#1352 / Jefemy](https://github.com/barryvdh/laravel-ide-helper/pull/1352)
Expand Down
56 changes: 36 additions & 20 deletions src/Console/ModelsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -605,13 +605,27 @@ public function getPropertiesFromTable($model)
*/
public function getPropertiesFromMethods($model)
{
$methods = get_class_methods($model);
if ($methods) {
sort($methods);
foreach ($methods as $method) {
$reflection = new \ReflectionMethod($model, $method);
$reflectionClass = new ReflectionClass($model);
$reflections = $reflectionClass->getMethods();
if ($reflections) {
// Filter out private methods because they can't be used to generate magic properties and HasAttributes'
// methods that resemble mutators but aren't.
$reflections = array_filter($reflections, function (\ReflectionMethod $methodReflection) {
return !$methodReflection->isPrivate() && !(
in_array(
\Illuminate\Database\Eloquent\Concerns\HasAttributes::class,
$methodReflection->getDeclaringClass()->getTraitNames()
) && (
$methodReflection->getName() === 'setClassCastableAttribute' ||
$methodReflection->getName() === 'setEnumCastableAttribute'
)
);
});
sort($reflections);
foreach ($reflections as $reflection) {
$type = $this->getReturnTypeFromReflection($reflection);
$isAttribute = is_a($type, '\Illuminate\Database\Eloquent\Casts\Attribute', true);
$method = $reflection->getName();
if (
Str::startsWith($method, 'get') && Str::endsWith(
$method,
Expand All @@ -628,16 +642,15 @@ public function getPropertiesFromMethods($model)
}
} elseif ($isAttribute) {
$name = Str::snake($method);
$types = $this->getAttributeReturnType($model, $method);
$types = $this->getAttributeReturnType($model, $reflection);
$comment = $this->getCommentFromDocBlock($reflection);

if ($types->has('get')) {
$type = $this->getTypeInModel($model, $types['get']);
$comment = $this->getCommentFromDocBlock($reflection);
$this->setProperty($name, $type, true, null, $comment);
}

if ($types->has('set')) {
$comment = $this->getCommentFromDocBlock($reflection);
$this->setProperty($name, null, null, true, $comment);
}
} elseif (
Expand Down Expand Up @@ -713,20 +726,20 @@ public function getPropertiesFromMethods($model)
$search = '$this->' . $relation . '(';
if (stripos($code, $search) || ltrim($impl, '\\') === ltrim((string)$type, '\\')) {
//Resolve the relation's model to a Relation object.
$methodReflection = new \ReflectionMethod($model, $method);
if ($methodReflection->getNumberOfParameters()) {
if ($reflection->getNumberOfParameters()) {
continue;
}

$comment = $this->getCommentFromDocBlock($reflection);
// Adding constraints requires reading model properties which
// can cause errors. Since we don't need constraints we can
// disable them when we fetch the relation to avoid errors.
$relationObj = Relation::noConstraints(function () use ($model, $method) {
$relationObj = Relation::noConstraints(function () use ($model, $reflection) {
try {
return $model->$method();
$methodName = $reflection->getName();
return $model->$methodName();
} catch (Throwable $e) {
$this->warn(sprintf('Error resolving relation model of %s:%s() : %s', get_class($model), $method, $e->getMessage()));
$this->warn(sprintf('Error resolving relation model of %s:%s() : %s', get_class($model), $reflection->getName(), $e->getMessage()));

return null;
}
Expand Down Expand Up @@ -1170,10 +1183,13 @@ 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 $reflectionMethod): Collection
{
// Private/protected ReflectionMethods require setAccessible prior to PHP 8.1
$reflectionMethod->setAccessible(true);

/** @var Attribute $attribute */
$attribute = $model->{$method}();
$attribute = $reflectionMethod->invoke($model);

return collect([
'get' => $attribute->get ? optional(new \ReflectionFunction($attribute->get))->getReturnType() : null,
Expand All @@ -1182,7 +1198,7 @@ protected function getAttributeReturnType(Model $model, string $method): Collect
->filter()
->map(function ($type) {
if ($type instanceof \ReflectionUnionType) {
$types =collect($type->getTypes())
$types = collect($type->getTypes())
/** @var ReflectionType $reflectionType */
->map(function ($reflectionType) {
return collect($this->extractReflectionTypes($reflectionType));
Expand Down Expand Up @@ -1270,7 +1286,7 @@ protected function getReturnTypeFromReflection(\ReflectionMethod $reflection): ?
$type = implode('|', $types);

if ($returnType->allowsNull()) {
$type .='|null';
$type .= '|null';
}

return $type;
Expand Down Expand Up @@ -1512,10 +1528,10 @@ protected function getParamType(\ReflectionMethod $method, \ReflectionParameter
$type = implode('|', $types);

if ($paramType->allowsNull()) {
if (count($types)==1) {
if (count($types) == 1) {
$type = '?' . $type;
} else {
$type .='|null';
$type .= '|null';
}
}

Expand Down Expand Up @@ -1592,7 +1608,7 @@ protected function extractReflectionTypes(ReflectionType $reflection_type)
} else {
$types = [];
foreach ($reflection_type->getTypes() as $named_type) {
if ($named_type->getName()==='null') {
if ($named_type->getName() === 'null') {
continue;
}

Expand Down
4 changes: 2 additions & 2 deletions tests/Console/ModelsCommand/Attributes/Models/Simple.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

class Simple extends Model
{
public function name(): Attribute
protected function name(): Attribute
{
return new Attribute(
function (?string $name): ?string {
Expand All @@ -29,7 +29,7 @@ function (?string $name): ?string {
*
* @return \Illuminate\Database\Eloquent\Casts\Attribute
*/
public function notAnAttribute()
protected function notAnAttribute()
{
return new Attribute(
function (?string $value): ?string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*/
class Simple extends Model
{
public function name(): Attribute
protected function name(): Attribute
{
return new Attribute(
function (?string $name): ?string {
Expand All @@ -40,7 +40,7 @@ function (?string $name): ?string {
*
* @return \Illuminate\Database\Eloquent\Casts\Attribute
*/
public function notAnAttribute()
protected function notAnAttribute()
{
return new Attribute(
function (?string $value): ?string {
Expand Down
4 changes: 4 additions & 0 deletions tests/Console/ModelsCommand/Getter/Models/Simple.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,8 @@ public function getAttributeReturnsNullableCallableAttribute(): ?callable
public function getAttributeReturnsVoidAttribute(): void
{
}

private function getInvalidAccessModifierAttribute()
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,8 @@ public function getAttributeReturnsNullableCallableAttribute(): ?callable
public function getAttributeReturnsVoidAttribute(): void
{
}

private function getInvalidAccessModifierAttribute()
{
}
}

0 comments on commit b882f79

Please # to comment.