Composer plugin replacing placeholders in the scripts section by dynamic values
When I wrote these macros, I didn't intent to reproduce some production code. I just tried to have the smallest reproducer to focus on fixing the internal errors.
Fix internal error with macros defined by a string
Fix internal error with macros defined by a string
While digging into Larastan's code, I've noticed some cases with the macros were not handled, causing internal errors:
-- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Error
-- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Internal error: Internal error: PHPStan\Type\ClosureTypeFactory::fromClosureObject(): Argument #1 ($closure) must be of type Closure, string given, called in /code/repro-larastan-factory-mixin/vendor/nunomaduro/larastan/src/Methods/MacroMethodsClassReflectionExtension.php on
line 116 in file /code/repro-larastan-factory-mixin/app/demo.php
Run PHPStan with -v option and post the stack trace to:
https://github.com/phpstan/phpstan/issues/new?template=Bug_report.md
Child process error (exit code 1):
-- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Changes
Handle for macros declared like this:
\Illuminate\Support\Str::macro('trimMacro', 'trim');
\Illuminate\Support\Str::macro('asciiAliasMacro', \Illuminate\Support\Str::class.'::ascii');
Breaking changes
none
I've added a 2nd test case because the result is different when the closure has a return type defined.
Add another test case with the Closure's return type declared
Add a test to reproduce issue #1591
Add a test to reproduce issue #1591
Pull request with failing test for #1591
I've tried your suggestion but I still get the same error :(
I've modified vendor/nunomaduro/larastan/stubs/Factory.stub
by adding this:
/**
* Get the first record matching the attributes and update it or create it.
*
* @param array<model-property<TModel>, mixed> $attributes
* @param array<model-property<TModel>, mixed> $values
* @return TModel
*/
public function updateOrCreate($attributes = [], $values = []);
And run these commands:
# vendor/bin/phpstan clear-result-cache
Result cache cleared from directory:
/tmp/phpstan
# vendor/bin/phpstan analyse app/demo.php
------ ------------------------------------------------------------------------
Line demo.php
------ ------------------------------------------------------------------------
8 Access to an undefined property Database\Factories\UserFactory::$name.
------ ------------------------------------------------------------------------
[ERROR] Found 1 error
I've tried this but got no luck (same error message): https://github.com/villfa/repro-larastan-factory-mixin/commit/2c301ff70298de80ba28136189006f86b0ff8590
I get this output:
------ ------------------------------------------------------------------------
Line demo.php
------ ------------------------------------------------------------------------
7 Access to an undefined property Database\Factories\UserFactory::$name.
------ ------------------------------------------------------------------------
[ERROR] Found 1 error
See https://github.com/nunomaduro/larastan/issues/1591 for more details.
--level
used: 6I work on a project where some macros are added to the Eloquent's factory using Factory::mixin()
.
However PHPStan reports errors because, it seems, it gets the return type of these macros wrong.
It looks like this:
<?php
namespace App\Providers;
use App\Mixins\FactoryMacros;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
Factory::mixin(new FactoryMacros());
}
}
Here how we define macros:
<?php
namespace App\Mixins;
/**
* Mixin class for Laravel's Model Factory.
*
* @template TModel of \Illuminate\Database\Eloquent\Model
* @mixin \Illuminate\Database\Eloquent\Factories\Factory<TModel>
* @property TModel $model
*/
class FactoryMacros
{
/**
* Get the first record matching the attributes and update it or create it.
* @return \Closure(array<string, mixed>, array<mixed>):TModel
*/
public function updateOrCreate(): \Closure
{
return function (array $attributes = [], array $values = []) {
if (!is_null($instance = $this->model::where($attributes)->first())) {
$instance->update($values);
return $instance;
}
return $this->create(array_merge($attributes, $values));
};
}
}
When PHPStan reports an error when it analyses the following code:
<?php
namespace App;
use App\Models\User;
echo User::factory()->updateOrCreate([])->name;
# vendor/bin/phpstan analyse app/demo.php
------ ------------------------------------------------------------------------
Line demo.php
------ ------------------------------------------------------------------------
7 Access to an undefined property Database\Factories\UserFactory::$name.
------ ------------------------------------------------------------------------
[ERROR] Found 1 error
Here how the User model and factory are defined:
<?php
namespace App\Models;
use Database\Factories\UserFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
/**
* @property string $name
* @method static UserFactory factory(...$parameters)
*/
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
// snip...
}
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
*/
class UserFactory extends Factory
{
// snip...
}
Update year to 2023
Fix Phar::running false positive error
Fix Phar::running false positive error