phpstan / phpstan-symfony

Symfony extension for PHPStan
MIT License
698 stars 89 forks source link

Support Symfony Validator #365

Closed wehostadm closed 10 months ago

wehostadm commented 10 months ago

Hi,

I have plenty of errors due to Symfony Validor đź‘Ť

  Line   Validator\UserEmailValidator.php
 ------ --------------------------------------------------------------------------------------
  42     Access to an undefined property Symfony\Component\Validator\Constraint::$message.
         đź’ˇ Learn more: https://phpstan.org/blog/solving-phpstan-access-to-undefined-property
 ------ --------------------------------------------------------------------------------------

Seems that phpStan does not understand this...

Is there something to configure ?

Thanks,

ondrejmirtes commented 10 months ago

Please show a piece of code where you're experiencing this error.

wehostadm commented 10 months ago

@ondrejmirtes

Yes, the code of my custom validator

<?php

namespace App\Validator;

use App\Repository\UserRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

class UserEmailValidator extends ConstraintValidator
{
    private $entityManager;
    private $userRepository;

    public function __construct(EntityManagerInterface $entityManager, UserRepository $userRepository)
    {
        $this->entityManager = $entityManager;
        $this->userRepository = $userRepository;
    }

    public function validate($value, Constraint $constraint)
    {
        if (null === $value || '' === $value)
        {
            return;
        }

        // Get previous values
        $oldObject = $this->entityManager->getUnitOfWork()->getOriginalEntityData($this->context->getObject());

        $check = (is_array($oldObject) && !empty($oldObject) && $oldObject['email'] != $value) || empty($oldObject);
        if ($check)
        {
            $user = $this->userRepository->findOneBy(['email' => $value]);
            if (!$user)
            {
                return;
            }

            $this->context->buildViolation($constraint->message)
                ->setParameter('{{ value }}', $value)
                ->addViolation()
            ;
        }
    }
}

And the Symfony constraint

<?php

namespace App\Validator;

use Symfony\Component\Validator\Constraint;

/**
 * @Annotation
 */
class UserEmail extends Constraint
{
    /*
     * Any public properties become valid options for the annotation.
     * Then, use these in your validator class.
     */
    public $message = 'L\'email {{ value }} est déjà utilisé';
}

Then when I run the PhpStan with the Symfony extension enabled, I got these errors for each custom validator

 Line   Validator\UserEmailValidator.php
 ------ --------------------------------------------------------------------------------------
  42     Access to an undefined property Symfony\Component\Validator\Constraint::$message.
         đź’ˇ Learn more: https://phpstan.org/blog/solving-phpstan-access-to-undefined-property
 ------ --------------------------------------------------------------------------------------

Thanks,

Praesidiarius commented 10 months ago

i have the same issue when i update phpstan/phpstan-symfony from 1.3.2 to 1.3.4

------ ----------------------------------------------------------------------------
  Line   src/Controller/Item/AvailabilityNotificationController.php
 ------ ----------------------------------------------------------------------------
  83     Call to an undefined method
         Symfony\Component\Validator\ConstraintViolationInterface::getConstraint().
 ------ ----------------------------------------------------------------------------

The corresponding code is:

78.    $error = $validator->validate($availabilityNotification);
79.
80.    $success = false;
81.
82.    if (count($error) > 0) {
83.      $first_constraint = $error[0]->getConstraint();
84.
85.      if ($first_constraint instanceof UniqueEntity) {
86.        $success = true;
87.      }
88.    }

$validator is autoinjected by ValidatorInterface $validator

My phpstan/phpstan version is 1.10.39

With phpstan/phpstan-symfony 1.3.2 i get no error, only when i update to 1.3.4

siketyan commented 10 months ago

As explained in https://symfony.com/doc/current/validation/custom_constraint.html#creating-the-validator-itself, you should add a guard to ensure the constraint is the expected type:

if (!$constraint instanceof YourConstraint) {
    throw new UnexpectedTypeException($constraint, YourConstraint::class);
}

Now PHPStan should treat the constraint as your constraint class below the guard!

ondrejmirtes commented 10 months ago

Situation could be improved with generics the same way security Voters work but that’s up to Symfony to decide.

github-actions[bot] commented 9 months ago

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.