php

 

php

Design Patterns in PHP - Teil 5: Das Vererbte Singleton-Factory-Registry Pattern

Bereich: 

Wenn man die drei Design Patterns aus den vorangegegangen Artikeln kombiniert, bekommt man sowas wie ein "Vererbtes Singleton-Factory-Registry". Dieses "Pattern" setze ich momentan erfolgreich als Basis des Refactoring/Redesigns meines kleinen Frameworks openWebX2 ein.



Als Basis dient hier eine erweiterte Version unserer abstrakten Singleton-Klasse:


  1. <?php
  2. abstract class SingletonFactoryRegistry {
  3.  
  4. 	// Instanzen werden in einem statischen Array gespeichert -> Registry
  5. 	private static $instances = array();
  6.  
  7. 	// Wie immer die Singleton-Vorraussetzungen:
  8. 	protected function __construct() {}
  9. 	final private function __clone() {}
  10.  
  11. 	// Die um Parameter erweiterte Singleton-Instanz
  12. 	final static public function getInstance($mixedParams = NULL) {
  13. 		static $instances = NULL;
  14. 		$myHash = md5(serialize($mixedParams));
  15. 		return $instances[$myHash] ?: $instances[$myHash] = new static($mixedParams);
  16. 	}
  17.  
  18. 	// Der "Loader", welcher sicherstellt, dass Objekte korrekt 
  19. 	// instanziiert werden, entweder via Singleton oder via Konstruktor.
  20. 	// Diese Instanzen werden dann gespeichert.
  21. 	// Also eine Art Factory-Registry
  22. 	final static public function load($strObject, $mixedParams = NULL) {
  23. 		$strObjectHash = md5($strObject.serialize($mixedParams));
  24. 		if (!isset(self::$instances[$strObjectHash])) {
  25. 			// Wenn das Objekt den Singleton benutzt, dann wird es via getInstance
  26. 			// instanziiert, ansonsten via Konstruktor.
  27. 			self::$instances[$strObjectHash] = (method_exists($strObject, 'getInstance') 
  28. 												? $strObject::getInstance($mixedParams) 
  29. 												: new $strObject($mixedParams));
  30. 			}
  31. 		return self::$instances[$strObjectHash];
  32. 	}
  33. }
  34. ?>




Dieses Object können wir nun benutzen um beliebige Objekte zu "laden". Wir legen uns dazu erst mal 2 unterschiedliche Klassen an:


  1. <?php
  2.  
  3. // Object benutzt das Singleton-Pattern
  4. class mySingleton extends SingletonFactoryRegistry {
  5.  
  6. 	private $__id = NULL;
  7.  
  8. 	// Der Constructor kann einen Parameter bekommen
  9. 	protected function __construct($strID = NULL) {
  10. 		$this->__id = $strID;
  11. 	}
  12. }
  13.  
  14. // Dieses Object hat einen normalen Konstruktor
  15. class myStandard {
  16.  
  17. 	private $__id = NULL;
  18.  
  19. 	public function __construct($strID = NULL) {
  20. 		$this->__id = $strID;
  21. 	}
  22. }
  23. ?>




Wir können nun beide Arten von Objekten über die load Methode unserer "SingletonFactoryRegistry"-Klasse instanziieren:


  1. <?php
  2. $myObj1 = SingletonFactoryRegistry::load('mySingleton');
  3. $myObj2 = SingletonFactoryRegistry::load('myStandard');
  4.  
  5. // Parameter:
  6. $myObj3 = SingletonFactoryRegistry::load('mySingleton','1');
  7. $myObj4 = SingletonFactoryRegistry::load('myStandard','2');
  8.  
  9. // Nun testen wir ob das SingletonFactoryRegistry-Pattern funktioniert:
  10.  
  11. $myObj5 = SingletonFactoryRegistry::load('mySingleton','1');
  12. $myObj6 = SingletonFactoryRegistry::load('myStandard','2');
  13.  
  14. var_dump($myObj1);
  15. var_dump($myObj2);
  16. var_dump($myObj3);
  17. var_dump($myObj4);
  18. var_dump($myObj5);
  19. var_dump($myObj6);
  20. ?>




