|
3 | 3 | declare (strict_types=1); |
4 | 4 | namespace Rector\NodeManipulator; |
5 | 5 |
|
| 6 | +use PhpParser\Modifiers; |
6 | 7 | use PhpParser\Node\Arg; |
7 | 8 | use PhpParser\Node\Expr\Assign; |
8 | 9 | use PhpParser\Node\Expr\StaticCall; |
9 | 10 | use PhpParser\Node\Expr\Variable; |
10 | 11 | use PhpParser\Node\Name; |
| 12 | +use PhpParser\Node\Param; |
11 | 13 | use PhpParser\Node\Stmt; |
12 | 14 | use PhpParser\Node\Stmt\Class_; |
13 | 15 | use PhpParser\Node\Stmt\ClassLike; |
@@ -200,12 +202,27 @@ private function addPromotedProperty(Class_ $class, PropertyMetadata $propertyMe |
200 | 202 | { |
201 | 203 | $param = $this->nodeFactory->createPromotedPropertyParam($propertyMetadata); |
202 | 204 | if ($constructClassMethod instanceof ClassMethod) { |
203 | | - // parameter is already added |
204 | | - if ($this->hasMethodParameter($constructClassMethod, $propertyMetadata->getName())) { |
| 205 | + $hasOwnConstruct = $class->getMethod(MethodName::CONSTRUCT) instanceof ClassMethod; |
| 206 | + $matchedParam = $this->matchMethodParameter($constructClassMethod, $propertyMetadata->getName()); |
| 207 | + if ($matchedParam instanceof Param) { |
| 208 | + // own constructor already has this param → nothing to do |
| 209 | + if ($hasOwnConstruct) { |
| 210 | + return; |
| 211 | + } |
| 212 | + // parent constructor has a same-named param; if it is a private promoted |
| 213 | + // property, the child cannot access it via $this — add a fresh constructor |
| 214 | + // on the child instead of trying to extend the parent signature |
| 215 | + if (($matchedParam->flags & Modifiers::PRIVATE) !== 0) { |
| 216 | + $childConstructClassMethod = $this->nodeFactory->createPublicMethod(MethodName::CONSTRUCT); |
| 217 | + $childConstructClassMethod->params[] = $param; |
| 218 | + $this->classInsertManipulator->addAsFirstMethod($class, $childConstructClassMethod); |
| 219 | + return; |
| 220 | + } |
| 221 | + // parent's matching param is protected/public — accessible from child, no add needed |
205 | 222 | return; |
206 | 223 | } |
207 | 224 | // found construct, but only on parent, add to current class |
208 | | - if (!$class->getMethod(MethodName::CONSTRUCT) instanceof ClassMethod) { |
| 225 | + if (!$hasOwnConstruct) { |
209 | 226 | $parentArgs = []; |
210 | 227 | foreach ($constructClassMethod->params as $originalParam) { |
211 | 228 | $parentArgs[] = new Arg(new Variable((string) $this->nodeNameResolver->getName($originalParam->var))); |
@@ -265,14 +282,14 @@ private function hasClassPropertyAndDependency(Class_ $class, PropertyMetadata $ |
265 | 282 | // is inject/autowired property? |
266 | 283 | return $property instanceof Property; |
267 | 284 | } |
268 | | - private function hasMethodParameter(ClassMethod $classMethod, string $name): bool |
| 285 | + private function matchMethodParameter(ClassMethod $classMethod, string $name): ?Param |
269 | 286 | { |
270 | 287 | foreach ($classMethod->params as $param) { |
271 | 288 | if ($this->nodeNameResolver->isName($param->var, $name)) { |
272 | | - return \true; |
| 289 | + return $param; |
273 | 290 | } |
274 | 291 | } |
275 | | - return \false; |
| 292 | + return null; |
276 | 293 | } |
277 | 294 | private function shouldAddPromotedProperty(Class_ $class, PropertyMetadata $propertyMetadata): bool |
278 | 295 | { |
|
0 commit comments