PHP: Die Singleton PDO-Datenbankklasse

Eine Datenbankklasse sollte im Normalfall nur eine einzige Verbindung zum Datenbankserver herstellen. Aus diesem Grund ist es sinnvoll, wenn man die Datenbankklasse als Singleton schreibt. Es gibt auch Fälle, in denen man mehrere Verbindungen zum Datenbankserver brauchen könnte, aber normalerweise reicht eine Verbindung zur Datenbank vollkommen aus.

Die PDO-Datenbankklasse als Singleton

Im Folgenden bauen wir eine ganz simple Singleton-Datenbankklasse, die die PDO-Klasse erbt. Es stehen dann in unserer Datenbankklasse alle öffentlichen Methoden, Eigenschaften und Konstanten der PDO-Klasse zur Verfügung.

Der Vorteil ist, dass man diese Klasse dann auch in anderen Klassen verwenden kann, ohne dass ein neues Objekt erstellt und dadurch eine neue Verbindung zur Datenbank aufgebaut wird. Wer das Prinzip des Singleton-Patterns noch nicht verstanden hat, der kann sich einfach nochmal diesen Artikel anschauen.

Erstellen der Singleton-Datenbankklasse (Teil 1)

Unsere Singleton-Datenbankklasse nenne ich einfach mal Database. Da diese ja von der PDO-Klasse schon alle relevanten Methoden zum Ausführen von Queries usw. erbt, müssen wir in der Database-Klasse selbst nur noch den Singleton reinschreiben und die PDO-Verbindung aufbauen. Die Konfigurationsdaten zum Verbinden mit dem Datenbankserver werde ich einfach mal hart in die Klasse reinschreiben.

class Database extends PDO {
	private static $INSTANCE;

	public function __construct() {}

	public static function getInstance() {
		if(!self::$INSTANCE) {
			self::$INSTANCE = new self;
		}
		return self::$INSTANCE;
	}
}

Erstellen der Singleton-Datenbankklasse (Teil 2)

Das war jetzt das Grundgerüst unserer Datenbankklasse, die bereits die PDO-Klasse erbt und die Singleton-Methode getInstance zum Holen der Instanz enthält. Der Konstruktor ist übrigens absichtlich public, mehr dazu später. Im Folgenden erstellen wir dann die Konfigurationseigenschaften und bauen im Konstruktor die PDO-Verbindung zum Datenbankserver auf – und das war's dann auch schon.

class Database extends PDO {
	private static $INSTANCE;
	private $HOST = 'localhost';
	private $USER = 'mysqluser';
	private $PASS = 'password';
	private $BASE = 'database';
	private $CHAR = 'utf8';
	private $PORT = 3306;

	public function __construct() {
		parent::__construct('mysql:host='.$this->HOST.';dbname='.$this->BASE.';charset='.$this->CHAR.';', $this->USER, $this->PASS);
	}

	public static function getInstance() {
		if(!self::$INSTANCE) {
			self::$INSTANCE = new self;
		}
		return self::$INSTANCE;
	}
}

Warum der Konstruktor auf Public ist

Der Konstruktor ist hier auf public, weil PHP sonst eine Fehlermeldung wirft die besagt, dass der Access Level des Konstruktors gleich dem der PDO-Klasse sein muss, da dieser ja schon auf public steht und man ihn nicht überschreiben kann. Das heißt, dass hier trotzdem noch mit new eine neue Instanz erstellt werden kann. Das muss man aber nicht, sondern kann weiterhin die getInstance-Methode verwenden. Vielleicht hat ja jemand eine Lösung für dieses Problem und würde sie mir über die Kommentare mitteilen. 😊

Anwenden der PDO-Datenbankklasse

Die Datenbankklasse ist nun fertig und kann instanziert werden. Dazu ein kleines Anwendungsbeispiel mit einer Beispielabfrage:

$pdo = Database::getInstance();

$stmt = $pdo->prepare('SELECT foo FROM bar WHERE id = ? AND aid = ?');
$stmt->bindValue(1, 4567);
$stmt->bindValue(2, 8765);

if($stmt->execute()) {
	while($obj = $stmt->fetch(PDO::FETCH_OBJ)) {
		echo 'The result is: '.$obj->foo.'<br />';
	}
}

Update: Die bessere Singleton-Datenbankklasse

Danke an @Patrick, der mich in den Kommentaren darauf hinwies, dass man ja auch die PDO-Instanz in der Instanzvariable speichern kann. Dementsprechend habe ich die Datenbankklasse mal angepasst. Beim Zugriff von außen ändert sich rein gar nichts, lediglich die Klasse wird überarbeitet.

Im folgenden Code ist der Konstruktur dann endlich auf private, so wie es sich gehört, damit es auch eine richtige Singleton-Klasse ist. Wer den alten Code bereits verwendet, der braucht ihn nur durch den folgenden zu ersetzen und die Konfiguration anzupassen.

class Database {
	private static $PDOINSTANCE;
	private static $HOST = 'localhost';
	private static $USER = 'mysqluser';
	private static $PASS = 'password';
	private static $BASE = 'database';
	private static $CHAR = 'utf8';
	private static $PORT = 3306;

	private function __construct() {}
	private function __clone() {}

	public static function getInstance() {
		if(!self::$PDOINSTANCE) {
			self::$PDOINSTANCE = new PDO('mysql:host='.self::$HOST.';dbname='.self::$BASE.';charset='.self::$CHAR.';', self::$USER, self::$PASS);
	}
		return self::$PDOINSTANCE;
	}
}

Hinweis:
Dies ist ein älterer Artikel von meinem alten Blog. Die Kommentare zu diesem Artikel werden (falls vorhanden) später noch hinzugefügt.

Der Autor

Unter dem Namen »TheBlackPhantom« alias »BlackY« veröffentlichte ich auf meinem alten Blog, BlackPhantom.DE, in der Zeit von 2011 bis 2015 leidenschaftlich Beiträge über Computer, Internet, Sicherheit und Malware. Während der BlackPhantom-Zeit war ich noch grün hinter den Ohren und lernte viel dazu. Mehr Infos vielleicht in Zukunft...