From 913c3e54de4fcedc2638ff727395a57d2b6997fa Mon Sep 17 00:00:00 2001 From: Sebastian Mendel Date: Thu, 16 Jul 2026 21:47:50 +0200 Subject: [PATCH 1/2] docs(php-modernization): strict-rule bans (short-ternary, @-suppression, mixed cast) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a "Strict-Rule Bans" subsection to phpstan-compliance.md: the compliant rewrites for three idioms level 9/10 + strict rules (phpstan-strict-rules / ergebnis) reject — short ternary ?:, the @ operator, and (string) $mixed — plus the reminder to run the analyzer after adding tests (tests are analyzed too). Signed-off-by: Sebastian Mendel --- .../references/phpstan-compliance.md | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/skills/php-modernization/references/phpstan-compliance.md b/skills/php-modernization/references/phpstan-compliance.md index e225bb3..d00b508 100644 --- a/skills/php-modernization/references/phpstan-compliance.md +++ b/skills/php-modernization/references/phpstan-compliance.md @@ -172,6 +172,37 @@ 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); // ✗ +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); // ✓ +$s = \is_string($raw) ? $raw : ''; +``` + +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 From 487e139816fa526c04b100a64b662539ec5d0670 Mon Sep 17 00:00:00 2001 From: Sebastian Mendel Date: Thu, 16 Jul 2026 22:26:21 +0200 Subject: [PATCH 2/2] docs(php-modernization): init $h before try; note is_scalar alternative (review) Signed-off-by: Sebastian Mendel --- skills/php-modernization/references/phpstan-compliance.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/skills/php-modernization/references/phpstan-compliance.md b/skills/php-modernization/references/phpstan-compliance.md index d00b508..774cf26 100644 --- a/skills/php-modernization/references/phpstan-compliance.md +++ b/skills/php-modernization/references/phpstan-compliance.md @@ -188,15 +188,16 @@ if ($rows === false) { $rows = []; } // 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); // ✗ -try { $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); // ✓ -$s = \is_string($raw) ? $raw : ''; +$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