Skip to content

Commit 35fec7a

Browse files
committed
Add documentation for trait inheritance and method aliasing; enhance examples for clarity
1 parent d8c3561 commit 35fec7a

1 file changed

Lines changed: 72 additions & 18 deletions

File tree

docs/advanced/liskov-and-inheritance.md

Lines changed: 72 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,9 @@ $model->setTraitId(-50);
205205
AppModel::setTraitVersion('');
206206
// Throws: TypeError: Property AppModel::$traitVersion must be of type non-empty-string
207207
```
208+
208209
---
210+
209211
## Trait Inheritance Across Parent-Child Classes
210212

211213
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:
@@ -242,6 +244,43 @@ $child->logMessage(10, 'boot');
242244
$child->logMessage(-50, 'boot');
243245
// Throws: TypeError: ChildService::logMessage(): Argument $level must be of type positive-int
244246
```
247+
248+
---
249+
250+
## Trait Method Aliasing (`use Trait { oldMethod as newMethod; }`)
251+
252+
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!
271+
}
272+
}
273+
274+
$service = new AuditService();
275+
276+
// Valid Call
277+
$service->recordAuditLog(1, 'audit_ok');
278+
279+
// Invalid Call ($level = -1 violates inherited Trait's @param positive-int)
280+
$service->recordAuditLog(-1, 'audit_ok');
281+
// Throws: TypeError: Argument $level must be of type positive-int
282+
```
283+
245284
---
246285

247286
## Partial Parameter Overriding (Gap-Filling)
@@ -292,42 +331,57 @@ $service->update(10, 'Charlie');
292331

293332
---
294333

295-
## Parameter Renaming ($id → $userId) & Position Shifts
334+
## Parameter Renaming ($id → $userId) & Position Shift Disambiguation
296335

297-
When a child class or 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.
298337

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!
301343

302344
```php
303-
class BaseField
345+
class BaseRegistry
304346
{
305347
/**
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
307352
*
308-
* @param string $type
309-
* @param bool|array{admin-api: bool} $api
353+
* @param array<string, string> $definitions
354+
* @param array<string, string> $repositoryMap
310355
*/
311-
public function __construct(string $type, bool|array $api = false) {}
356+
public function __construct(
357+
ContainerInterface $container,
358+
array $definitions,
359+
array $repositoryMap
360+
) {}
312361
}
313362

314-
class OneToManyRelation extends BaseField
363+
class SalesChannelRegistry extends BaseRegistry
315364
{
316365
/**
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!
368+
*
369+
* @param array<string, string> $definitionMap
370+
* @param array<string, string> $repositoryMap
318371
*/
319372
public function __construct(
320-
string $entity,
321-
string $ref,
322-
OnDeleteOption $onDelete = OnDeleteOption::NO_ACTION,
323-
bool|array $api = false
373+
string $prefix,
374+
ContainerInterface $container,
375+
array $definitionMap,
376+
array $repositoryMap
324377
) {
325-
parent::__construct('one-to-many', $api);
378+
parent::__construct($container, $definitionMap, $repositoryMap);
326379
}
327380
}
328381

329-
// $onDelete (position #2 in child) is NOT overwritten by $api's type (position #1 in parent)!
330-
$attr = new OneToManyRelation('unit', 'unit_id', OnDeleteOption::CASCADE, true);
382+
// TypePHP correctly keeps $container (Index 1 in child) untouched,
383+
// rather than mis-mapping parent's @param array $definitions (Index 1 in parent) onto it!
384+
new SalesChannelRegistry('sales_channel.', new Container(), ['prod' => 'ProductDef'], ['prod' => 'ProductRepo']);
331385
```
332386

333387
---

0 commit comments

Comments
 (0)