Wie erwartet, sehen wir, dass $myObj3 und $myObj5 ebenso auf das identische Objekt verweisen wie $myObj4 und $myObj6:


  1. object(mySingleton)#1 (1) { ["__id":"mySingleton":private]=>  NULL }
  2. object(myStandard)#2 (1) { ["__id":"myStandard":private]=> NULL }
  3. object(mySingleton)#3 (1) { ["__id":"mySingleton":private]=> string(1) "1" }
  4. object(myStandard)#4 (1) { ["__id":"myStandard":private]=> string(1) "2" }
  5. object(mySingleton)#3 (1) { ["__id":"mySingleton":private]=> string(1) "1" }
  6. object(myStandard)#4 (1) { ["__id":"myStandard":private]=> string(1) "2" } 




Wir haben uns somite eine Art Object-Cache gebaut, der momentan noch innerhalb eines Prozesses seine Grenzen findet. Interessant wäre aber die Möglichkeit, die Objekte persistent zu speichern. Dieses nehmen wir in den nächsten Folgen unserer kleinen Serie in Angriff ;-)




Jens

Design Patterns in PHP - Teil 4: Das Factory Pattern

Bereich: 

Das Factory Design Pattern wird immer dann eingesetzt, wenn man Objekte instanzieren will, deren Typ aber erst zur Laufzeit bekannt ist.



Klassisches Beispiel hierfür wäre z.B. ein Datenbankwrapper. Es existieren mehrerer identische Objekte, welche die selben Methoden zur Verfügung stellen, aber auf unterschiedliche Datenbanksystem zugreifen und deren Besonderheiten nach aussen hin "verstecken".


Um dies zu gewährleisten, sollte man mittels Interfaces sicherstellen, dass diese Objekte wirklich nach aussen transparent bzw. identisch sind:



  1. <?php
  2. interface dbObject_interface {
  3.  
  4. 	public function dbSetStatement($strSQLStatement);
  5. 	public function dbFetchArray();
  6. 	public function dbFetchObject();
  7. 	public function dbFetchRow();
  8.  
  9. }
  10.  
  11. class dbObject_mysql implements dbObject_interface {
  12. 	.
  13. 	.
  14. 	.
  15. 	public function dbSetStatement($strSQLStatement) {
  16. 		// code
  17. 	}
  18. 	public function dbFetchArray() {
  19. 		// code
  20. 	}
  21. 	public function dbFetchObject() {
  22. 		// code
  23. 	}
  24. 	public function dbFetchRow() {
  25. 		// code
  26. 	}
  27. 	.
  28. 	.
  29. 	.
  30. }
  31.  
  32. class dbObject_postgresql implements dbObject_interface {
  33. 	.
  34. 	.
  35. 	.
  36. 	public function dbSetStatement($strSQLStatement) {
  37. 		// code
  38. 	}
  39. 	public function dbFetchArray() {
  40. 		// code
  41. 	}
  42. 	public function dbFetchObject() {
  43. 		// code
  44. 	}
  45. 	public function dbFetchRow() {
  46. 		// code
  47. 	}
  48. 	.
  49. 	.
  50. 	.
  51. }
  52. ?>

Nun ist sichergestellt, dass mindestens diese 4 Funktionen nach aussen weitergegeben werden. Nun können wir unser Factory-Objekt erstellen:

  1. <?php
  2.  
  3. class dbObject {
  4.  
  5.   public function __construct( $strDatabaseType ) {
  6. 	// Hier sollte man später mittels Exceptionhandler & z.B.
  7. 	// __autoload und class_exists() etwas sicherer agieren.
  8. 	$myObject = 'dbObject_'.strtolower($strDatabaseType);
  9. 	return new $myObject();
  10.   }	
  11. }
  12.  
  13.  
  14. $myDBMysql      = new dbObject('mysql');
  15. $myDBPostgres	 = new dbObject('postgresql');
  16. ?>

Dies waren jetzt erstmal alle Patterns, die ich im täglichen Gebrauch kennen und lieben gelernt habe. Die anderen Entwurfsmuster wie z.B. das Observer-Pattern oder das Decorator-Pattern habe ich mangels Verwendungszweck eher theoretisch kennengelernt.



Interessanter ist es wohl im täglichen Gebrauch mehrere Patterns zu kombinieren. So wäre es z.B. möglich, ein Objekt per Singleton zu instanzieren und dieses dann mittels Registry Pattern komplett zu speichern. Wenn man an z.B. eine Logging-Klasse denkt, wäre dies eventuell sogar über Prozessgrenzen hinweg sinnvoll (Filelocking...). Hier müsste dann das Registry das komplette Objekt z.B. in eine Datenbank oder einem sonstigen Speicher ablegen, der für jeden php-Prozess erreichbar ist.

Design Patterns in PHP - Teil 3: Das Registry Pattern

Bereich: 

