Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Strict comparison using === between 0 and int<1, max> will always evaluate to false. #2889

Closed
lukepass opened this issue Jan 22, 2020 · 11 comments

Comments

@lukepass
Copy link

Bug report

Hello, phpstan is reporting a false positive here:

/** @var User */
$user = $this->getUser();
$em   = $this->getDoctrine()->getManager();

/** @var null|UserProduct */
$userProduct = $em->getRepository(UserProduct::class)->findOneByUserAndProduct($user, $product);

if (!$userProduct || $userProduct->getCount() < 1) {
    throw new BadRequestHttpException();
}

// decreases the products
$userProduct->setCount($userProduct->getCount() - 1); // <--- here I am decreasing!
if (0 === $userProduct->getCount()) {
    $em->remove($userProduct);
}

The error is this:

Strict comparison using === between 0 and int<1, max> will always evaluate to false.

And the check is if (0 === $userProduct->getCount()). It's correct because I am decreasing the value.

Code snippet that reproduces the problem

Expected output

@mergeable
Copy link

mergeable bot commented Jan 22, 2020

This bug report is missing a link to reproduction on phpstan.org.
It will most likely be closed after manual review.

@ondrejmirtes
Copy link
Member

Funny enough, if you make your UserProduct::setCount() method return void, it will work :) In that case PHPStan knows it's calling a method with a side-effect, so it will reset memorized value from getCount().

Otherwise you can work around it using local variables:

/** @var User */
$user = $this->getUser();
$em   = $this->getDoctrine()->getManager();

/** @var null|UserProduct */
$userProduct = $em->getRepository(UserProduct::class)->findOneByUserAndProduct($user, $product);

if (!$userProduct) {
    throw new BadRequestHttpException();
}

$count = $userProduct->getCount();
if ($count < 1) {
    throw new BadRequestHttpException();
}

// decreases the products
$userProduct->setCount($userProduct->getCount() - 1); // <--- here I am decreasing!
if (0 === $userProduct->getCount()) {
    $em->remove($userProduct);
}

@ossinkine
Copy link

@ondrejmirtes Why is this issue closed? You suggest a workaround but the issue still exists. Why the PHPStan memorizes the method result?

@ondrejmirtes
Copy link
Member

It memorizes it so it could detect:

assert($userProduct->getCount() === 0);
assert($userProduct->getCount() === 0); // always true

I'm aware of this issue, it's definitely a duplicate but I didn't spend the time looking for the issue ID.

@ossinkine
Copy link

It depends on getCount method implementation. For example, the following implementation returns difeerent values:

function getCount(): int
{
    static $count;
    return ++$count;
}

There is no rules which declare the some methods must be idempotent, right?

@ondrejmirtes
Copy link
Member

Currently not. All methods are memorized, except for void methods. Void methods also invalidate previously memorized calls.

@ossinkine
Copy link

So is there the way to disable this behavior? BTW void method does not invalidate the memorized calls: https://phpstan.org/r/51fd580d-ac38-4e82-ad22-a65c407da902

@ossinkine
Copy link

#1157 is this related issue?

@ondrejmirtes
Copy link
Member

All of these issues are related:

phpstan/phpstan-phpunit#25

#1157

#1745

#2209

#2349

#2403

#2288

@dktapps
Copy link
Contributor

dktapps commented Jan 31, 2020

This is something of a pest to be honest, I've run into this multiple times in various forms. The assumption that a method with a return value is pure is unreliable in many cases.

@lock
Copy link

lock bot commented Mar 10, 2020

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.

@lock lock bot locked as resolved and limited conversation to collaborators Mar 10, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants