You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/advanced/liskov-and-inheritance.md
+72-18Lines changed: 72 additions & 18 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -205,7 +205,9 @@ $model->setTraitId(-50);
205
205
AppModel::setTraitVersion('');
206
206
// Throws: TypeError: Property AppModel::$traitVersion must be of type non-empty-string
207
207
```
208
+
208
209
---
210
+
209
211
## Trait Inheritance Across Parent-Child Classes
210
212
211
213
When a parent class uses a Trait (`ParentClass` uses `LoggerTrait`), any child class extending the parent (`ChildClass extends ParentClass`) automatically inherits all `@param`, `@return`, and `@var` contracts declared on the parent's Trait:
When a class uses a Trait and renames a method using PHP's trait `as` alias syntax, TypePHP inspects trait alias mappings and automatically inherits the original Trait method's DocBlock contracts onto the aliased method:
253
+
254
+
```php
255
+
trait LoggerTrait
256
+
{
257
+
/**
258
+
* @param positive-int $level
259
+
* @param non-empty-string $message
260
+
*/
261
+
public function logEvent(int $level, string $message): bool
262
+
{
263
+
return true;
264
+
}
265
+
}
266
+
267
+
class AuditService
268
+
{
269
+
use LoggerTrait {
270
+
logEvent as recordAuditLog; // Aliases method from trait!
## Parameter Renaming ($id → $userId) & Position Shifts
334
+
## Parameter Renaming ($id → $userId) & Position Shift Disambiguation
296
335
297
-
When a child classor attribute constructor overrides a parent method, parameter positions or parameter names may shift. TypePHP resolves parameter contract inheritance using **Name-First Resolution**:
336
+
When a child class, constructor, or trait implementation overrides an ancestor method, parameter positions may shift when new parameters are inserted, or parameter names may be renamed.
298
337
299
-
1.**Name Matching:** If a parameter name in the child method matches a parameter name in the parent class (e.g. `$api`), the parent's contract is inherited by that parameter regardless of its position index in the child.
300
-
2.**Position Fallback:** If a parameter is renamed in the child class (e.g., `$id` $\rightarrow$ `$userId`), TypePHP falls back to matching by position index.
338
+
TypePHP resolves parameter contract inheritance using **3-Tier Name & Position Disambiguation**:
339
+
340
+
1.**Name-First Matching:** If a parameter name in the child method matches a parameter name in the parent class (e.g. `$container`), the parent's contract is mapped to that parameter regardless of its position index in the child.
341
+
2.**Position Fallback on Renamed Parameters:** If a parameter is renamed in the child class (e.g., `$id` $\rightarrow$ `$userId`), TypePHP maps the contract using its position index.
342
+
3.**Candidate Disambiguation (Shift Protection):** If a child class inserts a new parameter at index 0 (shifting all subsequent parameters down), TypePHP **verifies that the candidate child parameter does not already exist in the parent under its own name**. This prevents parent parameter contracts from accidentally mis-mapping onto shifted child parameters!
301
343
302
344
```php
303
-
class BaseField
345
+
class BaseRegistry
304
346
{
305
347
/**
306
-
* Parent constructor has $api at position #1
348
+
* Parent constructor has 3 params:
349
+
* Index 0: $container
350
+
* Index 1: $definitions
351
+
* Index 2: $repositoryMap
307
352
*
308
-
* @param string $type
309
-
* @param bool|array{admin-api: bool} $api
353
+
* @param array<string,string> $definitions
354
+
* @param array<string,string> $repositoryMap
310
355
*/
311
-
public function __construct(string $type, bool|array $api = false) {}
356
+
public function __construct(
357
+
ContainerInterface $container,
358
+
array $definitions,
359
+
array $repositoryMap
360
+
) {}
312
361
}
313
362
314
-
class OneToManyRelation extends BaseField
363
+
class SalesChannelRegistry extends BaseRegistry
315
364
{
316
365
/**
317
-
* Child inserts $entity, $ref, $onDelete BEFORE $api (position shift!)
366
+
* Child inserts $prefix at Index 0 (shifting $container to Index 1),
367
+
* and renames $definitions -> $definitionMap at Index 2!
0 commit comments