phpstan / phpstan-doctrine

Doctrine extensions for PHPStan
MIT License
589 stars 97 forks source link

Symfony PdoSessionHandler + Doctrine: Property $sessId is never written, only read. #575

Open Adambean opened 3 months ago

Adambean commented 3 months ago

Very closely related: https://github.com/phpstan/phpstan-doctrine/issues/229

If you have an entity class to handle PDO based Symfony sessions (Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler) the $sessId property will cause PHPStan to think that property $sessId is never written, only read. The purpose of having an entity class is to add extra properties in a session row, such as a link to a user entity, an IP address, browser user agent string, etc.

The Doctrine object manager loader ("tests/object-manager.php") does not solve this problem because the session ID does not use a Doctrine managed primary key generator. (This is identical to the PHP generated session ID.)

Example:

class Session
{
    /** @var resource $sessId */
    #[ORM\Column(name: "sess_id", type: DBALTypes::BINARY, length: 128, unique: true, nullable: false)]
    #[ORM\Id]
    #[ORM\GeneratedValue(strategy: "NONE")]
    private $sessId;
}

Is it possible to mark variable $sessId as read-only without ignoring the error globally? I don't think @property-read will be suitable here as this still requires a getSessId() method to read it.

ondrejmirtes commented 3 months ago

So this is always just a single class in the project? Where is the class name setting stored? How can we detect which class is that?

Adambean commented 3 months ago

The class name is arbitrary to the project, and it's not really configured as such in the project either. It just exists as a Doctrine entity class like any other in userland. Typically we'd be guided to make our sessions table manually with pre-provided SQL, however declaring it as an entity class instead makes sense to allow for userland extensions to it.

What's going on here is that the underlying Symfony PDO session handler is using the same PDO DSN as Doctrine does in userland. (This appears to be the default recommendation.) There are configuration options here to determine the name of the table (usually "sessions") and the column name containing the read-only session ID "db_id_col" (usually "sess_id") though this likely extends beyond the scope of PHPStan, plus it doesn't bare any resemblance to a particular arbitrary entity class in userland.

I guess what I'm somewhat after is a feature akin to @var-read.

ondrejmirtes commented 3 months ago

Or you can just ignore this one instance of an error https://phpstan.org/user-guide/ignoring-errors 😊