|
8 | 8 | use PHPStan\Reflection\ParametersAcceptorSelector;
|
9 | 9 | use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
|
10 | 10 | use PHPStan\Type\Accessory\AccessoryNonFalsyStringType;
|
| 11 | +use PHPStan\Type\Constant\ConstantArrayTypeBuilder; |
11 | 12 | use PHPStan\Type\DynamicFunctionReturnTypeExtension;
|
12 | 13 | use PHPStan\Type\IntersectionType;
|
13 | 14 | use PHPStan\Type\MixedType;
|
|
17 | 18 | use PHPStan\Type\TypeUtils;
|
18 | 19 | use function array_key_exists;
|
19 | 20 | use function count;
|
| 21 | +use function in_array; |
20 | 22 |
|
21 | 23 | final class ReplaceFunctionsDynamicReturnTypeExtension implements DynamicFunctionReturnTypeExtension
|
22 | 24 | {
|
@@ -101,9 +103,30 @@ private function getPreliminarilyResolvedTypeFromFunctionCall(
|
101 | 103 | if ($compareSuperTypes === $isStringSuperType) {
|
102 | 104 | return new StringType();
|
103 | 105 | } elseif ($compareSuperTypes === $isArraySuperType) {
|
104 |
| - if (count($subjectArgumentType->getArrays()) > 0) { |
| 106 | + $subjectArrays = $subjectArgumentType->getArrays(); |
| 107 | + if (count($subjectArrays) > 0) { |
105 | 108 | $result = [];
|
106 |
| - foreach ($subjectArgumentType->getArrays() as $arrayType) { |
| 109 | + foreach ($subjectArrays as $arrayType) { |
| 110 | + $constantArrays = $arrayType->getConstantArrays(); |
| 111 | + |
| 112 | + if ( |
| 113 | + $constantArrays !== [] |
| 114 | + && in_array($functionReflection->getName(), ['preg_replace', 'preg_replace_callback', 'preg_replace_callback_array'], true) |
| 115 | + ) { |
| 116 | + foreach ($constantArrays as $constantArray) { |
| 117 | + $generalizedArray = $constantArray->generalizeValues(); |
| 118 | + |
| 119 | + $builder = ConstantArrayTypeBuilder::createEmpty(); |
| 120 | + // turn all keys optional |
| 121 | + foreach ($constantArray->getKeyTypes() as $keyType) { |
| 122 | + $builder->setOffsetValueType($keyType, $generalizedArray->getOffsetValueType($keyType), true); |
| 123 | + } |
| 124 | + $result[] = $builder->getArray(); |
| 125 | + } |
| 126 | + |
| 127 | + continue; |
| 128 | + } |
| 129 | + |
107 | 130 | $result[] = $arrayType->generalizeValues();
|
108 | 131 | }
|
109 | 132 |
|
@@ -134,6 +157,20 @@ private function canReturnNull(
|
134 | 157 | Scope $scope,
|
135 | 158 | ): bool
|
136 | 159 | {
|
| 160 | + if ( |
| 161 | + in_array($functionReflection->getName(), ['preg_replace', 'preg_replace_callback', 'preg_replace_callback_array'], true) |
| 162 | + && count($functionCall->getArgs()) > 0 |
| 163 | + ) { |
| 164 | + $subjectArgumentType = $this->getSubjectType($functionReflection, $functionCall, $scope); |
| 165 | + |
| 166 | + if ( |
| 167 | + $subjectArgumentType !== null |
| 168 | + && $subjectArgumentType->isArray()->yes() |
| 169 | + ) { |
| 170 | + return false; |
| 171 | + } |
| 172 | + } |
| 173 | + |
137 | 174 | $possibleTypes = ParametersAcceptorSelector::selectFromArgs(
|
138 | 175 | $scope,
|
139 | 176 | $functionCall->getArgs(),
|
|
0 commit comments