|
4 | 4 |
|
5 | 5 | namespace Rector\NodeManipulator; |
6 | 6 |
|
| 7 | +use PhpParser\Modifiers; |
7 | 8 | use PhpParser\Node\Arg; |
8 | 9 | use PhpParser\Node\Expr\Assign; |
9 | 10 | use PhpParser\Node\Expr\StaticCall; |
10 | 11 | use PhpParser\Node\Expr\Variable; |
11 | 12 | use PhpParser\Node\Name; |
| 13 | +use PhpParser\Node\Param; |
12 | 14 | use PhpParser\Node\Stmt; |
13 | 15 | use PhpParser\Node\Stmt\Class_; |
14 | 16 | use PhpParser\Node\Stmt\ClassLike; |
@@ -248,13 +250,31 @@ private function addPromotedProperty( |
248 | 250 | $param = $this->nodeFactory->createPromotedPropertyParam($propertyMetadata); |
249 | 251 |
|
250 | 252 | if ($constructClassMethod instanceof ClassMethod) { |
251 | | - // parameter is already added |
252 | | - if ($this->hasMethodParameter($constructClassMethod, $propertyMetadata->getName())) { |
| 253 | + $hasOwnConstruct = $class->getMethod(MethodName::CONSTRUCT) instanceof ClassMethod; |
| 254 | + $matchedParam = $this->matchMethodParameter($constructClassMethod, $propertyMetadata->getName()); |
| 255 | + |
| 256 | + if ($matchedParam instanceof Param) { |
| 257 | + // own constructor already has this param → nothing to do |
| 258 | + if ($hasOwnConstruct) { |
| 259 | + return; |
| 260 | + } |
| 261 | + |
| 262 | + // parent constructor has a same-named param; if it is a private promoted |
| 263 | + // property, the child cannot access it via $this — add a fresh constructor |
| 264 | + // on the child instead of trying to extend the parent signature |
| 265 | + if (($matchedParam->flags & Modifiers::PRIVATE) !== 0) { |
| 266 | + $childConstructClassMethod = $this->nodeFactory->createPublicMethod(MethodName::CONSTRUCT); |
| 267 | + $childConstructClassMethod->params[] = $param; |
| 268 | + $this->classInsertManipulator->addAsFirstMethod($class, $childConstructClassMethod); |
| 269 | + return; |
| 270 | + } |
| 271 | + |
| 272 | + // parent's matching param is protected/public — accessible from child, no add needed |
253 | 273 | return; |
254 | 274 | } |
255 | 275 |
|
256 | 276 | // found construct, but only on parent, add to current class |
257 | | - if (! $class->getMethod(MethodName::CONSTRUCT) instanceof ClassMethod) { |
| 277 | + if (! $hasOwnConstruct) { |
258 | 278 | $parentArgs = []; |
259 | 279 |
|
260 | 280 | foreach ($constructClassMethod->params as $originalParam) { |
@@ -334,15 +354,15 @@ private function hasClassPropertyAndDependency(Class_ $class, PropertyMetadata $ |
334 | 354 | return $property instanceof Property; |
335 | 355 | } |
336 | 356 |
|
337 | | - private function hasMethodParameter(ClassMethod $classMethod, string $name): bool |
| 357 | + private function matchMethodParameter(ClassMethod $classMethod, string $name): ?Param |
338 | 358 | { |
339 | 359 | foreach ($classMethod->params as $param) { |
340 | 360 | if ($this->nodeNameResolver->isName($param->var, $name)) { |
341 | | - return true; |
| 361 | + return $param; |
342 | 362 | } |
343 | 363 | } |
344 | 364 |
|
345 | | - return false; |
| 365 | + return null; |
346 | 366 | } |
347 | 367 |
|
348 | 368 | private function shouldAddPromotedProperty(Class_ $class, PropertyMetadata $propertyMetadata): bool |
|
0 commit comments