Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Support Laravel 8.77 Attributes #1289

Merged
merged 7 commits into from
Jan 3, 2022

Conversation

SimonJnsson
Copy link
Contributor

@SimonJnsson SimonJnsson commented Dec 30, 2021

Summary

This PR adds support for the new Attribute syntax added in Laravel 8.77, added by laravel/framework#40022.

Using Reflection on the get/set method provided to the Attribute, we can determine the return type of the getter and setter.

Below is an example usage (also found in added test).

use Illuminate\Database\Eloquent\Casts\Attribute;

class Users extends Model
{
    public function name(): Attribute
    {
        return new Attribute(
            get: function (?string $name): ?string {
                return $name;
            },
            set: function (?string $name): ?string {
                return $name === null ? null : ucfirst($name);
            }
        );
    }
}

// => after generate models

/**
 * App\Models\Users
 * 
 * @property string|null $name
 * …
 */

Type of change

This PR should be non breaking, but will however require laravel/framework >= 8.77 to work.

  • New feature (non-breaking change which adds functionality)

Checklist

  • Existing tests have been adapted and/or new tests have been added
  • Add a CHANGELOG.md entry
  • Code style has been fixed via composer fix-style

@SimonJnsson SimonJnsson marked this pull request as ready for review December 30, 2021 08:05
Copy link
Collaborator

@mfn mfn left a comment

Choose a reason for hiding this comment

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

Approach overall LGTM thus I enabled running the tests, but they are failing for some of the PHP versions we need to support.

Can you please check this?

@SimonJnsson
Copy link
Contributor Author

Approach overall LGTM thus I enabled running the tests, but they are failing for some of the PHP versions we need to support.

Can you please check this?

Thanks for taking the time to look at it! I'll get to work on the changes as soon as I get the time 👍

@barryvdh
Copy link
Owner

Looks good. I would just skip the tests when the attribute doesn't exist :)

@SimonJnsson
Copy link
Contributor Author

@mfn i've updated the code to not use code that's incompatible with php 7.3. This should fix the failing unit test.

In regards to the failing integration tests, i'm a bit unsure why these are failing as it seems to somehow be happening when installing dependencies. Any pointers would be greatly appreciated.

@SimonJnsson
Copy link
Contributor Author

@barryvdh sounds good, i've added a check for existance of the Attribute class in the test 👍

Copy link
Collaborator

@mfn mfn left a comment

Choose a reason for hiding this comment

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

Tests green, change LGTM!

In regards to the failing integration tests

See no failing tests? 🤔

@SimonJnsson
Copy link
Contributor Author

I was referring to this https://github.com/barryvdh/laravel-ide-helper/runs/4667417056?check_suite_focus=true

But it might've been a random one-off as it sees to be gone now 👍

@barryvdh barryvdh merged commit 29dd57e into barryvdh:master Jan 3, 2022
mfn added a commit to mfn/laravel-ide-helper that referenced this pull request Jan 3, 2022
barryvdh#1289 calls `getReturnType` for every method on every model, which in
turn calls `getReturnTypeFromDocBlock` which has this code:
```php
$phpDocContext = (new ContextFactory())->createFromReflector($reflection);
```
Extracting the docblock is super slow, always has been. Now that we do
this for every method, this adds up a lot.

Performance on a private commercial project _before_ barryvdh#1289 was introduced:
```
$ time ./artisan ide-helper:models --write --reset >/dev/null

real	0m2.857s
user	0m1.835s
sys	0m0.129s
```
After #1829 :
```
$ time ./artisan ide-helper:models --write --reset >/dev/null

real	0m54.147s
user	0m47.132s
sys	0m1.047s
```

