What are some best practices for error handling and resource management in custom database classes in PHP?

When working with custom database classes in PHP, it is important to implement proper error handling and resource management to ensure the stability and security of your application. This includes using try-catch blocks to handle exceptions, closing database connections after use, and freeing up resources to prevent memory leaks.

class CustomDatabase {
    private $connection;

    public function __construct($host, $username, $password, $database) {
        $this->connection = new mysqli($host, $username, $password, $database);
        if ($this->connection->connect_error) {
            throw new Exception("Connection failed: " . $this->connection->connect_error);
        }
    }

    public function query($sql) {
        $result = $this->connection->query($sql);
        if (!$result) {
            throw new Exception("Query error: " . $this->connection->error);
        }
        return $result;
    }

    public function close() {
        $this->connection->close();
    }

    public function __destruct() {
        $this->close();
    }
}