Das Registry Pattern wird sehr oft eingesetzt, um globale Variablen im Context mehrerer Objekte zu vermeiden. Stellen wir uns dieses Entwurfsmuster einfach als eine Art Datenspeicher vor.




Da es sinnvoll ist, hier ein Singleton zu benutzen, um sicherzustellen, dass alle Objekte auf den gleichen Datenspeicher zugreifen, werden wir das Singleton aus Teil2 wieder "erben" und um uns wieder ein wenig Tipparbeit zu ersparen, nutzen wir die Überladung in php:


  1. <?php
  2. abstract class Singleton {
  3.     protected function __construct() {}
  4.     final private function __clone() {}
  5.     final static public function init() {
  6.         static $instance = null;
  7.         return $instance ?: $instance = new static;
  8.     }
  9. }
  10.  
  11. class Registry extends Singleton {
  12.  
  13. 	// Unsere gespeicherten Daten:
  14. 	protected $__data = NULL;
  15.  
  16. 	// Der Konstruktor, wird über den vererbten
  17. 	// init-Aufruf des Singleton aufgegrufen
  18. 	protected __contruct() {
  19. 		// was auch immer hier getan werden muss
  20. 	}
  21.  
  22. 	// Nun nutzen wir die überladenen Standardmethoden:
  23.  
  24. 	// Der überladene Setter
  25. 	public function __set($strPropertyName, $mixedValue) {
  26. 		$this->__data[$strPropertyName] = $mixedValue;
  27. 	}
  28.  
  29. 	// Der Getter
  30. 	public function __get($strPropertyName) {
  31. 		return isset($this->__data[$strPropertyName]) ? $this->__data[$strPropertyName] : NULL;
  32. 	}
  33.  
  34. 	// Pruefen ob eine Property gesetzt ist:
  35. 	public function __isset($strPropertyName) {
  36. 		return isset($this->__data[$strPropertyName]);
  37. 	}
  38.  
  39. 	// Einen Wert aus der Registry loeschen:
  40. 	public function __unset($strPropertyName) {
  41. 		// Die isset-Klausel vermeidet Notices...
  42. 		if isset($this->__data[$strPropertyName]) unset($this->__data[$strPropertyName]);
  43. 	}
  44. }
  45. ?>



Nun können wir das Registry von überall nutzen:


  1. <?php
  2.  
  3. $myReg1 = Registry::init();
  4. $myReg1->Variable = 'Hallo Welt';
  5.  
  6. $myReg2 = Registry::init();
  7. echo $myReg2->Variable; // -> Hallo Welt;
  8.  
  9. if (isset($myReg1->Variable)) {
  10. 	unset($myReg1->Variable);
  11. } 
  12.  
  13. echo $myReg2->Variable; // -> NULL;
  14.  
  15. ?>




Im nächsten Teil werden wir das Factory-Pattern kennenlernen.

Design Patterns in PHP - Teil 2: Das Singleton Pattern vererben

Bereich: 

Wie wir schon im ersten Teil gesehen haben, kann das Singleton-Pattern recht simpel und schnell in php realisiert werden.

Nun sind wir Developer ja von Natur aus bequem und selbst wenn man copy&paste liebt, geht es einem irgendwann auf die Nerven, dass jede Klasse die das Singleton-Pattern nutzen soll immer wieder die identischen Codezeilen enthält.

Seit php5.3 und seinen neuen "late static bindings" ist es jedoch möglich, das Singleton-Pattern in einer einzigen Klasse zu definieren und alle Klassen können einfach dieses Pattern erben:

  1. <?php
  2. abstract class Singleton {
  3.     // Dieses Mal als "protected", 
  4.     // damit die Kindklassen
  5.     // einen eigenen Constructor 
  6.     // haben können
  7.     protected function __construct() {}
  8.  
  9.     // Das kennen wir schon
  10.     final private function __clone() {}
  11.  
  12.  
  13.     // Durch die Late static Bindings 
  14.     // ist es möglich, mittels des 
  15.     // "static"-Keywords eine Instanz 
  16.     // der aufrufenden Klasse
  17.     // zurückzuliefern!
  18.     final static public function init() {
  19.         static $instance = null;
  20.         return $instance ?: $instance = new static;
  21.     }
  22. }
  23.  
  24. class ChildSingleton extends Singleton {
  25.  
  26.     // protected!! Sonst könnte man den
  27.     // Constructor "normal" aufrufen
  28.     protected function __construct() {
  29.         // Hier dann alles, was bei der Instanzierung
  30.         // der Childklasse zu tun ist
  31.     }
  32.  
  33. }
  34. ?>

