Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ 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)

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 @@ -569,13 +569,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 @@ -592,16 +606,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 @@ -677,20 +690,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 @@ -1123,10 +1136,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);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I chose not to wrap this in an if-statement checking for PHP_VERSION_ID since according to the docs from PHP 8.1 onward this method call is a noop.


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

return collect([
'get' => $attribute->get ? optional(new \ReflectionFunction($attribute->get))->getReturnType() : null,
Expand All @@ -1135,7 +1151,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 @@ -1223,7 +1239,7 @@ protected function getReturnTypeFromReflection(\ReflectionMethod $reflection): ?
$type = implode('|', $types);

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

return $type;
Expand Down Expand Up @@ -1465,10 +1481,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 @@ -1545,7 +1561,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()
{
}
}