1

Background

I am building a class for a laravel system. It is for casting the Ramsey\Uuid\Uuid type in laravel models. I also use phpstan and I seem to be having problems with the generics/templating.

  • in the DB, uuid is stored as binary
  • when loading or setting, Ramsey\Uuid\UuidInterface is enforced

The problem

The laravel vendor package has the following interface:

/**
 * @template TGet
 * @template TSet
 */
interface CastsAttributes
{
    /**
     * Transform the attribute from the underlying model values.
     *
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @param  string  $key
     * @param  mixed  $value
     * @param  array  $attributes
     * @return TGet|null
     */
    public function get($model, string $key, $value, array $attributes);

    /**
     * Transform the attribute to its underlying model values.
     *
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @param  string  $key
     * @param  TSet|null  $value
     * @param  array  $attributes
     * @return mixed
     */
    public function set($model, string $key, $value, array $attributes);
}

My class Uuid implements this

// ...
use Ramsey\Uuid\Uuid as RamseyUuid;
use Ramsey\Uuid\UuidInterface;
// ...

class Uuid implements CastsAttributes // ...
{
    /**
     * Cast the given value.
     *
     * @param Model $model
     * @param string $key
     * @param string $value
     * @param array<mixed> $attributes
     * @return UuidInterface
     */
    public function get($model, string $key, $value, array $attributes)
    {
        $uuid = RamseyUuid::fromBytes($value);
        return $uuid;
    }

    /**
     * Prepare the given value for storage.
     *
     * @param Model $model
     * @param string $key
     * @param UuidInterface $value
     * @param array<mixed> $attributes
     * @return string
     * @throws Exception
     */
    public function set($model, string $key, $value, array $attributes)
    {
        if (!$value instanceof (UuidInterface::class)) {
            throw new Exception('The uuid property is not an instance of Ramsey - UuidInterface');
        }

        return $value->getBytes();
    }

When I run phpstan (above level 6), I get the following error:

  • Class Uuid implements generic interface Illuminate\Contracts\Database\Eloquent\CastsAttributes but does not specify its types: TGet, TSet

I have read the phpstan documentation (generics section, generics by example) several times and I'm simply not sure how it relates to the issue I'm facing. I've also used the playground and it also didn't help me understand the issue.

Question

How do you specify the types of an interface in phpstan

elb98rm
  • 670
  • 6
  • 21

2 Answers2

1

The error has been fixed in this PR. TGet, TSet added in this pull request. I think your error will be fixed in the next release.

Timur Rodya
  • 157
  • 1
  • 1
  • 6
1

Following a response from the author of phpstan (thanks!) the part I was missing was using the @implements tag.

/**
 * @implements CastsAttributes<Uuid, UuidInterface>
 */
class Uuid implements CastsAttributes

This solves the issue up to phpstan max level.

elb98rm
  • 670
  • 6
  • 21