Danach können wir die Klasse "ChildSingleton" wieder nur über ihre init-Methode instanzieren:

  1. <?php
  2. // FATAL ERROR!
  3. $a = new ChildSingleton();
  4. // Funktioniert:
  5. $a = ChildSingleton::init();
  6. $b = ChildSingleton::init();
  7. ?>

Testen wir mal, ob alles geht...

  1. <?php
  2. var_dump($a);
  3. var_dump($b);
  4. var_dump(($a === $b));
  5. ?>
  6.  
  7. object(ChildSingleton)#1 (0) { }
  8. object(ChildSingleton)#1 (0) { }
  9. bool(true)

Design Patterns in PHP - Teil 1: Das Singleton Pattern

Bereich: 

Es gibt im Netz wohl endlose Diskussionen darüber, ob das Singleton jetzt böse ist oder nicht. Meines Erachtens ist es in einigen Situationen sehr nützlich.



Das Singleton verhindert, dass es mehrere Instanzen eines Objektes gibt. Es wird immer genau eine Instanz erzeugt, sollte diese bereits existieren, dann wir die Instanz anstelle eines neuen Objektes beim Aufruf zurückgegeben.



In PHP(5) kann man dies recht einfach innerhalb einer Klasse realisieren:

  1. <?php
  2. class ExampleSingleton {
  3.  
  4.  
  5.     // Wir brauchen eine statische Instanzvariable:
  6.  
  7.     private static $instance = NULL;
  8.  
  9.  
  10.     /*
  11.      * Verhindern, dass unser Objekt 
  12.      * geclont werden kann:
  13.      * Einfach die Magic-function __clone
  14.      * auf "private" setzen.
  15.      */
  16.     private function __clone() {}
  17.  
  18.  
  19.     // Gleiches gilt für den Constructor:
  20.  
  21.     private function __construct() {}
  22.  
  23.  
  24.     // Nun der Singleton-Constructor
  25.  
  26.     public static function init() {
  27.         if (!self::$instance) {
  28.             self::$instance = new self;
  29.         }
  30.         return self::$instance;
  31.     }
  32. }
  33. ?>

Somit kann man das Objekt nur noch über seinen neuen Singleton-Konstruktor instanzieren:

  1. <?php
  2. // Führt zu einem FATAL ERROR:
  3. $try1 = new ExampleSingleton();
  4. // Funktioniert:
  5. $try2 = ExampleSingleton::init();
  6. // Nun nochmal die gleiche Objektinstanz:
  7. $try3 = ExampleSingleton::init();
  8. ?>




Klassische Beispiele für Singletons wären z.B. Datenbank-Klassen. Wir verhindern, dass mehrere Instanzen dieser Klassen existieren und somit kostbaren Speicher belegen.



In einem konkreten Fall habe ich dieses Pattern auch in einer HTML5-Klasse eingesetzt, denn es ergibt wenig Sinn, mehrere Ausgabe-Objekte parallel zu haben. Alle Objekte, die dieses HTML5-Objekt nutzen, schreiben ihre Ausgabe in eine "body"-Eigentschaft des Singletons.



Im nächsten Teil sparen wir uns viel Tipparbeit und vererben das Singleton-Pattern einfach.

MongoDB und php5 - in 3 Schritten zu Datenbankperformance

Bereich: 

Nachdem ich die Ergebnisse einiger Performancetests veröffentlicht habe, zeige ich in diesem Artikel mal, wie schnell und einfach man mongoDB einrichten und nutzen kann. Ich versichte absichtlich hier auf die Einrichtung der Datenbank mit Benutzeranmeldung, der REST-API und der Replikation, un konzentriere mich auf den einfachsten Fall und den schnellsten Weg.


1. Installation

Die Binärpakete für fast alle Betriebssysteme findet man unter [1], Linux und Mac-User werden wohl besser ihr eingebautes (oder im Falle Mac "nachinstalliertes") Distributions-Paketsystem nutzen. Unter gentoo genügt ein "emerge mongodb".



Um die Datenbank in php5 nutzen zu können braucht man die PECL-Extension für php. Unter [2] finden sich die Binärpakete, eine gute Installationsanweisung findet ihr im php-Manual unter [3].


2. Der Server

Den Server startet man über das Programm mongod[.exe] in der Konsole. Eventuell muss man diverse Parameter mitgeben, vor allem den Speicherort der Datenbanken. Da mongodb die Datenbankfiles und die Indexfiles im Vorfeld exponentiell wachsen lässt, sollte man für ausreichend Speicherplatz sorgen und unter den Unix-/Linux-Systemen ein performantes Dateisystem wählen (ext4, btrfs, zfs, ...).



