Closed arderyp closed 2 months ago
This bug report is missing a link to reproduction at phpstan.org/try.
It will most likely be closed after manual review.
You should at least post some code that leads to this behaviour.
I will post later this weekend when I get a chance.
@ondrejmirtes
I've updated the phpstan
output obfuscation to be more clear and broken it into chunks with the corresponding code below each chunk. I've also marked the phpstan errors lines with corresponding // PHPSTAN
comments.
These issues only surfaced when updating to latest phpstan
from 1.10.32
to 1.10.57
and phpstan-doctrine
from 1.3.42
to 1.3.59
.
------ -------------------------------------------------------------------------------------------------------------
Line src/App/Repository/AbstractAppRepository.php
------ -------------------------------------------------------------------------------------------------------------
19 QueryBuilder: [Semantical Error] line 0, col 86 near 'active = 1': Error: Class
App\Entity\AbstractAppEntity has no field or association named active
40 QueryBuilder: [Semantical Error] line 0, col 86 near 'altId': Error: Class
App\Entity\AbstractAppEntity has no field or association named altId
49 QueryBuilder: [Semantical Error] line 0, col 86 near 'altId': Error: Class
App\Entity\AbstractAppEntity has no field or association named altId
82 QueryBuilder: [Semantical Error] line 0, col 86 near 'number = :number': Error: Class
App\Entity\AbstractAppEntity has no field or association named number
93 QueryBuilder: [Semantical Error] line 0, col 86 near 'active = 1 ORDER': Error: Class
App\Entity\AbstractAppEntity has no field or association named active
------ -------------------------------------------------------------------------------------------------------------
use App\Entity\AbstractAppEntity;
use Doctrine\ORM\EntityRepository;
/**
* @template TypeEntity of AbstractAppEntity
* @extends EntityRepository<TypeEntity>
*/
abstract class AbstractAppRepository extends EntityRepository
{
/** @return class-string<TypeEntity> */
abstract protected function getEntityClass(): string;
/** @return TypeEntity[] */
public function findAllActive(): array
{
return $this->getEntityManager()->createQueryBuilder()
->select('entity')
->from($this->getEntityClass(), 'entity')
->where('entity.active = 1') // PHPSTAN ERROR: "AbstractAppEntity has no field or association named active"
->getQuery()
->getResult();
}
/** @return TypeEntity|null */
public function findByAltId(string $altId, bool $active = null): ?AbstractAppEntity
{
$qb = $this->getEntityManager()->createQueryBuilder()
->select('entity')
->from($this->getEntityClass(), 'entity')
->where('entity.altId = :altId') // PHPSTAN ERROR: "AbstractAppEntity has no field or association named altId"
->setParameter('altId', $altId);
if (null !== $active) {
$qb->andWhere('entity.active = :active')
->setParameter('active', $active);
}
return $qb->getQuery()->getOneOrNullResult();
}
/**
* @param string[] $ids
* @return TypeEntity[]
*/
public function findAltIds(array $altIds): array
{
return $this->getEntityManager()->createQueryBuilder()
->select('entity')
->from($this->getEntityClass(), 'entity')
->where('entity.altId NOT IN (:altIds)') // PHPSTAN ERROR: "AbstractAppEntity has no field or association named altId"
->andWhere('entity.active = 1')
->setParameter('altIds', $altIds)
->getQuery()
->getResult();
}
/**
* @param array{
* number : int,
* prefix : string|null,
* suffix : string|null,
* } $options
*/
public function findLatest(array $options): ?AbstractAppEntity
{
$qb = $this->getEntityManager()->createQueryBuilder()
->select('entity')
->from($this->getEntityClass(), 'entity')
->where('entity.number = :number') // PHPSTAN ERROR: "AbstractAppEntity has no field or association named number"
->setParameter('number', $options['number']);
foreach (['prefix', 'suffix'] as $option) {
if (! empty($options[$option])) {
$qb->andWhere("entity.$option = :$option")
->setParameter($option, $options[$option]);
}
}
$result = $qb->orderBy('entity.id', 'ASC')
->setMaxResults(1)
->getQuery()
->getResult();
return $result ? $result[0] : null;
}
/** @return ?TypeEntity */
public function findLatestActive(): ?AbstractAppEntity
{
$results = $this->getEntityManager()->createQueryBuilder()
->select('entity')
->from($this->getEntityClass(), 'entity')
->andWhere('entity.active = 1') // PHPSTAN ERROR: "AbstractAppEntity has no field or association named active"
->orderBy('entity.id', 'DESC')
->setMaxResults(1)
->getQuery()
->getResult();
return $results ? $results[0] : null;
}
...
}
use App\Common\Entity\AbstractCommonEntity;
use App\Common\Entity\CommonEntityInterface;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
* @ORM\MappedSuperclass(repositoryClass="App\Repository\AbstractAppRepository")
* @ORM\HasLifecycleCallbacks()
*/
abstract class AbstractAppEntity extends AbstractCommonEntity implements CommonEntityInterface
{
/**
* @Assert\NotBlank()
* @Assert\NotNull()
* @ORM\Column(name="active", type="boolean", nullable=false)
*/
protected bool $active; // PHPSTAN is wrong, the field exists.
/**
* @Assert\NotBlank()
* @Assert\NotNull()
* @ORM\Column(name="alt_id", type="string", nullable=false)
*/
protected string $altId; // PHPSTAN is wrong, the field exists.
/**
* @Assert\Range(min=1, max=10000)
* @ORM\Column(name="number", type="smallint", nullable=false)
*/
protected int $number; // PHPSTAN is wrong, the field exists.
/**
* @Assert\NotIdenticalTo("")
* @ORM\Column(name="prefix", type="string", nullable=true)
*/
protected ?string $prefix;
/**
* @Assert\NotIdenticalTo("")
* @ORM\Column(name="suffix", type="string", nullable=true)
*/
protected ?string $suffix;
public function getActive(): bool
{
return $this->active;
}
public function getAltId(): string
{
return $this->altId;
}
public function getNumber(): int
{
return $this->number;
}
public function getPrefix(): ?string
{
return $this->prefix;
}
public function getSuffix(): ?string
{
return $this->suffix;
}
public function setActive(bool $active): static
{
$this->active = $active;
return $this;
}
------ --------------------------------------------------------------------------------------------------------------------
Line tests/Default/Common/Controller/AbstractCommonControllerTestCase.php
------ --------------------------------------------------------------------------------------------------------------------
484 QueryBuilder: [Semantical Error] line 0, col 20 near 'id) FROM App\Common\Entity\AbstractCommonEntity': Error: Class
App\Common\Entity\AbstractCommonEntity has no field or association named id
------ --------------------------------------------------------------------------------------------------------------------
use App\Common\Entity\AbstractCommonEntity;
use App\Tests\Default\Common\AbstractWebTestCase;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\AbstractType;
/** @template TypeEntity of AbstractCommonEntity */
abstract class AbstractCommonControllerTestCase extends AbstractWebTestCase
{
...
/** @param class-string<AbstractCommonEntity> $entityClass */
protected function getEntityCount(string $entityClass): int
{
// PHPSTAN ERROR: "AbstractCommonEntity has no field or association named id"
return (int) $this->em->createQueryBuilder()->select('count(entity.id)')->from($entityClass, 'entity')->getQuery()->getSingleScalarResult();
}
...
}
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\MappedSuperclass
* @ORM\HasLifecycleCallbacks()
*/
abstract class AbstractCommonEntity
{
/**
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
* @ORM\Column(name="id", type="integer", nullable=false)
*/
protected int $id; // PHPSTAN is wrong, the field exists.
final public function getId(): int
{
return $this->id;
}
/** Use this to check if an instance is new, in which case it won't have a database id */
final public function isIdSet(): bool
{
return isset($this->id);
}
final public function unsetId(): static
{
unset($this->id);
return $this;
}
}
for some reason, upgrading doctrine/orm
from 2.19.0
to 3.1.0
caused this error to go away... despite causing errors elsewhere with doctrine.
I had to roll back that orm
change, but these errors mysteriously disappeared again. I was ignorning the errors yesterday. In an unrelated task, I deleted and reinstalled my vendor today, and not phpstan is reporting that there are no errors to ignore. So, I have no idea when/how they dissapeard (or what was wrong with my vendor directory, for that matter)
Hey, I'm sorry, but I don't see anything actionable I could use to fix these errors. If you find the time, please submit a failing test here.
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.
Bug report
Here is an example of what I am seeing. It is incorrect on all counts.
I don't know how to replicate this type of thing on phpstan.org given the Doctrine dependency. Let me know if you might have a suggestion.
Code snippet that reproduces the problem
No response
Expected output
no erros
Did PHPStan help you today? Did it make you happy in any way?
always :)