How can Dependency Injection and Dependency Containers improve the testability and maintainability of PHP applications?

Dependency Injection and Dependency Containers improve the testability and maintainability of PHP applications by decoupling components, making it easier to replace dependencies with mock objects for testing and allowing for easier management of dependencies through a centralized container.

// Example of using Dependency Injection in PHP

class Database {
    public function query($sql) {
        // Database query implementation
    }
}

class UserRepository {
    private $db;

    public function __construct(Database $db) {
        $this->db = $db;
    }

    public function getUserById($id) {
        return $this->db->query("SELECT * FROM users WHERE id = $id");
    }
}

// Usage
$db = new Database();
$userRepository = new UserRepository($db);
$user = $userRepository->getUserById(1);