ADR 017 · Accepted
Safe Type Casting via SafeCastTrait
Context
Processing untyped data from JSON API responses, form submissions, and configuration arrays requires casting mixed values to specific scalar types. At PHPStan level 10, direct casts like (string)$mixed trigger "Cannot cast mixed to string" errors. Each usage site would need inline type guards, leading to repetitive boilerplate.
Problem statement
PHPStan level 10 strictness: (string)$data['key'] is forbidden on mixed.
Verbose alternatives: is_string($v) ? $v : (is_numeric($v) ? (string)$v : '') at every call site.
Inconsistent defaults: Different code paths used different fallback values.
Suppression temptation: Teams resort to @phpstan-ignore instead of proper narrowing.
Decision
Extract a reusable SafeCastTrait with three static methods that handle mixed input with sensible defaults and no PHPStan suppressions:
Design choices:
Static methods -- No instance state needed; enables self::toStr() calls.
Private visibility -- Implementation detail of the using class, not public API.
Numeric passthrough -- is_numeric() covers int, float, and numeric strings.
Empty-string default -- Safer than null for string contexts (concatenation, comparison).
Zero default for int/float -- Neutral value for arithmetic operations.
Complements the ResponseParserTrait in Classes/Provider/ which serves a similar purpose for provider API response arrays but with key-based access (getString($data, 'key')). SafeCastTrait handles standalone values.
Usage in WizardGeneratorService:
Consequences
Positive:
●● PHPStan level 10 compliance without any @phpstan-ignore suppressions.
● Consistent fallback behavior across all consumers.
● Three-line methods are trivially testable and auditable.
◐ Reduces boilerplate by ~5 lines per cast site.
Negative:
◑ Trait usage adds an indirect dependency (mitigated by being a small utility).
◑ is_numeric() accepts numeric strings like "1e2" which may surprise.
Net Score: +4.5 (Positive)
Files changed
Added:
Classes/Utility/SafeCastTrait.php
Modified (consumers):
Classes/Service/WizardGeneratorService.php -- Uses SafeCastTrait for JSON normalization.
Classes/Controller/Backend/TaskWizardController.php -- Uses SafeCastTrait for form data casting (the monolithic TaskController was split per ADR-027).