PHP Modernization Skill
Modernize PHP applications to PHP 8.x with type safety, PSR compliance, and static analysis.
Expertise Areas PHP 8.x: Constructor promotion, readonly, enums, match, attributes, union types PSR/PER Compliance: Active PHP-FIG standards Static Analysis: PHPStan (level 9+), PHPat, Rector, PHP-CS-Fixer Type Safety: DTOs/VOs over arrays, generics via PHPDoc PHP 8.x Features Constructor Property Promotion (PHP 8.0+) // ❌ OLD class UserService { private UserRepository $userRepository; private LoggerInterface $logger;
public function __construct(
UserRepository $userRepository,
LoggerInterface $logger
) {
$this->userRepository = $userRepository;
$this->logger = $logger;
}
}
// ✅ NEW final class UserService { public function __construct( private readonly UserRepository $userRepository, private readonly LoggerInterface $logger, ) {} }
Readonly Classes (PHP 8.2+) // ✅ All properties are implicitly readonly final readonly class UserDTO { public function __construct( public int $id, public string $name, public string $email, ) {} }
Enums (PHP 8.1+) // ❌ OLD - String constants class Status { public const DRAFT = 'draft'; public const PUBLISHED = 'published'; public const ARCHIVED = 'archived'; }
// ✅ NEW - Backed enum enum Status: string { case Draft = 'draft'; case Published = 'published'; case Archived = 'archived';
public function label(): string
{
return match($this) {
self::Draft => 'Draft',
self::Published => 'Published',
self::Archived => 'Archived',
};
}
}
// Usage public function setStatus(Status $status): void { $this->status = $status; }
$item->setStatus(Status::Published);
Match Expression (PHP 8.0+) // ❌ OLD - Switch switch ($type) { case 'a': $result = 'Type A'; break; case 'b': $result = 'Type B'; break; default: $result = 'Unknown'; }
// ✅ NEW - Match $result = match($type) { 'a' => 'Type A', 'b' => 'Type B', default => 'Unknown', };
Named Arguments (PHP 8.0+) // ✅ Clearer and order-independent $this->doSomething( name: 'value', options: ['key' => 'value'], enabled: true, );
Null Safe Operator (PHP 8.0+) // ❌ OLD $country = null; if ($user !== null && $user->getAddress() !== null) { $country = $user->getAddress()->getCountry(); }
// ✅ NEW $country = $user?->getAddress()?->getCountry();
Union Types (PHP 8.0+) public function process(string|int $value): string|null { // ... }
Intersection Types (PHP 8.1+) public function handle(Countable&Iterator $collection): void { // $collection must implement both interfaces }
Attributes (PHP 8.0+) use TYPO3\CMS\Core\Attribute\AsEventListener;
[AsEventListener(identifier: 'myext/my-listener')]
final class MyListener { public function __invoke(SomeEvent $event): void { // Handle event } }
DTOs and Value Objects Never Use Arrays for Structured Data // ❌ BAD - Array passing public function createUser(array $data): array { // What fields are expected? What types? }
// ✅ GOOD - DTO pattern public function createUser(CreateUserDTO $dto): UserDTO { // Type-safe, documented, IDE-friendly }
Data Transfer Object
value === $other->value; } public function __toString(): string { return $this->value; } } PSR/PER Compliance Active Standards Standard Purpose Status PSR-1 Basic Coding Required PSR-4 Autoloading Required PER CS Coding Style Required (supersedes PSR-12) PSR-3 Logger Interface Use for logging PSR-6/16 Cache Use for caching PSR-7/17/18 HTTP Use for HTTP clients PSR-11 Container Use for DI PSR-14 Events Use for event dispatching PSR-15 Middleware Use for HTTP middleware PSR-20 Clock Use for time-dependent code PER Coding Style $param1, 2 => $param1 . $param1, default => '', }; } } Static Analysis Tools PHPStan (Level 9+) # phpstan.neon includes: - vendor/phpstan/phpstan-strict-rules/rules.neon - vendor/saschaegerer/phpstan-typo3/extension.neon parameters: level: 10 paths: - Classes - Tests excludePaths: - Classes/Domain/Model/* Level Guide: Level 0-5: Basic checks Level 6-8: Type checking Level 9: Strict mixed handling Level 10: Maximum strictness (recommended) PHP-CS-Fixer setRules([ '@PER-CS' => true, '@PER-CS:risky' => true, 'declare_strict_types' => true, 'no_unused_imports' => true, 'ordered_imports' => ['sort_algorithm' => 'alpha'], 'single_line_empty_body' => true, 'trailing_comma_in_multiline' => [ 'elements' => ['arguments', 'arrays', 'match', 'parameters'], ], ]) ->setRiskyAllowed(true) ->setFinder( PhpCsFixer\Finder::create() ->in(__DIR__ . '/Classes') ->in(__DIR__ . '/Tests') ); Rector withPaths([ __DIR__ . '/Classes', __DIR__ . '/Tests', ]) ->withSets([ LevelSetList::UP_TO_PHP_83, SetList::CODE_QUALITY, SetList::TYPE_DECLARATION, SetList::DEAD_CODE, ]); PHPat (Architecture Testing) classes(Selector::inNamespace('Vendor\MyExtension\Domain')) ->shouldNotDependOn() ->classes(Selector::inNamespace('Vendor\MyExtension\Infrastructure')); } } Type Safety Patterns Typed Arrays with PHPDoc Generics /** * @return array