You are not logged in.

This articles has been requested to be deleted.

Friday, September 18th 2009, 8:38am

Tags

abstraktion, Datenbank, db, injection, MySQL, pdo, PostgreSQL, SQL

Abstract

PDO ist die moderne Datenbankschnittstelle von PHP5. Manch geneigten Umsteiger überfordet PDO mit dem vermeintlich großen Funktionsumfang. Hier werden die wichtigsten Funktion erläutert.

Article

PDO(PHP Data Object) ist der moderne Weg mit PHP5 auf die Datenbank zuzugreifen. Es bietet eine Abstraktionsschicht für den Datenzugriff. Unabhängig vom verwendeten DBMS können die selben Funktionen verwenden.
PDO räumt außerdem mit vielen Sicherheitsängsten auf, die man von anderen Datenbank-Schnittstellen kennt. Die vielen Wege die teilweise zum selben Ergebnis führen schrecken viele Programmierer ab, daher soll dieses Tutorial einen konsistenten Weg liefern.
Konfiguration
Eine gängige Art die Konfigurationvariablen zu hinterlegen ist eine finale Klasse mit Konstanten. Sie benötigt relativ wenig Prozess Overhead.

PHP Quellcode

1
2
3
4
5
6
7
final class Configuration { 
	const DB_HOST='localhost'; 
	const DB_DATABASE='datebase'; 
	const DB_PORT=3306; 
	const DB_USER='database_user'; 
	const DB_PASSWORD='database_password'; 
}


1. Verbindung herstellen


Einleitend wurde erläutert, dass PDO eine Abstraktionsschicht für den Datenbankzugriff bereitstellt. Durch Abstraktion folgert Einschränkung - nämlich auf den Nenner des schwächsten unterstützen DBMS. Aus mehreren Gründen macht es Sinn den Datenbankzugriff weiter zu kapseln.
Wir erstellen uns daher eine neue Klasse "MyDB" die als Singleton implementiert wird, somit wird verhindert, dass in der selben PHP Anwendung mehrmals die Verbindung hergestellt wird.
Außerdem erstellen wir eine persistente Verbindung - das bietet sich bei den meisten Webanwendungen an.

PHP Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MyDB { 
	private static $db; 
 
	static public function getInstance() { 
		if(!self::$db) { 
			self::$db = new PDO( 
				'mysql:host='.Configuration::DB_HOST.';dbname='.Configuration::DB_DATABASE.';port='.Configuration::DB_PORT, 
				Configuration::DB_USER, 
				Configuration::DB_PASSWORD, 
				array( 
					PDO::ATTR_PERSISTENT => true, 
					PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ,
					PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
				) 
			); 
		} 
		return self::$db; 
	} 
}


2. Datenbankänderungen


Die einfachste Form von Datenbankzugriffen stellt das Ändern kleiner Daten dar. Parameter die man per Formular übergibt oder über die URL erhält, werden als Array-Parameter durch das execute() Statement gebunden.
Außerdem fällt auf, dass der SQL-String durch das prepare Statement vorbereitet wird. Das ist ein Performance Faktor, sollte man das selbe "Prepared Statement" mehrmals mit unterschiedlichen Parameter ausführen wollen (was ein extrem seltener Fall ist).

PHP Quellcode

1
2
3
4
5
6
7
8
9
10
$pdoparams = array( 
	':userid' => $_REQUEST['userid'], 
	':website' => 'http://www.easy-coding.de' 
); 
$sql = "UPDATE		user 
	SET		website = :website 
	WHERE 		userid = :userid 
	LIMIT		1"; 
$stmt = MyDB::getInstance()->prepare($sql); 
$stmt->execute($pdoparams);


3. Neue Datenbankeintrage: ID des Datensatzes


Werden auto_increment (MySQL) oder SERIAL (postgreSQL) Felder genutzt, kümmert sich das DBMS bei neuen Einträgen um die neuen Primärschlüssel. Sie werden mit der Funktion lastInsertID abgefragt.

PHP Quellcode

1
2
3
4
5
6
7
8
9
10
$pdoparams = array( 
	':website' => 'http://www.easy-coding.de' 
); 
$sql = "INSERT INTO	user 
			(website) 
	VALUES		(:website)"; 
