Skip to content

Commit 73c502d

Browse files
authored
Fix broken ReflectionUnionTypes (#1132)
* Fix exceptions on ReflectionUnionTypes this commit fixes exceptions thrown on ReflectionUnionType::isBuiltIn() and ReflectionUnionType::getNme() called on php8 union types * Fix exceptions on ReflectionUnionTypes this commit fixes exceptions thrown on ReflectionUnionType::isBuiltIn() and ReflectionUnionType::getNme() called on php8 union types * add check for php8.0 * fix static analysis error * fix $types variable undefined * fix failing test * fix failing test * fixed style with php-cs-fixer * add test for union types in parameters and return type * add test for nullable union types in parameters and return type * updated CHANGELOG.md
1 parent 3376522 commit 73c502d

File tree

5 files changed

+156
-17
lines changed

5 files changed

+156
-17
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ All notable changes to this project will be documented in this file.
3535
- Allow model_locations to have glob patterns [\#1059 / saackearl](https://github.com/barryvdh/laravel-ide-helper/pull/1059)
3636
- Error when generating helper for macroable classes which are not facades and contain a "fake" method [\#1066 / domkrm] (https://github.com/barryvdh/laravel-ide-helper/pull/1066)
3737
- Casts with a return type of `static` or `$this` now resolve to an instance of the cast [\#1103 / riesjart](https://github.com/barryvdh/laravel-ide-helper/pull/1103)
38-
38+
- Broken ReflectionUnionTypes [\#1132 / def-studio](https://github.com/barryvdh/laravel-ide-helper/pull/1132)
3939
### Removed
4040
- Removed format and broken generateJsonHelper [\#1053 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1053)
4141

src/Console/ModelsCommand.php

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@
3535
use Illuminate\Support\Str;
3636
use phpDocumentor\Reflection\Types\ContextFactory;
3737
use ReflectionClass;
38+
use ReflectionNamedType;
3839
use ReflectionObject;
40+
use ReflectionType;
3941
use Symfony\Component\Console\Input\InputArgument;
4042
use Symfony\Component\Console\Input\InputOption;
4143
use Symfony\Component\Console\Output\OutputInterface;
@@ -578,7 +580,7 @@ protected function getPropertiesFromMethods($model)
578580
$reflection = new \ReflectionMethod($model, $method);
579581

580582
if ($returnType = $reflection->getReturnType()) {
581-
$type = $returnType instanceof \ReflectionNamedType
583+
$type = $returnType instanceof ReflectionNamedType
582584
? $returnType->getName()
583585
: (string)$returnType;
584586
} else {
@@ -1013,16 +1015,12 @@ protected function getReturnTypeFromReflection(\ReflectionMethod $reflection): ?
10131015
return null;
10141016
}
10151017

1016-
$type = $returnType instanceof \ReflectionNamedType
1017-
? $returnType->getName()
1018-
: (string)$returnType;
1018+
$types = $this->extractReflectionTypes($returnType);
10191019

1020-
if (!$returnType->isBuiltin()) {
1021-
$type = '\\' . $type;
1022-
}
1020+
$type = implode('|', $types);
10231021

1024-
if ($returnType->allowsNull()) {
1025-
$type .= '|null';
1022+
if($returnType->allowsNull()){
1023+
$type .='|null';
10261024
}
10271025

10281026
return $type;
@@ -1215,17 +1213,19 @@ protected function writeModelExternalBuilderMethods(Model $model): void
12151213
protected function getParamType(\ReflectionMethod $method, \ReflectionParameter $parameter): ?string
12161214
{
12171215
if ($paramType = $parameter->getType()) {
1218-
$parameterName = $paramType->getName();
1216+
$types = $this->extractReflectionTypes($paramType);
12191217

1220-
if (!$paramType->isBuiltin()) {
1221-
$parameterName = '\\' . $parameterName;
1222-
}
1218+
$type = implode('|', $types);
12231219

1224-
if ($paramType->allowsNull()) {
1225-
return '?' . $parameterName;
1220+
if($paramType->allowsNull()){
1221+
if(count($types)==1){
1222+
$type = '?' . $type;
1223+
}else{
1224+
$type .='|null';
1225+
}
12261226
}
12271227

1228-
return $parameterName;
1228+
return $type;
12291229
}
12301230

12311231
$docComment = $method->getDocComment();
@@ -1290,4 +1290,32 @@ protected function getParamType(\ReflectionMethod $method, \ReflectionParameter
12901290
// then we have found the type of the variable if not we return null
12911291
return $type;
12921292
}
1293+
1294+
protected function extractReflectionTypes(ReflectionType $reflection_type)
1295+
{
1296+
if($reflection_type instanceof ReflectionNamedType){
1297+
$types[] = $this->getReflectionNamedType($reflection_type);
1298+
}else{
1299+
$types = [];
1300+
foreach ($reflection_type->getTypes() as $named_type){
1301+
if($named_type->getName()==='null'){
1302+
continue;
1303+
}
1304+
1305+
$types[] = $this->getReflectionNamedType($named_type);
1306+
}
1307+
}
1308+
1309+
return $types;
1310+
}
1311+
1312+
protected function getReflectionNamedType(ReflectionNamedType $paramType): string
1313+
{
1314+
$parameterName = $paramType->getName();
1315+
if (!$paramType->isBuiltin()) {
1316+
$parameterName = '\\' . $parameterName;
1317+
}
1318+
1319+
return $parameterName;
1320+
}
12931321
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\UnionTypes\Models;
6+
7+
use Illuminate\Database\Eloquent\Model;
8+
use Illuminate\Database\Eloquent\Relations\HasMany;
9+
use Illuminate\Database\Query\Builder;
10+
11+
class UnionTypeModel extends Model
12+
{
13+
public function scopeWithUnionTypeParameter(Builder $query, string|int $bar): Builder
14+
{
15+
return $query->where('foo', $bar);
16+
}
17+
18+
public function scopeWithNullableUnionTypeParameter(Builder $query, null|string|int $bar): Builder
19+
{
20+
return $query->where('foo', $bar);
21+
}
22+
23+
public function withUnionTypeReturn(): HasMany|UnionTypeModel
24+
{
25+
return $this->hasMany(UnionTypeModel::class);
26+
}
27+
28+
public function getFooAttribute(): string|int|null
29+
{
30+
return $this->getAttribute('foo');
31+
}
32+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\UnionTypes;
6+
7+
use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
8+
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand;
9+
use Illuminate\Foundation\Application;
10+
11+
class Test extends AbstractModelsCommand
12+
{
13+
protected function setUp(): void
14+
{
15+
parent::setUp();
16+
17+
if (PHP_VERSION_ID < 80000) {
18+
$this->markTestSkipped('This test requires PHP 8.0 or higher');
19+
}
20+
}
21+
22+
public function test(): void
23+
{
24+
$command = $this->app->make(ModelsCommand::class);
25+
26+
$tester = $this->runCommand($command, [
27+
'--write' => true,
28+
]);
29+
30+
$this->assertSame(0, $tester->getStatusCode());
31+
$this->assertStringContainsString('Written new phpDocBlock to', $tester->getDisplay());
32+
$this->assertMatchesMockedSnapshot();
33+
}
34+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\UnionTypes\Models;
6+
7+
use Illuminate\Database\Eloquent\Model;
8+
use Illuminate\Database\Eloquent\Relations\HasMany;
9+
use Illuminate\Database\Query\Builder;
10+
11+
/**
12+
* Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\UnionTypes\Models\UnionTypeModel
13+
*
14+
* @property-read string|int|null $foo
15+
* @property-read \Illuminate\Database\Eloquent\Collection|UnionTypeModel[] $withUnionTypeReturn
16+
* @property-read int|null $with_union_type_return_count
17+
* @method static \Illuminate\Database\Eloquent\Builder|UnionTypeModel newModelQuery()
18+
* @method static \Illuminate\Database\Eloquent\Builder|UnionTypeModel newQuery()
19+
* @method static \Illuminate\Database\Eloquent\Builder|UnionTypeModel query()
20+
* @method static \Illuminate\Database\Eloquent\Builder|UnionTypeModel withNullableUnionTypeParameter(string|int|null $bar)
21+
* @method static \Illuminate\Database\Eloquent\Builder|UnionTypeModel withUnionTypeParameter(string|int $bar)
22+
* @mixin \Eloquent
23+
*/
24+
class UnionTypeModel extends Model
25+
{
26+
public function scopeWithUnionTypeParameter(Builder $query, string|int $bar): Builder
27+
{
28+
return $query->where('foo', $bar);
29+
}
30+
31+
public function scopeWithNullableUnionTypeParameter(Builder $query, null|string|int $bar): Builder
32+
{
33+
return $query->where('foo', $bar);
34+
}
35+
36+
public function withUnionTypeReturn(): HasMany|UnionTypeModel
37+
{
38+
return $this->hasMany(UnionTypeModel::class);
39+
}
40+
41+
public function getFooAttribute(): string|int|null
42+
{
43+
return $this->getAttribute('foo');
44+
}
45+
}

0 commit comments

Comments
 (0)