However, in this case we **do not need** the phpdoc fallback (which is
legitimate and by design for many other cases), because also the Laravel
implementation only works by inspecting the _actual type_, see
https://github.com/laravel/framework/blob/e0c2620b57be6416820ea7ca8e46fd2f71d2fe35/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php#L570-L575
```php
$returnType = (new ReflectionMethod($this, $method))->getReturnType();

return static::$attributeMutatorCache[get_class($this)][$key] = $returnType &&
            $returnType instanceof ReflectionNamedType &&
            $returnType->getName() === Attribute::class &&
            is_callable($this->{$method}()->get);
```

This side-stepping the phpdoc parsing a) still works correctly and b)
brings us back to the previous performance characteristics:
```
time ./artisan ide-helper:models --write --reset >/dev/null

real	0m2.987s
user	0m1.915s
sys	0m0.120s
```
mfn added a commit to mfn/laravel-ide-helper that referenced this pull request Jan 3, 2022
barryvdh#1289 calls `getReturnType` for every method on every model, which in
turn calls `getReturnTypeFromDocBlock` which has this code:
```php
$phpDocContext = (new ContextFactory())->createFromReflector($reflection);
```
Extracting the docblock is super slow, always has been. Now that we do
this for every method, this adds up a lot.

Performance on a private commercial project _before_ barryvdh#1289 was introduced:
```
$ time ./artisan ide-helper:models --write --reset >/dev/null

real	0m2.857s
user	0m1.835s
sys	0m0.129s
```
After #1829 :
```
$ time ./artisan ide-helper:models --write --reset >/dev/null

real	0m54.147s
user	0m47.132s
sys	0m1.047s
```

However, in this case we **do not need** the phpdoc fallback (which is
legitimate and by design for many other cases), because also the Laravel
implementation only works by inspecting the _actual type_, see
https://github.com/laravel/framework/blob/e0c2620b57be6416820ea7ca8e46fd2f71d2fe35/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php#L570-L575
```php
$returnType = (new ReflectionMethod($this, $method))->getReturnType();

return static::$attributeMutatorCache[get_class($this)][$key] = $returnType &&
            $returnType instanceof ReflectionNamedType &&
            $returnType->getName() === Attribute::class &&
            is_callable($this->{$method}()->get);
```

This side-stepping the phpdoc parsing a) still works correctly and b)
brings us back to the previous performance characteristics:
```
time ./artisan ide-helper:models --write --reset >/dev/null

real	0m2.987s
user	0m1.915s
sys	0m0.120s
```
mfn added a commit to mfn/laravel-ide-helper that referenced this pull request Jan 3, 2022
barryvdh#1289 calls `getReturnType` for every method on every model, which in
turn calls `getReturnTypeFromDocBlock` which has this code:
```php
$phpDocContext = (new ContextFactory())->createFromReflector($reflection);
```
Extracting the docblock is super slow, always has been. Now that we do
this for every method, this adds up a lot.

Performance on a private commercial project _before_ barryvdh#1289 was introduced:
```
$ time ./artisan ide-helper:models --write --reset >/dev/null

real	0m2.857s
user	0m1.835s
sys	0m0.129s
```
After barryvdh#1289 :
```
$ time ./artisan ide-helper:models --write --reset >/dev/null

real	0m54.147s
user	0m47.132s
sys	0m1.047s
```

However, in this case we **do not need** the phpdoc fallback (which is
legitimate and by design for many other cases), because also the Laravel
implementation only works by inspecting the _actual type_, see
https://github.com/laravel/framework/blob/e0c2620b57be6416820ea7ca8e46fd2f71d2fe35/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php#L570-L575
```php
$returnType = (new ReflectionMethod($this, $method))->getReturnType();

return static::$attributeMutatorCache[get_class($this)][$key] = $returnType &&
            $returnType instanceof ReflectionNamedType &&
            $returnType->getName() === Attribute::class &&
            is_callable($this->{$method}()->get);
```

