diff --git a/skills/php-modernization/references/phpstan-compliance.md b/skills/php-modernization/references/phpstan-compliance.md index e225bb3..774cf26 100644 --- a/skills/php-modernization/references/phpstan-compliance.md +++ b/skills/php-modernization/references/phpstan-compliance.md @@ -172,6 +172,38 @@ public function getItems(): array public function getConfig(): array ``` +### Strict-Rule Bans: short-ternary, `@`-suppression, `(string) $mixed` + +Level 9/10 plus a strict ruleset (`phpstan-strict-rules`, or the `ergebnis` +custom rules) reject three idioms that pass at lower levels. New code trips them +repeatedly; the compliant rewrites: + +```php +// ternary.shortNotAllowed — the short ternary is banned: +$rows = glob($p) ?: []; // ✗ +$rows = glob($p); // ✓ +if ($rows === false) { $rows = []; } + +// ergebnis.noErrorSuppression — the "@" operator is banned. Wrap the +// warning-emitter and handle the failure explicitly (also catches the case +// where an error handler turns the warning into an exception): +$h = @proc_open($cmd, $spec, $pipes); // ✗ +$h = false; // ✓ init first, so the check is safe if proc_open throws +try { $h = proc_open($cmd, $spec, $pipes); } +catch (\Throwable $e) { /* degrade */ } +if (!\is_resource($h)) { /* degrade */ } +// For unlink/file ops, guard instead of suppress: if (is_file($f)) { unlink($f); } + +// cast.string — casting a mixed to string is unsafe (it could be an array): +$s = (string) $config->get($key); // ✗ +$raw = $config->get($key); // ✓ string-only intent (config keys); +$s = \is_string($raw) ? $raw : ''; // use is_scalar($raw) ? (string) $raw : '' to keep int/bool +``` + +Run the static analyzer **after** adding the test files, not before — tests are +analyzed too, so a `(string) $mixed` cast or an offset-on-possibly-empty in a +fresh test file fails CI even though the pre-test run was green. + ## Type Aliases and Custom Types ### Defining Type Aliases