# Legacy PHP Versus Modern PHP — PHP

Source: https://www.skillbyai.com/en/php/p-modern

> Recognise outdated practices and modernise PHP code step by step.

## Leaving bad habits behind

Much PHP advice online predates modern PHP. Practices to **avoid**: the removed `mysql_*` functions and SQL built by string concatenation; `md5` or `sha1` for passwords; `extract()` and variable variables; global state and `global` keywords; mixing HTML, SQL and business logic in one file; suppressing errors with `@`; relying on loose `==` comparisons; untyped functions and arrays of arrays everywhere; and `include` chains instead of autoloading. **Modern equivalents**: PDO or an ORM with prepared statements, `password_hash`, typed classes and value objects, dependency injection, templates with auto-escaping, `strict_types`, enums, readonly classes, Composer autoloading, PSR interfaces, static analysis and automated tests. Modernise legacy code **incrementally**: add Composer and autoloading, add tests around critical paths (characterisation tests), raise PHPStan levels with a baseline, introduce types, use **Rector** to automate syntax upgrades, and replace hand-written plumbing with framework components one area at a time, the strangler approach applied inside a codebase.

## Legacy code and its modern rewrite

Same behaviour: fetch a user's orders.

```php
<?php
// legacy (do not do this)
// $id = $_GET['id'];
// $res = mysqli_query($conn, "SELECT * FROM orders WHERE user_id = $id");   // SQL injection
// while ($row = mysqli_fetch_assoc($res)) { echo "<li>" . $row['title'] . "</li>"; }   // XSS

// modern
declare(strict_types=1);

final readonly class OrderSummary
{
    public function __construct(public string $id, public string $title, public OrderStatus $status) {}
}

final class OrderQueries
{
    public function __construct(private readonly PDO $pdo) {}

    /** @return list<OrderSummary> */
    public function forUser(int $userId): array
    {
        $stmt = $this->pdo->prepare('SELECT id, title, status FROM orders WHERE user_id = :uid ORDER BY created_at DESC');
        $stmt->execute(['uid' => $userId]);
        return array_map(
            fn (array $r): OrderSummary => new OrderSummary($r['id'], $r['title'], OrderStatus::from($r['status'])),
            $stmt->fetchAll(PDO::FETCH_ASSOC),
        );
    }
}
// template: <li><?= e($order->title) ?></li>   (escaped output)
```

## Upgrade in small, tested steps

Big-bang rewrites of legacy PHP applications often fail. Add tests around a feature, modernise it, ship it, and repeat. Rector and PHPStan make each step mechanical and safe.

**Quiz:** Which practice belongs to modern PHP?

- [x] Prepared statements with PDO and strict types
- [ ] Using mysql_* functions
- [ ] Hashing passwords with md5
- [ ] Suppressing errors with @

*Answer:* Prepared statements with PDO and strict types. Prepared statements and strict types are core modern practices; the others are outdated and unsafe.
