What best practices should be followed when creating a database connection class in PHP, considering error handling and security measures?

When creating a database connection class in PHP, it is important to implement proper error handling to catch any potential issues that may arise during the connection process. Additionally, security measures should be taken to prevent SQL injection attacks by using prepared statements and parameterized queries.

<?php
class Database {
    private $host = "localhost";
    private $username = "username";
    private $password = "password";
    private $dbname = "database";

    public $conn;

    public function __construct() {
        try {
            $this->conn = new PDO("mysql:host=$this->host;dbname=$this->dbname", $this->username, $this->password);
            $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        } catch(PDOException $e) {
            echo "Connection failed: " . $e->getMessage();
        }
    }

    public function query($sql, $params = []) {
        $stmt = $this->conn->prepare($sql);
        $stmt->execute($params);
        return $stmt;
    }
}
?>