Die meisten Linuxe werden mongodb jedoch mit mehr oder weniger sinnvollen Stnadardeinstellungen installiert und konfiguriert haben, so dass ein "/etc/init.d/mongodb start" oder Ähnliches genügen.



Danach kann man auf der Konsole mit dem Clientprogramm testen, ob der Server läuft: mongo[.exe] eingeben und man ist mit dem server verbunden.


3. MongoDB in php5 nutzen

Das php5-pecl-Modul ist erfrischend objektorientiert und die Daten welche man in der DB speichert und ausliesst werden als assoziative Arrays übergeben, so dass die ersten Schritte recht schnell nachvollziehen kann:


  1. // Wir instanzieren ein Mongo-Objekt:
  2. $myMongoObject = new Mongo();
  3.  
  4. // Und wählen unsere Datenbank "dataBase" und die Collection "myCollection" aus.
  5. // "Collection" -> Entspricht den Tabellen in relationalen DBs
  6. $myCollection = $myMongoObject->dataBase->collectionEins;
  7.  
  8. // Wir fuegen jetzt ein Array in die Collection ein:
  9. $myArray = array (
  10.   'name' => 'Test123',
  11.   'wert' => 123,
  12.   'nocheinwert' => TRUE
  13. ); 
  14. $myCollection->insert($myArray);
  15.  
  16. // Jetzt lesen wir unser Array wieder aus
  17. // als Rueckgabewert erhalten wir einen MongoCursor.
  18. $myCursor = $myCollection->find(array('name' => 'Test123'));
  19.  
  20. // diesen Cursor wandeln wir in ein Array
  21. $retArray = iterator_to_array($myCursor)




Wir sehen, die ersten Schritte in MongoDB sind wirklich einfach und das php-Manual unter [4] oder die mongodb-Homepage unter [5] sind sehr ausführlich.



Ich hoffe, Euer Interesse geweckt zu haben und ihr erhebt Euch von der Couch ;-)


  1. http://www.mongodb.org/downloads
  2. https://github.com/mongodb/mongo-php-driver/downloads
  3. http://php.net/manual/de/mongo.installation.php
  4. http://www.php.net/manual/de/book.mongo.php
  5. http://www.mongodb.org/display/DOCS/Home

echo vs. print

Bereich: 

Oft belächelt, aber immer noch wahr: echo ist schneller als print!



Um das ganze unter realistischen Bedingungen zu testen, wurden alle Caches (APC, ...) deaktiviert und das Outputbuffering in der CLI-Version von php deaktiviert.



Folgender Code dient als Test:

  1. <?php
  2. // Ergebnisse im Array speichern...
  3. $arrRes = array();
  4.  
  5. $time = microtime(true);
  6. for ($i=0; $i<100000; $i++) {
  7.         echo $i;
  8. }
  9. $arrRes['echo'] = microtime(true) - $time;
  10.  
  11. $time = microtime(true);
  12. for ($i=0; $i<100000; $i++) {
  13.         print $i;
  14. }
  15. $arrRes['print'] = microtime(true) - $time;
  16.  
  17. $time = microtime(true);
  18. for ($i=0; $i<100000; $i++) {
  19.         echo ($i);
  20. }
  21. $arrRes['echo()'] = microtime(true) - $time;
  22.  
  23. $time = microtime(true);
  24. for ($i=0; $i<100000; $i++) {
  25.         print ($i);
  26. }
  27. $arrRes['print()'] = microtime(true) - $time;
  28.  
  29. print_r($arrRes);
  30. ?>

Diese Testläufe führen im aktuellen Lauf zu folgenden Werten:

  1. (
  2.     [echo] => 9.266254901886
  3.     [print] => 13.399059057236
  4.     [echo()] => 14.875532150269
  5.     [print()] => 13.882831096649
  6. )

Die Werte variieren je nach sonstiger Auslastung des Testsystems, aber es gibt immer einen klaren Sieger: echo. Die beiden Varianten mit den Klammern sind immer - logischerweise - langsamer als ihre Pendants ohne Klammerung.

Oft als Mythos verschrien, zeigt sich alleine bei der Ausgabe von den wenigen Zahlen eine klare Überlegenheit. Mancher Entwickler sollte im Kleinen beginnen zu optimieren!

In der nächsten Folge testen wir die Geschwindigkeit von Anführungszeichen vs. Hochkommas.

Seiten

RSS - php abonnieren