PHP 7: Maintaining and Upgrading Legacy Apps · Refactoring Legacy PHP Safely

Replacing Associative Arrays with Objects

Turn 'array shapes' into typed DTOs and value objects.

Legacy code passes associative arrays everywhere ($user['email']), so typos and missing keys only fail at runtime. Introduce small DTOs/value objects with typed properties (7.4) or getters, created once from the array at the boundary. Document remaining array shapes with PHPStan's array{id:int, email:string} until they're converted.

<?php
final class CustomerDto
{
    public int $id;
    public string $email;
    public ?string $company;

    public static function fromRow(array $row): self
    {
        $dto = new self();
        $dto->id = (int) $row['id'];
        $dto->email = (string) $row['email'];
        $dto->company = $row['company'] !== '' ? $row['company'] : null;
        return $dto;
    }
}
// PHP 8 version later: constructor promotion + readonly.

Common pitfalls

  • Converting everything at once instead of at the boundaries you touch.
  • DTOs with public mutable properties used as domain objects with rules.
  • Keeping both arrays and objects in parallel indefinitely.

Key points

  • Typed objects catch errors earlier
  • Convert at the boundary
  • Document remaining arrays with shapes

Further reading: PHPStan: array shapes

Practice questions

What's an array shape in PHPStan?

Answer

A PHPDoc type like array{id: int, email: string} describing the keys and value types of an associative array, so static analysis can check access.

Why create DTOs at the boundary rather than deep inside the code?

Answer

Data is validated and typed once on entry, and all downstream code benefits from types without scattered isset checks.

Open in the app to mark lessons complete, take notes and track your progress.