$stmt = MyDB::getInstance()->prepare($sql); 
$stmt->execute($pdoparams); 
$userID = MyDB::getInstance()->lastInsertId();
echo $userID;


4. Datenbankabfragen


Datenbankabfragen werden genauso wie Änderungen konstruiert - einzig die zusätzliche Methode fetch() wird danach aufgerufen.
Wir haben eingangs beim Konstruieren den Standard Fetch-Modus auf ASSOC geändert. Dadurch erhalten wir ein Array Objekt mit dem Spaltennamen als Schlüssel und dem Inhalt als Wert erhalten. Führen wir die fetch() Funktion mehrmals durch erhalten wir alle Zeilen (falls mehrere vorhanden sind).

PHP Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
$pdoparams = array( 
	':userid' => $_REQUEST['userid'] 
); 
$sql = "SELECT		* 
	FROM		user 
	WHERE		userid = :userid "; 
$stmt = MyDB::getInstance()->prepare($sql); 
$stmt->execute($pdoparams); 
$row = $stmt->fetch(); 
print_r($row);
$row = $stmt->fetch(); 
print_r($row);


5. Mehrere Daten abfragen


Häufig werden einfach alle Daten abgefragt, die man per SQL String definiert. Statt der Methode fetch() benutzt man dazu die Methode fetchAll(). Die Rückgabe der Funktion ist ein normales zweidimensionales Array, über das man mit foreach iterieren kann.

PHP Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$pdoparams = array( 
	':username' => "%".$_REQUEST['username']."%", 
	':minage' => $_REQUEST['minage'], 
	':maxage' => $_REQUEST['maxage'] 
); 
$sql = "SELECT		* 
	FROM		user 
	WHERE		username LIKE :username 
	AND		age BETWEEN :minage AND :maxage"; 
$stmt = MyDB::getInstance()->prepare($sql); 
$stmt->execute($pdoparams); 
foreach($stmt->fetchAll() as $row) { 
	print_r($row); 
}


6. Kommaseparierte Liste mit IN


MySQL und andere DBMS bieten die Möglichkeit kommaseparierte Listen mit IN zu verarbeiten.
Der SQL String um die Benutzer 1,3 und 5 zu löschen würde folgendermaßen aussehen:

SQL Code

1
2
DELETE FROM	user 
WHERE		userID IN (1,3,5)


Um eine kommaseparierte Listen verarbeiten zu können kennt das PDO leider keine Möglichkeit.
Die Klasse MyDB wird daher um eine spezielle implode Methode erweitert:

PHP Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyDB { 
	private static $escapecounter = 0; 
	... 
 
	static public function implode(&$pdoparams, $arr) { 
		$tmp = array(); 
		foreach($arr as $val) { 
			$key = ':implode'.self::$escapecounter++; 
			$pdoparams[$key] = $val; 
			$tmp[] = $key; 
		} 
		return implode(',', $tmp); 
	}
}


Das Beispiel erweitern wir wie folgt:

PHP Quellcode

1
2
3
4
5
$pdoparams = array(); 
$sql = "DELETE FROM	user 
	WHERE		userID IN (".MyDB::implode($pdoparams, array(1,3,5)).")"; 
$stmt = MyDB::getInstance()->prepare($sql); 
$stmt->execute($pdoparams);


Die Methode implode() liefert einen String mit Platzhaltern zurück:

SQL Code

1
2
DELETE FROM	user 
WHERE		userID IN (:implode0,:implode1,:implode2)

Zusätzlich wird das als Referenz übergebene Array $pdoparams um die Inhalte erweitert. Nach Ausführung von implode sieht das Array also wie folgt aus:

Source code

1
2
3
4
5
Array (
    [:implode0] => 1
    [:implode1] => 3
    [:implode2] => 5
)

Das execute() bindet diese Parameter an den String und das Statement wird sicher ausgeführt. Die Lösung ist besser als sich den String mit PHP Stringfunktionen selbst zusammen zu bauen, da das PDO nur auf diese Art vor SQL Injections schützt.

7. Code Download


Den fertigen Code von Klasse und dem letzten Beispiel gibt es zum Download unter http://demo.easy-coding.de/php/pdo/download.zip.

Lexikon 4.1.3, developed by www.viecode.com