This side-stepping the phpdoc parsing a) still works correctly and b)
brings us back to the previous performance characteristics:
```
time ./artisan ide-helper:models --write --reset >/dev/null

real	0m2.987s
user	0m1.915s
sys	0m0.120s
```
barryvdh pushed a commit that referenced this pull request Jan 3, 2022
#1289 calls `getReturnType` for every method on every model, which in
turn calls `getReturnTypeFromDocBlock` which has this code:
```php
$phpDocContext = (new ContextFactory())->createFromReflector($reflection);
```
Extracting the docblock is super slow, always has been. Now that we do
this for every method, this adds up a lot.

Performance on a private commercial project _before_ #1289 was introduced:
```
$ time ./artisan ide-helper:models --write --reset >/dev/null

real	0m2.857s
user	0m1.835s
sys	0m0.129s
```
After #1289 :
```
$ time ./artisan ide-helper:models --write --reset >/dev/null

real	0m54.147s
user	0m47.132s
sys	0m1.047s
```

However, in this case we **do not need** the phpdoc fallback (which is
legitimate and by design for many other cases), because also the Laravel
implementation only works by inspecting the _actual type_, see
https://github.com/laravel/framework/blob/e0c2620b57be6416820ea7ca8e46fd2f71d2fe35/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php#L570-L575
```php
$returnType = (new ReflectionMethod($this, $method))->getReturnType();

return static::$attributeMutatorCache[get_class($this)][$key] = $returnType &&
            $returnType instanceof ReflectionNamedType &&
            $returnType->getName() === Attribute::class &&
            is_callable($this->{$method}()->get);
```

This side-stepping the phpdoc parsing a) still works correctly and b)
brings us back to the previous performance characteristics:
```
time ./artisan ide-helper:models --write --reset >/dev/null

real	0m2.987s
user	0m1.915s
sys	0m0.120s
```
@mfn mfn mentioned this pull request Jan 9, 2022
5 tasks
d3v2a pushed a commit to d3v2a/laravel-ide-helper that referenced this pull request Feb 16, 2024
* ✨ Set properties from model functions returning an Attribute

* ✅ Add test for model Attributes

* 🎨 Fix code style

* 🔖 Update changelog

* ✅ Update test to not require php 8

* 🐛 Fix PHP 7.3 incompatibility

* ✅ Update tests to only run when Illuminate Attribute exists
d3v2a pushed a commit to d3v2a/laravel-ide-helper that referenced this pull request Feb 16, 2024
…ryvdh#1292)

barryvdh#1289 calls `getReturnType` for every method on every model, which in
turn calls `getReturnTypeFromDocBlock` which has this code:
```php
$phpDocContext = (new ContextFactory())->createFromReflector($reflection);
```
Extracting the docblock is super slow, always has been. Now that we do
this for every method, this adds up a lot.

Performance on a private commercial project _before_ barryvdh#1289 was introduced:
```
$ time ./artisan ide-helper:models --write --reset >/dev/null

real	0m2.857s
user	0m1.835s
sys	0m0.129s
```
After barryvdh#1289 :
```
$ time ./artisan ide-helper:models --write --reset >/dev/null

real	0m54.147s
user	0m47.132s
sys	0m1.047s
```

However, in this case we **do not need** the phpdoc fallback (which is
legitimate and by design for many other cases), because also the Laravel
implementation only works by inspecting the _actual type_, see
https://github.com/laravel/framework/blob/e0c2620b57be6416820ea7ca8e46fd2f71d2fe35/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php#L570-L575
```php
$returnType = (new ReflectionMethod($this, $method))->getReturnType();

return static::$attributeMutatorCache[get_class($this)][$key] = $returnType &&
            $returnType instanceof ReflectionNamedType &&
            $returnType->getName() === Attribute::class &&
            is_callable($this->{$method}()->get);
```

This side-stepping the phpdoc parsing a) still works correctly and b)
brings us back to the previous performance characteristics:
```
time ./artisan ide-helper:models --write --reset >/dev/null

real	0m2.987s
user	0m1.915s
sys	0m0.120s
```
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants