Skip to content

Commit 6b0d8c3

Browse files
Handle BcMath\Number operators for simple cases
Co-authored-by: Ondrej Mirtes <ondrej@mirtes.cz>
1 parent 2be8600 commit 6b0d8c3

File tree

6 files changed

+494
-6
lines changed

6 files changed

+494
-6
lines changed

conf/config.neon

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,6 +1053,10 @@ services:
10531053
arguments:
10541054
reportMagicProperties: %reportMagicProperties%
10551055
checkDynamicProperties: %checkDynamicProperties%
1056+
-
1057+
class: PHPStan\Type\Php\BcMathNumberOperatorTypeSpecifyingExtension
1058+
tags:
1059+
- phpstan.broker.operatorTypeSpecifyingExtension
10561060

10571061
-
10581062
class: PHPStan\Rules\Properties\UninitializedPropertyRule

src/Php/PhpVersion.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,4 +400,9 @@ public function substrReturnFalseInsteadOfEmptyString(): bool
400400
return $this->versionId < 80000;
401401
}
402402

403+
public function supportsBcMathNumberOperatorOverloading(): bool
404+
{
405+
return $this->versionId >= 80400;
406+
}
407+
403408
}

src/Reflection/InitializerExprTypeResolver.php

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,11 @@ public function getModType(Expr $left, Expr $right, callable $getTypeCallback):
872872
return $this->getNeverType($leftType, $rightType);
873873
}
874874

875+
$extensionSpecified = $this->callOperatorTypeSpecifyingExtensions(new BinaryOp\Mod($left, $right), $leftType, $rightType);
876+
if ($extensionSpecified !== null) {
877+
return $extensionSpecified;
878+
}
879+
875880
if ($leftType->toNumber() instanceof ErrorType || $rightType->toNumber() instanceof ErrorType) {
876881
return new ErrorType();
877882
}
@@ -1234,16 +1239,16 @@ public function getPowType(Expr $left, Expr $right, callable $getTypeCallback):
12341239
$leftType = $getTypeCallback($left);
12351240
$rightType = $getTypeCallback($right);
12361241

1237-
$exponentiatedTyped = $leftType->exponentiate($rightType);
1238-
if (!$exponentiatedTyped instanceof ErrorType) {
1239-
return $exponentiatedTyped;
1240-
}
1241-
12421242
$extensionSpecified = $this->callOperatorTypeSpecifyingExtensions(new BinaryOp\Pow($left, $right), $leftType, $rightType);
12431243
if ($extensionSpecified !== null) {
12441244
return $extensionSpecified;
12451245
}
12461246

1247+
$exponentiatedTyped = $leftType->exponentiate($rightType);
1248+
if (!$exponentiatedTyped instanceof ErrorType) {
1249+
return $exponentiatedTyped;
1250+
}
1251+
12471252
return new ErrorType();
12481253
}
12491254

src/Type/ObjectType.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
use PHPStan\Reflection\Type\UnresolvedPropertyPrototypeReflection;
3232
use PHPStan\ShouldNotHappenException;
3333
use PHPStan\TrinaryLogic;
34+
use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
35+
use PHPStan\Type\Accessory\AccessoryNumericStringType;
3436
use PHPStan\Type\Constant\ConstantArrayType;
3537
use PHPStan\Type\Constant\ConstantBooleanType;
3638
use PHPStan\Type\Constant\ConstantStringType;
@@ -593,6 +595,14 @@ public function toFloat(): Type
593595

594596
public function toString(): Type
595597
{
598+
if ($this->isInstanceOf('BcMath\Number')->yes()) {
599+
return new IntersectionType([
600+
new StringType(),
601+
new AccessoryNumericStringType(),
602+
new AccessoryNonEmptyStringType(),
603+
]);
604+
}
605+
596606
$classReflection = $this->getClassReflection();
597607
if ($classReflection === null) {
598608
return new ErrorType();
@@ -678,7 +688,10 @@ public function toCoercedArgumentType(bool $strictTypes): Type
678688

679689
public function toBoolean(): BooleanType
680690
{
681-
if ($this->isInstanceOf('SimpleXMLElement')->yes()) {
691+
if (
692+
$this->isInstanceOf('SimpleXMLElement')->yes()
693+
|| $this->isInstanceOf('BcMath\Number')->yes()
694+
) {
682695
return new BooleanType();
683696
}
684697

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Php;
4+
5+
use PHPStan\Php\PhpVersion;
6+
use PHPStan\Type\ErrorType;
7+
use PHPStan\Type\NeverType;
8+
use PHPStan\Type\ObjectType;
9+
use PHPStan\Type\OperatorTypeSpecifyingExtension;
10+
use PHPStan\Type\Type;
11+
use function in_array;
12+
13+
final class BcMathNumberOperatorTypeSpecifyingExtension implements OperatorTypeSpecifyingExtension
14+
{
15+
16+
public function __construct(private PhpVersion $phpVersion)
17+
{
18+
}
19+
20+
public function isOperatorSupported(string $operatorSigil, Type $leftSide, Type $rightSide): bool
21+
{
22+
if (!$this->phpVersion->supportsBcMathNumberOperatorOverloading() || $leftSide instanceof NeverType || $rightSide instanceof NeverType) {
23+
return false;
24+
}
25+
26+
$bcMathNumberType = new ObjectType('BcMath\Number');
27+
28+
return in_array($operatorSigil, ['-', '+', '*', '/', '**', '%'], true)
29+
&& (
30+
$bcMathNumberType->isSuperTypeOf($leftSide)->yes()
31+
|| $bcMathNumberType->isSuperTypeOf($rightSide)->yes()
32+
);
33+
}
34+
35+
public function specifyType(string $operatorSigil, Type $leftSide, Type $rightSide): Type
36+
{
37+
$bcMathNumberType = new ObjectType('BcMath\Number');
38+
$otherSide = $bcMathNumberType->isSuperTypeOf($leftSide)->yes()
39+
? $rightSide
40+
: $leftSide;
41+
42+
if (
43+
$otherSide->isInteger()->yes()
44+
|| $otherSide->isNumericString()->yes()
45+
|| $bcMathNumberType->isSuperTypeOf($otherSide)->yes()
46+
) {
47+
return $bcMathNumberType;
48+
}
49+
50+
return new ErrorType();
51+
}
52+
53+
}

0 commit comments

Comments
 (0)