Skip to content

Commit 4ce9d14

Browse files
committed
added Forms\Container::getComponent() and $form['…'] return type narrowing based on addXxx() calls
1 parent a706950 commit 4ce9d14

6 files changed

Lines changed: 705 additions & 2 deletions

File tree

CLAUDE.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ vendor/bin/tester tests/SomeTest.phpt -s # Run a single test
2020
- **`src/Tester/TypeAssert.php`** — Reusable type inference testing helper for Nette Tester (used by other Nette packages)
2121
- **`extension.neon`** — Entry point, includes `extension-php.neon` and `extension-nette.neon`, auto-included by `phpstan/extension-installer`
2222
- **`extension-php.neon`** — Generic PHP-level extensions (RemoveFailingReturnType, ClosureTypeCheckIgnore)
23-
- **`extension-nette.neon`** — All Nette package extensions (component-model, schema, tester, utils), separated by comments
23+
- **`extension-nette.neon`** — All Nette package extensions (component-model, forms, schema, tester, utils), separated by comments
2424
- **`phpstan.neon`** — Self-analysis config (level 8, analyses `src/` and `tests/`)
2525

2626
### How extensions are registered
@@ -73,6 +73,10 @@ Extensions for specific Nette packages use dedicated namespaces: `Nette\PHPStan\
7373

7474
`GetComponentReturnTypeExtension` (`DynamicMethodReturnTypeExtension`) narrows return types of `Container::getComponent()` and `Container::offsetGet()` (i.e. `$this['xxx']`). When the component name is a constant string, it looks for a `createComponent<Name>()` factory method on the caller type and returns its return type — e.g. `$this->getComponent('poll')` returns `PollControl` if `createComponentPoll(): PollControl` exists. Falls back to the declared return type when no factory method is found. Config: `extension-nette.neon`.
7575

76+
### FormContainerReturnTypeExtension
77+
78+
`FormContainerReturnTypeExtension` (`DynamicMethodReturnTypeExtension`) narrows return types of `Forms\Container::getComponent()` and `::offsetGet()` (i.e. `$form['xxx']`) based on `addXxx()` calls in the same function body. When the component name is a constant string, it parses the current file, finds the enclosing function/method, and walks the AST looking for `$form->addText('name')`, `$form->addSelect('name')`, etc. on the same variable. Returns the `addXxx` method's declared return type — e.g. `$form['name']` returns `TextInput` after `$form->addText('name', ...)`. Falls back to `createComponent*()` factory lookup. Only matches simple variable names (not complex expressions). Config: `extension-nette.neon`.
79+
7680
### AssertTypeNarrowingExtension
7781

7882
`AssertTypeNarrowingExtension` (`StaticMethodTypeSpecifyingExtension` + `TypeSpecifierAwareExtension`) narrows variable types after `Tester\Assert` assertion calls. Each assertion method is mapped to an equivalent PHP expression that PHPStan already understands, then delegated to `TypeSpecifier::specifyTypesInCondition()`. Supported methods: `null`, `notNull`, `true`, `false`, `truthy`, `falsey`, `same`, `notSame`, and `type` (with built-in type strings like `'string'`, `'int'`, etc. and class/interface names). Config: `extension-nette.neon`.

extension-nette.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ services:
1313
create: Nette\PHPStan\ComponentModel\GetComponentReturnTypeExtension
1414
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
1515

16+
# nette/forms
17+
-
18+
create: Nette\PHPStan\Forms\FormContainerReturnTypeExtension(@defaultAnalysisParser)
19+
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
20+
1621
# nette/schema
1722
-
1823
create: Nette\PHPStan\Schema\ExpectArrayReturnTypeExtension

readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ includes:
4141

4242
<!---->
4343

44-
**Precise return types** — narrows return types of `Strings::match()`, `matchAll()`, `split()`, `Helpers::falseToNull()`, `Expect::array()`, `Arrays::invoke()`, and `Arrays::invokeMethod()` based on the arguments you pass. Also narrows `Container::getComponent()` and `$container['...']` to match the corresponding `createComponent*()` factory return type.
44+
**Precise return types** — narrows return types of `Strings::match()`, `matchAll()`, `split()`, `Helpers::falseToNull()`, `Expect::array()`, `Arrays::invoke()`, and `Arrays::invokeMethod()` based on the arguments you pass. Also narrows `Container::getComponent()` and `$container['...']` to match the corresponding `createComponent*()` factory return type. For forms, `$form['name']` returns the specific control type (e.g. `TextInput`, `SelectBox`) based on the `addText()`, `addSelect()`, etc. call in the same function.
4545

4646
**Removes `|false` and `|null` from PHP functions** — many native functions like `getcwd`, `json_encode`, `preg_split`, `preg_replace`, and [many more](extension-php.neon) include `false` or `null` in their return type even though these error values are unrealistic on modern systems.
4747

0 commit comments

Comments
 (0)