<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Gjero Krsteski</title>
	<atom:link href="http://krsteski.de/feed" rel="self" type="application/rss+xml" />
	<link>http://krsteski.de</link>
	<description>...schreibt über PHP-Enterprise, Core-Themen und für die PHP-Community</description>
	<lastBuildDate>Mon, 23 Jan 2012 07:40:46 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Objekte abrufen und mehrere Instanzen vermeiden</title>
		<link>http://krsteski.de/php-tricks-und-tipps/objekten-abrufen-und-mehrere-instanzen-vermeiden.html</link>
		<comments>http://krsteski.de/php-tricks-und-tipps/objekten-abrufen-und-mehrere-instanzen-vermeiden.html#comments</comments>
		<pubDate>Fri, 18 Nov 2011 08:37:48 +0000</pubDate>
		<dc:creator>Gjero Krsteski</dc:creator>
				<category><![CDATA[PHP Tricks und Tipps]]></category>
		<category><![CDATA[Active Record]]></category>
		<category><![CDATA[Caching]]></category>
		<category><![CDATA[Identity Map]]></category>
		<category><![CDATA[OOP]]></category>
		<category><![CDATA[Pattern]]></category>
		<category><![CDATA[PDO]]></category>
		<category><![CDATA[Performance]]></category>

		<guid isPermaLink="false">http://krsteski.de/?p=700</guid>
		<description><![CDATA[Auf Englisch würde das etwa als „Retrieve objects avoiding multiple instances” geschrieben werden. Den Artikel habe ich auf Englisch verfasst, da ich von phpclasses.org gebeten wurde paar Zeilen Dokumentation zum Projekt „Building an Identity Map in PHP” zu schreiben.  Das Projekt  zeigt auf wie eine Identity-Map im PHP Projekt implementiert wird und was [...]]]></description>
			<content:encoded><![CDATA[<p>Auf Englisch würde das etwa als „Retrieve objects avoiding multiple instances” geschrieben werden. Den Artikel habe ich auf Englisch verfasst, da ich von <a href="http://phpclasses.org">phpclasses.org</a> gebeten wurde paar Zeilen Dokumentation zum Projekt „<a href="http://www.phpclasses.org/package/7209-PHP-Retrieve-objects-avoiding-multiple-instances.html">Building an Identity Map in PHP</a>” zu schreiben.  Das Projekt  zeigt auf wie eine Identity-Map im PHP Projekt implementiert wird und was für Vorteile das Ganze mit sich bringt. So, ab heir ist der Artikel auf Englisch verfasst:  The  „Building an Identity Map in PHP” package can store and retrieve objects in persistent storage containers avoiding to have multiple instances of the same object in memory. It can use a mapper class to store objects of a class in a container like for instance a database table. It can also retrieve the objects from the container assuring that only one instance of the same object is retrieved into memory.<span id="more-700"></span></p>
<h4>This package requires</h4>
<p>- PDO a lightweight, consistent interface for accessing databases in PHP.<br />
- PHPUnit a unit testing framework for PHP projects.</p>
<h4>This package implements</h4>
<p>- <a href="http://martinfowler.com/eaaCatalog/dataMapper.html">Data-Mapper Pattern</a><br />
- <a href="http://martinfowler.com/eaaCatalog/identityMap.html">Identity-Map Pattern</a></p>
<h4>Why identity mapping?</h4>
<p>By using Data-Mapper pattern without an identity map, you can easily run into problems because you may have more than one object that references the same domain entity.</p>
<h4>Data-Mapper without identity map</h4>
<pre name="code" class="php">
      $userMapper = new UserMapper($pdo);

      $user1 = $userMapper->find(1); // creates new object
      $user2 = $userMapper->find(1); // creates new object

      echo $user1->getNickname(); // joe123
      echo $user2->getNickname(); // joe123

      $user1->setNickname('bob78');

      echo $user1->getNickname(); // bob78
      echo $user2->getNickname(); // joe123 -> ?!?
</pre>
<h4>Data-Mapper with identity map</h4>
<p>The identity map solves this problem by acting as a registry for all loaded domain instances.</p>
<pre name="code" class="php">
      $userMapper = new UserMapper($pdo);

      $user1 = $userMapper->find(1); // creates new object
      $user2 = $userMapper->find(1); // returns same object

      echo $user1->getNickname(); // joe123
      echo $user2->getNickname(); // joe123

      $user1->setNickname('bob78');

      echo $user1->getNickname(); // bob78
      echo $user2->getNickname(); // bob78 -> yes, much better
</pre>
<p>By using an identity map you can be confident that your domain entity is shared throughout your application for the duration of the request. Note that using an identity map is not the same as adding a cache layer to your mappers. Although caching is useful and encouraged, it can still produce duplicate objects for the same domain entity.</p>
<h3>Source &#038; UML</h3>
<p>Take a look at the <a href="https://github.com/gjerokrsteski/php-identity-map">php-identity-map repository on GitHub</a><br />
<a href="http://krsteski.de/wp-content/uploads/2011/11/php-identity-map-with-one-2-many-uml.gif"><img src="http://krsteski.de/wp-content/uploads/2011/11/php-identity-map-with-one-2-many-uml-300x154.gif" alt="php-identity-map-with-one-2-many-uml" title="php-identity-map-with-one-2-many-uml" width="300" height="154" class="alignnone size-medium wp-image-725" border="1"/></a> </p>
<h3>Process Workflow</h3>
<p><a href="http://martinfowler.com/eaaCatalog/identityMap.html">Martin Fowler</a> says: <em>&#8220;If you load the same data more than once you&#8217;re incurring an expensive cost in remote calls. Thus, not loading the same data twice doesn&#8217;t just help correctness, but can also speed up your application. An Identity Map keeps a record of all objects that have been read from the database in a single business transaction. Whenever you want an object, you check the Identity Map first to see if you already have it.&#8221;</em></p>
<p><a href="http://krsteski.de/wp-content/uploads/2011/12/identity-map-workflow.gif"><img src="http://krsteski.de/wp-content/uploads/2011/12/identity-map-workflow-300x212.gif" alt="identity map workflow" title="identity map workflow" width="300" height="212" class="size-medium wp-image-737" border="1" /></a></p>
<h3>Presentation</h3>
<div style="width:510px" id="__ss_10532161"> <strong style="display:block;margin:12px 0 4px"><a href="http://www.slideshare.net/gjerokrsteski/proven-design-patterns-of-the-datalayer-in-php" title="Proved PHP Design Patterns for Data Persistence" target="_blank">Proved PHP Design Patterns for Data Persistence</a></strong> <object id="__sse10532161" width="510" height="426"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=designpatternsofthedatalayer-gjerokrsteski-111209083731-phpapp02&#038;rel=0&#038;stripped_title=proven-design-patterns-of-the-datalayer-in-php&#038;userName=gjerokrsteski" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><param name="wmode" value="transparent"/><embed name="__sse10532161" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=designpatternsofthedatalayer-gjerokrsteski-111209083731-phpapp02&#038;rel=0&#038;stripped_title=proven-design-patterns-of-the-datalayer-in-php&#038;userName=gjerokrsteski" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" wmode="transparent" width="510" height="426"></embed></object>
<div style="padding:5px 0 12px"> View more <a href="http://www.slideshare.net/" target="_blank">presentations</a> from <a href="http://www.slideshare.net/gjerokrsteski" target="_blank">Gjero Krsteski</a> </div>
</p></div>
]]></content:encoded>
			<wfw:commentRss>http://krsteski.de/php-tricks-und-tipps/objekten-abrufen-und-mehrere-instanzen-vermeiden.html/feed</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>MySQL-Functions vs. PDO vs. Doctrine2</title>
		<link>http://krsteski.de/php-tricks-und-tipps/mysql-functions-vs-pdo-vs-doctrine2.html</link>
		<comments>http://krsteski.de/php-tricks-und-tipps/mysql-functions-vs-pdo-vs-doctrine2.html#comments</comments>
		<pubDate>Mon, 26 Sep 2011 14:56:36 +0000</pubDate>
		<dc:creator>Gjero Krsteski</dc:creator>
				<category><![CDATA[PHP Tricks und Tipps]]></category>
		<category><![CDATA[Benchmarking]]></category>
		<category><![CDATA[DOctrine2]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[ORM]]></category>
		<category><![CDATA[PDO]]></category>
		<category><![CDATA[Performance]]></category>

		<guid isPermaLink="false">http://krsteski.de/?p=637</guid>
		<description><![CDATA[Neulich bin ich in ein komplett neues Projekt eingestiegen. Das Team diskutierte über ein mögliches ORM Framework. Die Anforderungen an da ORM-Framework waren: „Wir möchten keine SQL-Statement mehr schreiben“, „Wir möchten nur mit Objekten arbeiten“, „Ein Scaffolding oder ein Reverse-Engineering wäre super, ist aber kein muss“ und „Wir möchten Vor- und Nachteile von Doctrine2 gegenüber [...]]]></description>
			<content:encoded><![CDATA[<p>Neulich bin ich in ein komplett neues Projekt eingestiegen. Das Team diskutierte über ein mögliches ORM Framework. Die Anforderungen an da ORM-Framework waren: „Wir möchten keine SQL-Statement mehr schreiben“, „Wir möchten nur mit Objekten arbeiten“, „Ein Scaffolding oder ein Reverse-Engineering wäre super, ist aber kein muss“ und „Wir möchten Vor- und Nachteile von Doctrine2 gegenüber MySQL-Functions und PDO haben“. Zuletzt auch noch diese: „Wenn möglich, dann soll es mit nicht relationalen Datenbanken sowie mit relationalen Datenbanken zusammen arbeiten können“.<span id="more-637"></span></p>
<p>Für dieses Mamut-Benchmarking habe ich die TestSuite von Roman Borschel <a href="http://code.google.com/p/php-orm-benchmark/source/browse/#svn/trunk/doctrine_2" target="_blank">[Benchmark of the major PHP ORMs]</a> verwendet. Die TestSuite ist auch eine Art Benchmarking verschiedener ORM-Frameworks. Daraus habe ich mir jeweils das Benchmark für Doctrine2 und PDO rausgezogen und die Tests für meine Testzwecke erweitert. Anschließend habe ich ein neues Benchmark mit den nativen MySQL-Funktionen hinzugefügt. Das PDO- und das MySQL- Benchmark wurden als Active-Record implementiert.</p>
<p>Als Datenbank habe ich MySQL verwendet. Dort habe ich zwei Tabellen „Author“ und „Book“ mit einer 1:n Beziehung, Fremdschlüssel und Referenz-Optionen erstellt. Also ein Autor kann viele Bücher haben. Hier die Tabellendefinition und die SQL-Statements:</p>
<pre name="code" class="php">

Author
+-----------+--------------+------+-----+---------+----------------+
| Field     | Type         | Null | Key | Default | Extra          |
+-----------+--------------+------+-----+---------+----------------+
| id        | int(11)      | NO   | PRI | NULL    | auto_increment |
| firstName | varchar(128) | NO   |     | NULL    |                |
| lastName  | varchar(128) | NO   |     | NULL    |                |
| email     | varchar(128) | YES  |     | NULL    |                |
+-----------+--------------+------+-----+---------+----------------+

Book
+-----------+---------------+------+-----+---------+----------------+
| Field     | Type          | Null | Key | Default | Extra          |
+-----------+---------------+------+-----+---------+----------------+
| id        | int(11)       | NO   | PRI | NULL    | auto_increment |
| author_id | int(11)       | YES  | MUL | NULL    |                |
| title     | varchar(255)  | NO   |     | NULL    |                |
| isbn      | varchar(24)   | NO   |     | NULL    |                |
| price     | decimal(10,0) | NO   |     | NULL    |                |
+-----------+---------------+------+-----+---------+----------------+
</pre>
<p>
Hier die CREATE TABLE Statements, die von Doctrine2 generiert werden.<br />
</p>
<pre name="code" class="php">
CREATE TABLE `Author` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `firstName` varchar(128) NOT NULL,
  `lastName` varchar(128) NOT NULL,
  `email` varchar(128) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1701 DEFAULT CHARSET=utf8;

CREATE TABLE `Book` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `author_id` int(11) DEFAULT NULL,
  `title` varchar(255) NOT NULL,
  `isbn` varchar(24) NOT NULL,
  `price` decimal(10,0) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `IDX_6BD70C0FF675F31B` (`author_id`),
  CONSTRAINT `Book_ibfk_1` FOREIGN KEY (`author_id`) REFERENCES `Author` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1701 DEFAULT CHARSET=utf8;
</pre>
<p>Danach habe ich folgende Testszenarien definiert und pro Benchmark entsprechend umgesetzt:</p>
<p><strong>Test-Szenario 1:</strong><br />
Erstellen eines Modell-Objekts, Setzen und Speichern von Attributen. Hier wurde<br />
die Model-Objekt-Geschwindigkeit und die INSERT-Statement-Generierung gemessen. Je 1700 Mal ausgeführt</p>
<p><strong>Test-Szenario 2:</strong><br />
Sucht einen Eintrag anhand des Primärschlüssels. Hier wurden das grundlegende Abfrage-Prinzip und die Objekt-Hydratation gemessen. Je 1700 Mal ausgeführt.</p>
<p><strong>Test-Szenario 3:</strong><br />
Sucht einen Datensatz mit einer komplexen Abfrage. Hier wurde die Objektabfrage-Geschwindigkeit gemessen. Je 1900 Mal ausgeführt.</p>
<p><strong>Test-Szenario 4:</strong><br />
Liefert fünf Datensätze für ein einfaches Kriterium, etwa Book.price=???. Hier wurde die Hydratations Geschwindigkeit gemessen. Je 190 Mal ausgeführt.</p>
<p><strong>Test-Szenario 5: </strong><br />
Liefert einen Datensatz zusammen mit dem dazugehörigen Hydrat-Datensatz aus seiner Referenz-Tabelle. Hier wurde die Geschwindigkeit beim JOIN-Abfragen gemessen. Je 700 Mal ausgeführt.</p>
<p>Jede Abfrage-Möglichkeit wurde isoliert durch die oben beschriebenen Test-Szenarien ausgeführt. Dabei habe ich versucht, einen Zustand wie im Echt-Betrieb zu simulieren.</p>
<h3>Testergebnisse und harte Fakten</h3>
<p>1 Durchlauf zum aufwärmen:</p>
<pre name="code" class="php">
             | insert | find   | complex| hydrate| join   |  memory MB
             |--------|--------|--------|--------|--------|----------------
   MySQL	  |    471 |    374 |    228 |    292 |    275 |     0.25559375
   PDO  	  |    495 |    369 |    220 |    312 |    276 |  0.30880859375
   Doctrine2 |    293 |   1260 |    298 |   1478 |    883 |  6.73025390625
             |--------|--------|--------|--------|--------|----------------
</pre>
<p>2 Durchlauf:</p>
<pre name="code" class="php">
             | insert | find   | complex| hydrate| join   |  memory MB
             |--------|--------|--------|--------|--------|----------------
   MySQL     |    398 |    295 |    196 |    271 |    250 |     0.25559375
   PDO       |    454 |    336 |    205 |    285 |    269 |  0.30880859375
   Doctrine2 |    271 |   1110 |    291 |   1324 |   3390 |  6.67366796875
             |--------|--------|--------|--------|--------|----------------
</pre>
<p>3 Durchlauf:</p>
<pre name="code" class="php">
             | insert | find   | complex| hydrate| join   |  memory MB
             |--------|--------|--------|--------|--------|----------------
   MySQL     |    411 |    304 |    202 |    275 |    244 |     0.25559375
   PDO       |    441 |    333 |    208 |    284 |    260 |  0.30880859375
   Doctrine2 |    276 |   1158 |    297 |   1341 |    839 |  6.73586328125
             |--------|--------|--------|--------|--------|----------------
</pre>
<p>Hier die jeweiligen SQL-Statements in abgekürzter Form, um sehen zu können, was die obigen Test-Probanden an SQL im Hintergrund erzeugen</p>
<p><a href="https://gist.github.com/1205769" target="_blank">SQL-Statements erstellt von MySQL während des Benchmarks </a><br />
<a href="https://gist.github.com/1205765" target="_blank">SQL-Statements erstellt von PDO während des Benchmarks</a><br />
<a href="https://gist.github.com/1205757" target="_blank">SQL-Statements erstellt von Doctrine2 während des Benchmarks</a> </p>
<h3>Erkenntnisse</h3>
<p>Für die unten aufgeführten Erkenntnisse habe ich mich mehr auf Doctrine2 beschränkt.</p>
<h4>INSERT-Statements</h4>
<p>Alle INSERT-Statements werden immer gebündelt und gemeinsam in einer Transaktion ausgeführt. Man bekommt hierfür in etwa ein Gefühl dafür, wie die Geschwindigkeit sich bei einem Massen-Import verhält </p>
<h4>Suche anhand des Primärschlüssels</h4>
<p>Ohne es mit einen nativen PHP-MySql Abfrage-Art zu vergleichen, möchte ich behaupten, dass Doctrin2 langsamer ist. Hier haben wir jedoch ein ORM mit viel Abstraktion. Letztendlich ging das Abfragen mit DOctrine2 ganz bequem und das erwartete Reslut-Objekt wurde ausgeliefert. Doctrine2 bietet mehrere Möglichkeiten für das Ausliefern eines Abfrage-Ergebnisses: Als Modell-Objekt, als Array-Hydrat, als Scalar-Hydrat, als Modell-Objekt mit Verwendung des internen Array-Cache, als Modell-Objekt ohne Verwendung der internen Proxy-Klassen (zuständig für das Überladen) und als Iterator-Hydrat.</p>
<h4>Suche anhand einer komplexen Abfrage</h4>
<p>Hierfür wurde ein Statement mit Bedingungen WHERE+OR+CONCAT kombiniert verwendet. Die Suchergebnisse wurden ebenfalls schnell ausgeliefert. Dieselbe Abfrage mitunter Verwendung des internen Array-Cache war wie erwartet &#8211; schneller.</p>
<h4>Auslieferung von Suchergebnissen als nicht Doctrine2-Model-Objekte</h4>
<p>Wie oben im Text beschrieben, können die Suchergebnisse als Array oder Scalar-Object ausgeliefert werden. Diese können wiederum mit Verwendung vom internen Caching optional mitArray, Memory oder APC ausgeführt werden. Zudem hat man die Möglichkeit, das Ausliefern der Suchergebnisse ohne das interne Überladen (lazy-loading) durchzuführen. Diese Möglichkeit (also lazy-loading, überschreiben) sollte man jedoch auslassen, da sich die ausgelieferten Objektmodelle instabil zu ihren Entitäten verhalten können. Nichtsdestotrotz, alle drei Kombinationen haben sich als ähnlich schnell erwiesen, nur bei den JOIN-Statements war ein kleiner, jedoch unwesentlicher, Unterschied zu vermerken.</p>
<h4>Caching und Cache-Validation</h4>
<p>Doctrine2 bringt mehrere Caching Mechanismen: APC, Memcache, Xcache und ArrayCache. Es bietet die gängigen Caching Operationen. Das Finden und Löschen kann nach Angabe eines Suffixes, Präfixes, Regulären-Ausdrucks und Namespaces erfolgen. Es werden also viele Möglichkeiten geboten, nach bestimmten Kriterien eine Gruppe aus dem Cache zu identifizieren und oder sie zu löschen.</p>
<p>Hierfür habe ich einen ganz einfachen Test ausgeführt:</p>
<p>1. Gib mir das Modellobjekt aus dem Repository mit der id=1<br />
2. Speichere diese in den Cache unter der cacheId=cache_id123 für 3sek.<br />
3. Hole das Modell-Objekt aus dem Cache und vergleiche es mit dem Model-Oobjekt aus dem Repository.<br />
4. Halte den Prozess für vier Sekunden an und versuche, das Objekt aus dem Cache zu laden.<br />
<br />
Hier die Ausgabe in der Konsole:</p>
<pre>
---------- Repository before Cache ----------
Attribute: 1 color blue
Attribute: 1 name dog
Content: hallo wie geht es dir?
---------- Repository == Cache -------------
bool(false)
---------- Repository ---------------------
Attribute: 1 color blue
Attribute: 1 name dog
Content: hallo wie geht es dir?
---------- Cache --------------------------
Attribute: 1 color blue
Attribute: 1 name dog
Content: hallo wie geht es dir?
---------- Sleep 4sec, get data Cache ------
bool(false)
</pre>
<p></p>
<p>
Beide Objekte sind von derselben Instanz und haben denselben Inhalt, werden jedoch nicht als gleich identifiziert. Doctrin2 biete also das gängige Caching-Verhalten und sorgt nicht dafür, dass auch die Objekte im Cache konsistent zur Datenbank bleiben.</p>
<p>Weitere und detaillierte Information über den Cache gibt es hier: http://www.doctrine-project.org/docs/orm/2.0/en/reference/caching.html
</p>
<h4>Massen Inserts &#038; Massen Object Processing</h4>
<p>Doctrine2 unterstützt keine multi-INSERTs wie „(insert into (…) values (…), (…), (…),..“. Dafür werden die INSERT-Statements jeweils einzeln ausgeführt. Laut Doctrine2 soll das perfomanter sein. Zudem sind die Single-INSERT-Statements für Doctrine2 leichter in anderen DB-Systemen zu übertragen, als multi-INSERT-Statements. Für eine initiale Massen-Migration ist das Doctrine2 eher nicht gedacht. Jedoch bietet es eine Art von “Batch Processing”. Damit ist es möglich, die Massen-Migration von Daten in fest definierten Chargen abzuarbeiten bzw. in die Datenbank zu migrieren. Die Ausführungszeiten und der Speicherverbrauch sind mehr als akzeptabel. Wenn man oft mit sehr vielen Daten arbeiten muss, dann gibt es die Möglichkeit, sich die Daten als “Iterable-Result” ausliefern zu lassen und darüber zu iterieren. In diesem Fall ist das schneller, als wenn man sich die Daten als Hydrat-Objekt ausliefern lässt. Wem das aber immer noch nicht schnell genug ist, dem rät Doctrine2, interne Datenbank-Optionen für Massen-Importe zu verwenden. Wie bei MySQL “LOAD DATA INFILE”.</p>
<p>Mehr Information und einen ausführlichen Bericht hier:<br />
-http://www.doctrine-project.org/blog/doctrine2-batch-processing<br />
-http://www.doctrine-project.org/docs/orm/2.0/en/reference/batch-processing.html</p>
<h4>Arbeiten mit nicht relationalen Datenbanken</h4>
<p>Der Vorteil von NoSQL Datenbanken gegenüber MySQL oder jeder anderen RDBMS ist ganz klar die Performance. Für Doctrine2 gibt es PlungIns auf GitHub zum Herunterladen. Diese können laut Dokumentation auch alle gängigen Entitäten abbilden. Der Unterschied liegt in der Deklaration der Annotationen in den Modell-Klassen. Für ein Umsteigen von SQL auf NoSQL Datenbank ist also eine umfassende Anpassung an der Projekt-Konfiguration und der Modell-Klasse notwendig. Abfrage-Methoden wie find*(…) oder getRepository(…) müssen nicht angepasst werden.</p>
<p>Mehr Information:<br />
- http://www.doctrine-project.org/docs/mongodb_odm/1.0/en/index.html<br />
- http://www.doctrine-project.org/blog/mongodb-for-ecommerce<br />
- http://www.doctrine-project.org/docs/couchdb_odm/1.0/en/index.html<br />
- mongodb-odm https://github.com/doctrine/mongodb-odm<br />
- couchdb-odm https://github.com/doctrine/couchdb-odm</p>
<h4>Erstellen von Custom Mapping Types</h4>
<p>In Doctrine2 ist es möglich, auch eigene Spalten-Typen zu definieren. Ich habe es mit dem Typ “email” getestet. Dafür muss im Projekt ein neues Verzeichnis “Types” erstellt werden. In der Konfigurationsdatei muss der neue Typ bekannt gemacht werden:</p>
<pre name="code" class="php">
//...
use Doctrine\DBAL\Types\Type;
//...
Type::addType('email', 'Types\Email');
//...
$conn = $em->getConnection();
$conn->getDatabasePlatform()->registerDoctrineTypeMapping('email', 'email');
</pre>
<p>
Und so sieht die Klasse des neuen email-Types aus. Eine einfache E-Mail-Validierung ist auch eingebaut.<br />
</p>
<pre name="code" class="php">
namespace Types;
use Doctrine\DBAL\Types\ConversionException,
    Doctrine\DBAL\Types\Type,
    Doctrine\DBAL\Platforms\AbstractPlatform;
class Email extends Type
{
    const MYTYPE = 'email';
    /** @override */
    public function convertToDatabaseValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform)
    {
      if (false === filter_var($value, FILTER_VALIDATE_EMAIL))
      {
        throw ConversionException::conversionFailed($value, $this->getName());
      }
      return $value;
    }
    /** @override */
    public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
    {
      return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration);
    }
    /** @override */
    public function getDefaultLength(AbstractPlatform $platform)
    {
      return $platform->getVarcharDefaultLength();
    }
    public function getName()
    {
        return self::MYTYPE;
    }
}
</pre>
<p>Nach dem Setup, wurde die Tabelle Author wie erartet ohne Probleme erweitert und gültige E-Mails konnten abgespeichert werden. </p>
<h3>Alle Features in einer Tabelle</h3>
<table id="one-column-emphasis" summary="MySQL-Functions vs. PDO vs. Doctrine2">
<colgroup>
<col class="oce-first" />
    </colgroup>
<tbody>
<tr>
<td> <b>ORM</b> </td>
<td> <b>Prepared Statements</b> </td>
<td> <b>Transactions</b> </td>
<td> <b>Caching</b> </td>
<td> <b>Exceptions</b> </td>
<td> <b>Mass insert</b> </td>
<td> <b>Object Hydration</b> </td>
<td> <b>Scaffolding</b> </td>
<td> <b>Reverse Engineering</b> </td>
<td> <b>Drivers</b> </td>
</tr>
<tr>
<td style="text-align: right" class="align-right"> MySQL &amp; Active Record </td>
<td style="text-align: right" class="align-right"> &nbsp; </td>
<td> + </td>
<td style="text-align: right" class="align-right"> &nbsp; </td>
<td> </td>
<td> ++ </td>
<td style="text-align: right" class="align-right"> + </td>
<td style="text-align: right" class="align-right"> &nbsp; </td>
<td style="text-align: right" class="align-right"> &nbsp; </td>
<td> MySQL </td>
</tr>
<tr>
<td style="text-align: right" class="align-right"> PDO &amp; Active Record </td>
<td> ++ </td>
<td> + </td>
<td> +- </td>
<td> ++ </td>
<td> + </td>
<td style="text-align: right" class="align-right"> ++ </td>
<td style="text-align: right" class="align-right"> +- </td>
<td style="text-align: right" class="align-right"> &nbsp; </td>
<td style="text-align: left" class="align-left"> MySQL, Cubrid, FreeTDS, Firebird, DB2, Informix, Oracle, ODBC, PostgreSQL, SQLite, 4D, MS SQL </td>
</tr>
<tr>
<td style="text-align: left" class="align-left"> Doctrine2 </td>
<td> +++ </td>
<td> ++ </td>
<td> +++ </td>
<td> +++ </td>
<td> +++ </td>
<td style="text-align: right" class="align-right"> +++ </td>
<td style="text-align: right" class="align-right"> ++ </td>
<td style="text-align: right" class="align-right"> +++ </td>
<td> MySQL, Cubrid, FreeTDS, Firebird, DB2, Informix, Oracle, ODBC, PostgreSQL,SQLite, 4D, MS SQL, MongoDB, CouchDB </td>
</tr>
</tbody>
</table>
<p><strong>Prepared Statements:</strong> bei der MySQL Lösung muss man leider selber Methoden schreiben, um Prepared Statements ausführen zu können. PDO und DOctrine2 bringen das von Haus aus mit.</p>
<p><strong>Transactions:</strong> alle Test-Probanden unterstützen das. Wobei bei MySQL die Methoden erst geschrieben werden müssen. Lustig wird das bei Nested-Transactions. Doctrine2 kann das, für PDO und MySQL muss das implementiert werden.</p>
<p><strong>Caching:</strong> bei dem MySQL- und PDO-Probanden muss das Caching implementiert werden, Doctrine2 hingegen bringt das mit von Haus aus mit.</p>
<p><strong>Mass insert:</strong> das unterstützen alle drei Probanden. </p>
<p><strong>Exceptions:</strong> bei MySQL muss man die Exceptions implementieren. PDO und Doctrine2 bringen diese von Haus aus mit.</p>
<p><strong>Object Hydration:</strong> alle drei Probanden können ein Result-Set als Object zurückliefen. Doctrine2 kann jedoch noch mehr: als Array-Object, Iretable-Object und Model-Object.</p>
<p><strong>Scaffolding:</strong> bei den MySQL- und PDO-Probanden muss das erst implementiert werden, Doctrine2 bringt das von Haus aus mit.</p>
<p><strong>Reverse Engineering:</strong> nur der Doctrin2-Proband kann das. Ein paar mögliche Operationen wären: SQL-Datei in die Datenbank importieren, internes Caching Operationen, konvertieren von diversen Datenbank-Shema ins PHP, Model- und Mapper-Klassen aus der Datenbank generieren. </p>
<p><strong>Drivers:</strong> bei MySQL ist es klar, nur MySQL Datenbank. PDO kann viele SQL basierende Datenbanksysteme unterstützen. Bei Doctrine2 ist es genauso wie bei PDO, da es auf PDO basisiert. Für Doctrine2 gibt es zusätzlich noch Driver als Add-ons, die MongoDB oder CouchDB unterstützen.</p>
<h3>Gedanke</h3>
<p>Wenn ein Team zum ersten mal mit Modellen/Objekten arbeiten möchte, dann sollte es sich zuerst mit dem Active-Record-Pattern gut vertraut machen. Ist auch leicht zum umsetzen, kontrollieren, erweitern, optimieren und debuggen. Ein typisches ORM hingegen basiert auf das Repository-Pattern. Dieses macht viel Magie unter der Haube. Ist daher nicht einfach zu kontrollieren und debuggen. Man macht sich von ein ORM abhängig. Mann muss das ORM auch nicht komplett verwenden. Es reicht oft auch, wenn man nur das manipulieren der Datenbank dem ORM überlässt. Bei diesem Punkt möchte ich es nicht verallgemeinern, den der genaue Einsatz eines ORM hängt von der Anforderung des Projektes und Teams ab.</p>
]]></content:encoded>
			<wfw:commentRss>http://krsteski.de/php-tricks-und-tipps/mysql-functions-vs-pdo-vs-doctrine2.html/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>PCRE &#8211; der Stackspeicher Fresser</title>
		<link>http://krsteski.de/php-tricks-und-tipps/pcre-der-stack-speicher-fresser.html</link>
		<comments>http://krsteski.de/php-tricks-und-tipps/pcre-der-stack-speicher-fresser.html#comments</comments>
		<pubDate>Thu, 28 Jul 2011 08:37:03 +0000</pubDate>
		<dc:creator>Gjero Krsteski</dc:creator>
				<category><![CDATA[PHP Tricks und Tipps]]></category>
		<category><![CDATA[Errors]]></category>
		<category><![CDATA[PCRE]]></category>
		<category><![CDATA[Redesigns]]></category>
		<category><![CDATA[Refactoring]]></category>

		<guid isPermaLink="false">http://krsteski.de/?p=628</guid>
		<description><![CDATA[Als erstes möchte ich euch mitteilen, dass ich mich sehr freue diesen Beitrag zu schreiben.   Die letzten drei Monate war ich in Elternzeit und bin nicht zum Schreiben geschweige zum Programmieren gekommen. Es war aber eine sehr schöne Zeit für mich und die Familie. Nach dieser langen Programmier-Abstinenz wollte ich eine neue LAMP [...]]]></description>
			<content:encoded><![CDATA[<p>Als erstes möchte ich euch mitteilen, dass ich mich sehr freue diesen Beitrag zu schreiben.   Die letzten drei Monate war ich in Elternzeit und bin nicht zum Schreiben geschweige zum Programmieren gekommen. Es war aber eine sehr schöne Zeit für mich und die Familie. Nach dieser langen Programmier-Abstinenz wollte ich eine neue LAMP Entwicklungsumgebung Installieren. Kurz darauf habe ich einen in den Jahren gekommenen Laptop platt gemacht und eine neue Version von Ubuntu installiert. Das ging ganz schnell und einfach. Danach habe ich mittels „aptitude“, eine Erweiterung der Paketverwaltung APT welche auf allen Debian-basierten Systemen zum Einsatz komm, einen Apache2 Server, eine MySQL Datenbank, PHP, PEAR und PHPUnit installiert. Um das ganze zu testen habe ich ein im PHP5 erstelltes Projekt in der neuen LAMP Umgebung kopiert und die PHPUnit Tests gestartet. Alles lief recht schnell und ohne Probleme. Die Tests waren erfolgreich. Nun bin ich auch neugierig geworden und habe ein etwas älteres Projekt, das von PHP4 auf PHP5 umgeschrieben habe, ausprobiert. Hier handelt es sich um eine Schnittstelle die sehr viele Text-Dateien verarbeitet um den Inhalt anschließend in der Datenbank zu importiert. So, ab hier fangen die Probleme an.<span id="more-628"></span></p>
<p>Kurz nach dem ich den Test gestartet habe, meldete sich PHP mit einem PCRE Rekursionslimit-Fehler. Das Rekursionslimit sei 128 und darf nicht überschritten werden. Nach einer kurzen Recherche im Internet, habe ich erfahren, dass seit PHP5.2.0 zwei neue Einstellungen „pcre.recursion_limit“ und „pcre.backtrack_limit“ eingeführt worden sind. Diese sollen PHP vor komplexe oder fehlerhafte reguläre Ausdrücke schützen. Da sie zu viel Stackspeicher benötigen und PHP zum Absturz bringen können.</p>
<p><strong>Lösungsansatz 1:</strong><br />
Da es sich hier um eine Einstellungen im PHP handelt, können diese direkt in der php.ini oder gleich mit ini_set() auf einen höheren Wert gesetzt werden. Die php.ini lässt man besser in Ruhe, denn der Standartwert von hunderttausend ist ganz gut gewählt. Dafür sollten man besser darauf achten, keine Stackspeicher-Fresser zu programmieren. Eventuell nach Alternativen suchen oder native PHP String-Funktionen kombinieren. Nun, zurück zum Thema. Je nach dem, ist es auch möglich den Wert auch auf zehn Millionen zu setzen, aber nicht bevor man sich mit dem Teamkollegen oder Administrator konsultiert hat. Man sollte eben nicht den ganzen Speicher für PCRE Operationen reservieren.</p>
<pre name="code" class="php">
ini_set('pcre.recursion_limit',10000000);
ini_set('pcre.backtrack_limit',10000000);
</pre>
<p><strong>Lösungsansatz 2:</strong><br />
Ich gebe es zu, der erste Lösungsansatz war nicht der beste. Wenn man die Einstellungen zur Scriptlaufzeit ändert, riskiert man dadurch einen PHP Absturz, wenn der reguläre Ausdruck doch zu viel Speicher verbraucht. Eleganter ist es, wenn man es durch eine Fehlerprüfung absichert. Ganz besonders wenn man mit komplexen regulären Ausdrücken arbeitet oder reguläre Ausdrücke auf große Datenmengen anwendet. Der Abbruch im Script passiert also wenn ein regulärer Ausdruck das gesetzte Limit überschreitet. PHP liefert keine Fehlermeldung indem Fall, und der reguläre Ausdruck liefert NULL als Ergebnis. Dabei muss man unmittelbar nach der PCRE-Funktion mittels preg_last_error() prüfen, welcher Fehler aufgetreten ist. Dabei wird ein integer-Wert zurück geliefert. Die Werte sind ebenfalls als PHP-Konstanten vertreten.</p>
<pre name="code" class="php">
< ?php

preg_match('/(?:\D+|<\d+>)*[!?]/', '...big text....');

if (preg_last_error() == PREG_BACKTRACK_LIMIT_ERROR
	|| preg_last_error() == PREG_RECURSION_LIMIT_ERROR)
{
    throw new Exception('Regular Expression limit was exhausted!');
}

?>
</pre>
<p>Im den oberen Beispiel, wirft PHP eine Exception wenn der Speicher-Limit erreicht ist. So kann man besser den Fehler abfangen um entsprechend reagieren. Das Problem und die Folgefehler wurden somit behoben.</p>
]]></content:encoded>
			<wfw:commentRss>http://krsteski.de/php-tricks-und-tipps/pcre-der-stack-speicher-fresser.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Dependency-Injection ist nicht Inversion-of-Control</title>
		<link>http://krsteski.de/php-tricks-und-tipps/dependency-injection-ist-nicht-inversion-of-control.html</link>
		<comments>http://krsteski.de/php-tricks-und-tipps/dependency-injection-ist-nicht-inversion-of-control.html#comments</comments>
		<pubDate>Mon, 11 Apr 2011 09:26:40 +0000</pubDate>
		<dc:creator>Gjero Krsteski</dc:creator>
				<category><![CDATA[PHP Tricks und Tipps]]></category>
		<category><![CDATA[Dependency Injection]]></category>
		<category><![CDATA[Design Pattern]]></category>
		<category><![CDATA[Entwurfsmuster]]></category>
		<category><![CDATA[Inversion-of-Control]]></category>
		<category><![CDATA[Pattern]]></category>
		<category><![CDATA[Refactoring]]></category>

		<guid isPermaLink="false">http://krsteski.de/?p=612</guid>
		<description><![CDATA[Eine der Regel für gutes Software-Design besagt, dass es wichtig ist, eine feste Kopplung zwischen einzelnen Klassen Ihrer Anwendung zu vermeiden. Um dies erreichen zu können, muss man die Abhängigkeiten einer Klasse von außen in die Klasse injizieren. Hier sprechen wir also von der „Dependency-Injection“. Eine Injizierung kann ebenso über den Constructor oder über eine [...]]]></description>
			<content:encoded><![CDATA[<p>Eine der Regel für gutes Software-Design besagt, dass es wichtig ist, eine feste Kopplung zwischen einzelnen Klassen Ihrer Anwendung zu vermeiden. Um dies erreichen zu können, muss man die Abhängigkeiten einer Klasse von außen in die Klasse injizieren. Hier sprechen wir also von der „Dependency-Injection“. Eine Injizierung kann ebenso über den Constructor oder über eine Setter-Methode erfolgen. Hier ein Quellcode-Beispiel.<span id="more-612"></span></p>
<p><strong>Vorher: feste Kopplung</strong>
<pre name="code" class="php">
class Storage
{
    public function store()
    {
        // store it to a XML file.
    }

    // ....
}

class User
{
    protected $_name;

    public function save()
    {
        $storage = new Storage();
        $storage->store();

        // ....
    }
}
</pre>
<p><strong>Nachher: lose Kopplung</strong>
<pre name="code" class="php">
interface Storage
{
    public function store();
}

class XmlStorage implements Storage
{
    public function store()
    {
        // store data to XML file.
    }

    // ....
}

class MySqlStorage implements Storage
{
    public function store()
    {
        // store data to MySQL.
    }

    // ....
}

class User
{
    protected $_name;

    public function save(Storage $storage)
    {
        $storage->store();

        // ....
    }
}
</pre>
<p>Wir haben somit eine sehr lose Kopplung zwischen den einzelnen Komponenten erreicht. Die Klasse User weiß nicht welche Implementierung sie des Storage-Interface verwendet. Sie verlässt sich vollkommen darauf, dass die gewünschte Implementierung an die Klasse übergeben wird. Man nennt es auch die Umkehrung der Abhängigkeiten oder auch das Inversion-of-Control Prinzip. Die Klasse User selbst steuert nicht mehr, wie sie an ihre Abhängigkeiten kommt, der Kontrollfluss wird von außerhalb der Klasse gesteuert.</p>
<p>Trotzdem darf man das Inversion-of-Cointrol Prinzip mit der Dependency-Injection nicht gleich setzen. Inversion-of-Controll ist ein Prinzip was definiert, dass der Kontrollfluss nicht bei der Hauptanwendung liegt, sondern dieser an ein Framework abgegeben wird. Das Framework kümmert sich dann, dass innerhalb des Kontrollflusses die von der Anwendung bereitgestellten Funktionen und Anhängigkeiten aufgerufen werden. Die Klasse wartet darauf, dass sie von der Applikation an- bzw. aufgerufen wird und die Abhängigkeiten übergeben bekommt.</p>
]]></content:encoded>
			<wfw:commentRss>http://krsteski.de/php-tricks-und-tipps/dependency-injection-ist-nicht-inversion-of-control.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Static-Variablen in Methoden</title>
		<link>http://krsteski.de/php-tricks-und-tipps/static-variablen-in-methoden.html</link>
		<comments>http://krsteski.de/php-tricks-und-tipps/static-variablen-in-methoden.html#comments</comments>
		<pubDate>Thu, 10 Mar 2011 16:34:49 +0000</pubDate>
		<dc:creator>Gjero Krsteski</dc:creator>
				<category><![CDATA[PHP Tricks und Tipps]]></category>
		<category><![CDATA[OOP]]></category>
		<category><![CDATA[PHP-Core]]></category>
		<category><![CDATA[Pragmatismus]]></category>
		<category><![CDATA[Refactoring]]></category>

		<guid isPermaLink="false">http://krsteski.de/?p=593</guid>
		<description><![CDATA[Seit paar Tagen optimiere und refactoriere ich eine Komponente die bereits vor einem Jahr von PHP4 auf PHP5 umgestellt und refactoriert wurde. Dabei bin ich auf viele lustige Codefragmente gestoßen. Eins hat bei mir für viel Aufmerksamkeit gesorgt. Und zwar:  innerhalb einer Methode wurden Variablen die nur in dieser Methode verwendet werden als „static“ [...]]]></description>
			<content:encoded><![CDATA[<p>Seit paar Tagen optimiere und refactoriere ich eine Komponente die bereits vor einem Jahr von PHP4 auf PHP5 umgestellt und refactoriert wurde. Dabei bin ich auf viele lustige Codefragmente gestoßen. Eins hat bei mir für viel Aufmerksamkeit gesorgt. Und zwar:  innerhalb einer Methode wurden Variablen die nur in dieser Methode verwendet werden als „static“ Deklariert. Hmmm…, was soll das den bitte? Ist es ein Feature? Wenn ja, wo ist der Verteil? So habe ich mich auf der Suche gemacht. Hier ein Beispiel aus der Komponente:<span id="more-593"></span></p>
<pre name="code" class="php">
class SelectBox
{
    public function getTimeOptions()
    {
        static $hours, $mins, $options;

        $hours = range(0, 23, 1);
        $mins  = range(0, 45, 15);

        foreach ($hours as $hour)
        {
            foreach ($mins as $min)
            {
                $hour = ($hour == 0) ? '00' : $hour;
                $hour = (mb_strlen($hour) == 1) ? '0'.$hour : $hour;
                $min = ($min == 0) ? '00' : $min;

                $options[] = $hour.':'.$min;
            }
        }

      return $options;
    }
}
</pre>
<p>In den oben gezeigtem Beispiel ist es deutlich zu sehen, dass die alte Deklaration „global“ einfach durch „static“ ersetzt worden ist. Ob an diese Stelle richtig refactoriert worden ist, sei es mal hingestellt. Die Methode „getTimeOptions“ soll ein Array zurück liefern das alle Uhrzeiten zwischen 00 Uhr und 23 Uhr in 15 Minuten Schritte beinhaltet. Also, ein Array wie das hier:</p>
<pre name="code">
Array
(
    [0] => 00:00
    [1] => 00:15
    [2] => 00:30
    [3] => 00:45
    [4] => 01:00
    [5] => 01:15
    [6] => 01:30
    [7] => 01:45
    [8] => 02:00
    [9] => 02:15
    [10] => 02:30
    [11] => 02:45
    [12] => 03:00
    [13] => 03:15
    [14] => 03:30
    [15] => 03:45
    [16] => 04:00
    ...
    ..
    .
</pre>
<p>Nun habe ich mich genau über die „static“ Deklaration informiert und erfahren, dass auch in der Programmierung eine Variable als ein Akkumulator verwendet werden kann. Man kann sie also aufladen und entladen. Ist also besonders gut geeignet um Zwischenergebnisse, innerhalb der Laufzeit eines Prozesses abzulegen. Solche Akkumulatoren sollen auch in Schleifen innerhalb von PHP selbst verwendet werden. Hmm…, das klingt spannend! Also eine Art Cache bzw. Zwischenspeicher. Kurzerhand habe ich die Klasse umgebaut. Hier die Klasse nach dem ersten Umbau:</p>
<pre name="code" class="php">
class SelectBox
{
    public function getTimeOptions()
    {
        static $options;

        var_dump(empty($options));

        if (true === empty($options))
        {
            $hours = range(0, 23, 1);
            $mins  = range(0, 45, 15);

            foreach ($hours as $hour)
            {
                foreach ($mins as $min)
                {
                    $hour = ($hour == 0) ? '00' : $hour;
                    $hour = (mb_strlen($hour) == 1) ? '0'.$hour : $hour;
                    $min = ($min == 0) ? '00' : $min;

                    $options[] = $hour.':'.$min;
                }
            }
        }

        return $options;
    }

}

class CheckBox
extends SelectBox
{

}
</pre>
<p>Den Aufruf „var_dump(empty($options));“ habe ich absichtlich eingebaut um die Auswirkung von unseren Akkumulator deutlicher zu machen. Zudem habe ich die Klasse mit der „CheckBox“ Klasse erweitert. Einfach für testzwecke. Ich weiß ja nicht was mich erwartet. Hier der Test:</p>
<pre name="code" class="php">
$selectBox = new SelectBox();
print 'Instance of SelectBox'.PHP_EOL;
print 'CALL 1:';$selectBox->getTimeOptions();
print 'CALL 2:';$selectBox->getTimeOptions();
print 'CALL 3:';$selectBox->getTimeOptions();
print '----------------------------'.PHP_EOL;

$selectBox = new SelectBox();
print 'Instance of SelectBox'.PHP_EOL;
print 'CALL 1:';$selectBox->getTimeOptions();
print 'CALL 2:';$selectBox->getTimeOptions();
print 'CALL 3:';$selectBox->getTimeOptions();
print '----------------------------'.PHP_EOL;

$checkBox = new CheckBox();
print 'Instance of CheckBox'.PHP_EOL;
print 'CALL 1:';$checkBox->getTimeOptions();
print 'CALL 2:';$checkBox->getTimeOptions();
print 'CALL 3:';$checkBox->getTimeOptions();
print '----------------------------'.PHP_EOL;
</pre>
<p>Hier die Ausgabe:</p>
<pre name="code">
Instance of SelectBox
CALL 1:bool(true)
CALL 2:bool(false)
CALL 3:bool(false)
----------------------------
Instance of SelectBox
CALL 1:bool(false)
CALL 2:bool(false)
CALL 3:bool(false)
----------------------------
Instance of CheckBox
CALL 1:bool(true)
CALL 2:bool(false)
CALL 3:bool(false)
----------------------------
</pre>
<p>Das Ergebnis ist nicht verblüffend. Beim aller ersten Aufruf der Methode wird die Berechnung der Zeit-Optionen ausgeführt und in dem Akkumulator gespeichert.  Bei jedem weiterem Aufruf der Methode wird direkt auf dem Akkumulator zugegriffen. Auch wenn die Instanz von SelectBox neu erzeugt wurde. Bei der Instanz von CheckBox, die SelectBox erweitert, müssen die Zeit-Optionen wiederum neu berechnet werden. Ein Vorteil hat das nur wenn man sicher ist, dass eine Berechnung lange dauert und dasselbe Ergebnis im gesamten Prozess zurück liefert. Den Einsatz von der „static“ Deklaration betrachte ich mit gemischten Gefühlen. Einerseits hat das nichts mehr in der Objekt-Orientierten-Programmierung zu suchen, andererseits kann es doch nützlich sein, wenn man genau weiß wie es eingesetzt werden kann. </p>
<p>Hier habe ich ein endgültiges refactoring vollzogen. Denn mir war wichtig, dass die Methode flexibler wird, und mit anderen Uhrzeit- und Minuten-Abstände arbeiten kann.</p>
<pre name="code" class="php">
class SelectBox
{
    public function getTimeOptions(array $hours, array $mins)
    {
        static $options;

        if (true === empty($options))
        {
            foreach ($hours as $hour)
            {
                foreach ($mins as $min)
                {
                    $hour = (mb_strlen($hour) == 1) ? '0'.$hour : $hour;
                    $min = ($min == 0) ? '00' : $min;

                    $options[] = $hour.':'.$min;
                }
            }
        }

        return $options;
    }
}

$hours = range(0, 23, 1);
$mins  = range(0, 45, 15);
$selectBox = new SelectBox();
$selectBox->getTimeOptions($hours, $mins);
</pre>
]]></content:encoded>
			<wfw:commentRss>http://krsteski.de/php-tricks-und-tipps/static-variablen-in-methoden.html/feed</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>strncmp() ist schneller als substr()</title>
		<link>http://krsteski.de/php-tricks-und-tipps/strncmp-ist-schneller-als-substr.html</link>
		<comments>http://krsteski.de/php-tricks-und-tipps/strncmp-ist-schneller-als-substr.html#comments</comments>
		<pubDate>Wed, 23 Feb 2011 09:50:32 +0000</pubDate>
		<dc:creator>Gjero Krsteski</dc:creator>
				<category><![CDATA[PHP Tricks und Tipps]]></category>
		<category><![CDATA[Benchmarking]]></category>
		<category><![CDATA[PHP-Core]]></category>
		<category><![CDATA[Pragmatismus]]></category>

		<guid isPermaLink="false">http://krsteski.de/?p=573</guid>
		<description><![CDATA[In diversen Foren und Blogs wird schon seit 2007 geschrieben, dass strncmp() schneller als substr() sei. Und zwar, stolze 20%!!! Ist das denn so? Stimmt die Berechnung auch?  Nun, wie wir alle es wissen, Codefragmente die viel mit string-Typen zu tun haben, sind nicht Ressourcen-schonend und können den gesamten Prozess sehr verlangsamen. Um das [...]]]></description>
			<content:encoded><![CDATA[<p>In diversen Foren und Blogs wird schon seit 2007 geschrieben, dass strncmp() schneller als substr() sei. Und zwar, stolze 20%!!! Ist das denn so? Stimmt die Berechnung auch?  Nun, wie wir alle es wissen, Codefragmente die viel mit string-Typen zu tun haben, sind nicht Ressourcen-schonend und können den gesamten Prozess sehr verlangsamen. Um das Ganze besser vergleichen zu können, habe ich diese einfache Klasse „Benchmark_Substr_vs_Strncmp“ geschrieben. Mittels der oben genannten Funktionen wird versucht aus einem Test-Array, mit zwanzig Einträgen, den  Array-Key „GETME__“ zu ermitteln. Diese Suche wird mit beiden Funktionen je 10.000-mal ausgeführt. Bei jedem Testlauf wird die Dauer für die Suche in eine CSV Datei festgehalten. <span id="more-573"></span></p>
<pre name="code" class="php">
< ?php
class Benchmark_Substr_vs_Strncmp
{
    const LOOP = 10000;

    protected $_resultSubstr = 0;
    protected $_resultStrncmp = 0;

    protected $_testData = array(
        'GETME__3' => 3,
        'GETME__4' => 4,
        'DONT_GETME__1' => 1,
        'DONT_GETME__2' => 2,
        'DONT_GETME__3' => 3,
        'DONT_GETME__4' => 4,
        'GETME__5' => 5,
        'GETME__6' => 6,
        'DONT_GETME__6' => 6,
        'DONT_GETME__7' => 7,
        'DONT_GETME__8' => 8,
        'GETME__7' => 7,
        'GETME__8' => 8,
        'GETME__9' => 9,
        'DONT_GETME__5' => 5,
        'DONT_GETME__9' => 9,
        'GETME__1' => 1,
        'GETME__2' => 2,
		'GETME__1234' => 1,
        'GETME__2234' => 2,
    );

    protected function _substr_data()
    {
        $out = array();

        foreach ($this->_testData as $key => $val)
        {
            if (substr($key, 0, 7) == 'GETME__')
            {
                $out[$key] = $val;
            }
        }
    }

    protected function _strncmp_data()
    {
        $out = array();

        foreach ($this->_testData as $key => $val)
        {
            if (!strncmp($key, 'GETME__', 7))
            {
                $out[$key] = $val;
            }
        }
    }

    public function runBenchmarkOfStrncmp()
    {
        $beginn = microtime(true);

        $i = 0;

        while($i < self::LOOP)
        {
            $this->_strncmp_data();

            ++$i;
        }

        $this->_resultStrncmp = sprintf('%.3f', microtime(true) - $beginn);
    }

    public function runBenchmarkOfSubstr()
    {
        $beginn = microtime(true);

        $i = 0;

        while($i < self::LOOP)
        {
            $this->_substr_data();

            ++$i;
        }

        $this->_resultSubstr = sprintf('%.3f', microtime(true) - $beginn);
    }

    public function saveData()
    {
        $fp = fopen(dirname(__FILE__).'benchmark_substr_vs_strncmp.csv', 'a');

        fputcsv($fp, array($this->_resultSubstr, $this->_resultStrncmp), ';');
        fclose($fp);

        $this->_resultSubstr = 0;
        $this->_resultStrncmp = 0;

        print '.';
    }
}
</pre>
<p>Hier ein Testszenario:</p>
<pre name="code" class="php">
$benchmark = new Benchmark_Substr_vs_Strncmp();
$benchmark->runBenchmarkOfSubstr();
$benchmark->runBenchmarkOfStrncmp();
$benchmark->saveData();
</pre>
<p>Für den Test habe ich auf das System dafür gesorgt, dass dieser Test die einzige Klasse im System ist die was mit PHP macht. Hier das Ergebnis nach zwanzig Testszenarien.</p>
<pre name="code">
Substr	Strncmp		Differenz in Sek.	Differenz in %
--------------------------------------------------------------
0,227	0,2		0,027				11,89
0,222	0,199		0,023				10,36
0,216	0,198		0,018				8,33
0,217	0,199		0,018				8,29
0,232	0,213		0,019				8,19
0,217	0,201		0,016				7,37
0,223	0,207		0,016				7,17
0,213	0,199		0,014				6,57
0,21	0,198		0,012				5,71
0,215	0,204		0,011				5,12
0,207	0,198		0,009				4,35
0,205	0,198		0,007				3,41
0,207	0,202		0,005				2,42
0,2	0,198		0,002				1,00
0,209	0,207		0,002				0,96
0,199	0,198		0,001				0,50
0,202	0,201		0,001				0,50
0,206	0,205		0,001				0,49
0,199	0,199		0				0,00
</pre>
<p></p>
<p>Der Aufruf von substr() ist zwar relativ schnell, aber die widerholende Ausführung summiert sich. Ich habe bei meiner Berechnung die besagten 20% nicht erreicht, jedoch stolze 5%. Liegt es eventuell daran dass PHP5 schneller als PHP4 ist? Wie auch immer, ich bin mir sicher, dass man gut behaupten darf, dass schlecht implementierte Zeichenkettenanalysen in umfassenden Anwendungen oder Frameworks einen großen Teil der Zeit auf diese Funktion verwenden. Die strncmp() ist nur dann ein natürlicher Ersatz für substr(), wenn es um Zeichenvergleich am Anfang von Zeichenketten, handelt.</p>
<p>Die substr() Funktion muss erst ihren Rückgabewert zuweisen und schreiben, und dann einen Vergleich durchführen, während strncmp() nur Zeichen für Zeichen vergleicht. Wie die Speicherverwaltung bei PHP im Einzelnen funktioniert, das bleibt uns verborgen, dennoch werden wir die Zeit für die Zuweisung immer deutlich spüren.</p>
<p>Frage: Im Zend-Framework wird substr()  alleine 209-mal verwendet. Der Großteil davon wird verwendet um Zeichenketten am Anfang der Zeichenkette zu vergleichen. Und strncmp(), hingegen wird nur 2-mal verwendet. Was meint Ihr, würde ein Umbau auf strncmp(), eine Anwendung die komplett auf Zend-Framework aufbaut, beschleunigen?</p>
]]></content:encoded>
			<wfw:commentRss>http://krsteski.de/php-tricks-und-tipps/strncmp-ist-schneller-als-substr.html/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Ein Prozess im Unternehmen implementieren</title>
		<link>http://krsteski.de/projektmanagement/ein-prozess-im-unternehmen-implementieren.html</link>
		<comments>http://krsteski.de/projektmanagement/ein-prozess-im-unternehmen-implementieren.html#comments</comments>
		<pubDate>Fri, 04 Feb 2011 10:44:37 +0000</pubDate>
		<dc:creator>Gjero Krsteski</dc:creator>
				<category><![CDATA[Projektmanagement]]></category>
		<category><![CDATA[Lernbereitschaft]]></category>
		<category><![CDATA[Motivation]]></category>
		<category><![CDATA[Organisation]]></category>
		<category><![CDATA[Team]]></category>
		<category><![CDATA[Testgetriebene Entwicklung]]></category>

		<guid isPermaLink="false">http://krsteski.de/?p=561</guid>
		<description><![CDATA[In unseren Berufsalttag entwickeln wir eine Software. Wir erzeugen kein Gegenstand, sondern verrichten eine Tätigkeit, also ein Prozess, bei dem was hergestellt wird. Dabei ist es sehr wichtig sich auf dem Prozess und auf das Produkt zu konzentrieren. Kein Kunde wird sich freuen wenn sein Produkt nicht genau das leistet was er erwartet hat, oder [...]]]></description>
			<content:encoded><![CDATA[<p>In unseren Berufsalttag entwickeln wir eine Software. Wir erzeugen kein Gegenstand, sondern verrichten eine Tätigkeit, also ein Prozess, bei dem was hergestellt wird. Dabei ist es sehr wichtig sich auf dem Prozess und auf das Produkt zu konzentrieren. Kein Kunde wird sich freuen wenn sein Produkt nicht genau das leistet was er erwartet hat, oder wenn er das Produkt nicht rechtzeitig ausgeliefert bekommt.<span id="more-561"></span>Erfreulicherweise haben sich viele kluge Köpfe, schon vor langer Zeit, darüber Gedanke gemacht, wie man Software mit guter Qualität herstellt, und diese Erkenntnisse in sogenannte Methodologien festgehalten. </p>
<p>In der Realität schaut das anders aus, und zwar – die meisten Entwicklungsteams kommen kaum auf diese Erkenntnisse und Informationen. Für die meisten Teams ist ein Prozess ein banaler Gedanke, oder etwas was vom Management oder Vorstand aufgedrückt wird. Unter einer Methodologie stellen sich die meisten lange Konferenzen und viel Papier-Kram vor. Oft werden Methodologien von Managern aufgesetzt und Manager müssen oft einen Prozess verfolgen. Dabei handelt es sich um Prozesse die in den 80er definiert worden sind und in der heutigen Zeit nicht mehr passen. Diese alten Prozesse werden einfach neu aufbereitet, werden mit diversen Mode-Wörtern wie „agil“ aufgepeppt und an das Team aufgedrückt. Das lustige daran ist, dass neu angehende Manager die aus dem Team stammen, oft diese Prozesse einfach übernehmen und das ohne sie zu hinterfragen.</p>
<p>Doch es gibt bessere Wege für Teams gute Software zu entwickeln. Man arbeitet in Team vermutlich als Software-Designer, Programmierer oder Tester und man denkt, dass der Entwicklungsprozess nicht in seiner Verantwortung liegt. Das stimmt auch – für den Prozess ist leider niemand zuständig. Soll aber ein neues Entwicklungsprozess im Unternehmen erfolgreich implementiert werden, dann kann das nur der implementieren der ihn auch anwendet und versteht – also der Programmierer.</p>
<p>Nun, stellt sich die Frage: Wie spürt man die Verantwortung über einen Prozess? Die Antwort ist: Man hilft bei der Implementierung diesen Prozesses in eigenen Unternehmen bzw. Team. Wenn das Unternehmen keinen klaren Prozess anwendet, dann findet man heraus welche Methodologien für das Unternehmen passen würden. Also: SCRUM, V-Modell, Testgetriebenes-Entwickeln oder sonst was. Dafür diskutiert man am besten mit dem Team über die aktuellen Entwicklungsprobleme und Möglichkeiten, sie durch Übernahme eines Standartprozesses zu beseitigen. Danach erstellt man alleine oder besser mit dem Management einen Plan auf, um den gewählten Prozess in Unternehmen einzuführen. Dafür ist wichtig, dass man von jedem Teammitglied die Bereitschaft zugesichert bekommt, bei der Umsetzung des Plans, mitzumachen.</p>
<p>Es steht eine Vielzahl an Methodologien zur Verfügung. Trotzdem wird man nie ein Unternehmen vorfinden, das diese vollständig implementiert hat. Ja, das ist vollkommen in Ordnung! Der beste Prozess ist immer der, der das Team am produktivsten macht und die beste Software ausliefert. Es ist also möglich zwei Prozess-Typen zu finden. Dabei ist es wichtig, einzelne Komponenten auszuwählen, die in Team sinnvoll zu sein erscheinen, und fortlaufend anhand der Erfahrung zu verfeinern. </p>
<p>Letztendlich kann man, wen man den Prozess nicht beherrscht, das Produkt nicht herstellen. Kenntnisse über den Software-Entwicklungsprozess sind in diesem Fall nützlich. Denn, es ist viel einfacher jemanden zu finden der eine Software erstellen und zu laufen bringen kann, als jemanden zu finden, der den Prozess der Erstellung von Software erfolgreich im Unternehmen implementieren kann.</p>
]]></content:encoded>
			<wfw:commentRss>http://krsteski.de/projektmanagement/ein-prozess-im-unternehmen-implementieren.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>cURL vs. Fopen vs. ZF</title>
		<link>http://krsteski.de/php-tricks-und-tipps/curl-vs-fopen-vs-zf.html</link>
		<comments>http://krsteski.de/php-tricks-und-tipps/curl-vs-fopen-vs-zf.html#comments</comments>
		<pubDate>Thu, 27 Jan 2011 08:54:13 +0000</pubDate>
		<dc:creator>Gjero Krsteski</dc:creator>
				<category><![CDATA[PHP Tricks und Tipps]]></category>
		<category><![CDATA[Benchmarking]]></category>
		<category><![CDATA[cURL]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://krsteski.de/?p=552</guid>
		<description><![CDATA[In Moment ist ein sehr gefährlicher Infekt in der PHP-Szene namens “ PhpBenchmarkItis” in Umlauf. Wenn es erwischt hat, derjenige ist gezwungen Dinge in PHP miteinander auf Leistung und Schnelligkeit gegeneinander zu prüfen. Mich hat die “ PhpBenchmarkItis” nun auch erwischt. Ich musste unbedingt erfahren welche Methode von PHP und ZF eine schnellere Verbindung zu [...]]]></description>
			<content:encoded><![CDATA[<p>In Moment ist ein sehr gefährlicher Infekt in der PHP-Szene namens “ PhpBenchmarkItis” in Umlauf. Wenn es erwischt hat, derjenige ist gezwungen Dinge in PHP miteinander auf Leistung und Schnelligkeit gegeneinander zu prüfen. Mich hat die “ PhpBenchmarkItis” nun auch erwischt. Ich musste unbedingt erfahren welche Methode von PHP und ZF eine schnellere Verbindung zu einem Server aufbaut und dessen Antwort bzw. Ergebnis ausliefert. Hintergrund des Ganzen ist, eine Klasse zu erstellen die auf dem schnellsten Wege verschiedene RSS-Feeds auslesen und ausliefern kann.<span id="more-552"></span></p>
<p><strong>Die Testumgebung</strong><br />
Server = Debian 5.0.4<br />
Processors =  Intel(R) Xeon(R) CPU E5335 @ 2.00GHz<br />
PHP Version = 5.3.3-0.dotdeb.1<br />
allow_url_fopen = On<br />
cURL Version =  7.18.2<br />
ZendFramework Version = 1.10.0</p>
<p><strong>Das Test-Scenario</strong><br />
Als Testumgebung dient PHPUnit, da jeder Test isoliert ausgeführt werden kann. Jede Methode ruft den RSS-Feed (http://krsteski.de/feed/rss) auf dieses Blog 50-mal auf und liefert ebenso 50-mal den Inhalt zurück. Gut, dieser Blog ist eine WordPress Instanz und braucht etwas zu antworten, jedoch alles in akzeptablen Zeitrahmen. Getestet werden hier: file_get_contents, fopen, Zend_Http_Client, Zend_Feed_Reader und cURL.</p>
<pre name="code" class="php">
< ?php
class Feed_PhpBenchmarkItis extends PHPUnit_Framework_TestCase
{
    const LOOP = 50;
    const FEED = 'http://krsteski.de/feed/rss';

    public function testZendFeed()
    {
        $beginn = microtime(true);

        $i = 0;
        while($i < self::LOOP)
        {
            $feed = Zend_Feed_Reader::importFile(self::FEED);
            $feed->getFeedLink();

            ++$i;
        }

        $dauer = sprintf('%.3f', microtime(true) - $beginn);

        echo self::LOOP." ZENDFEED Anfragen in: ".
                $dauer." Sek. durchschnittlich ".
                ($dauer / self::LOOP)." Sek. pro Anfrage".PHP_EOL;
    }

    public function testFILE_GET_CONTENTS()
    {
        $beginn = microtime(true);

        $i = 0;
        while($i < self::LOOP)
        {
            $contents = file_get_contents(self::FEED);

            ++$i;
        }

        $dauer = sprintf('%.3f', microtime(true) - $beginn);

        echo self::LOOP." FILE_GET_CONTENTS Anfragen in: ".
                $dauer." Sek. durchschnittlich ".
                ($dauer / self::LOOP)." Sek. pro Anfrage".PHP_EOL;

    }

    public function testFOPEN ()
    {
        $beginn = microtime(true);

        $i = 0;
        while($i < self::LOOP)
        {
            $handle = fopen(self::FEED, "r");
            $contents = stream_get_contents($handle, -1);
            fclose($handle);

            ++$i;
        }

        $dauer = sprintf('%.3f', microtime(true) - $beginn);

        echo self::LOOP." FOPEN Anfragen in: ".
                $dauer." Sek. durchschnittlich ".
                ($dauer / self::LOOP)." Sek. pro Anfrage".PHP_EOL;

    }

    public function testCURL()
    {
        $beginn = microtime(true);

        $i = 0;
        while($i < self::LOOP)
        {
            $handle = curl_init(self::FEED);
            curl_setopt($handle, CURLOPT_HEADER, false);
            curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
            $contents = curl_exec($handle);
            curl_close($handle);

            ++$i;
        }

        $dauer = sprintf('%.3f', microtime(true) - $beginn);

        echo self::LOOP." CURL Anfragen in: ".
            $dauer." Sek. durchschnittlich ".
            ($dauer / self::LOOP)." Sek. pro Anfrage".PHP_EOL;
    }

    public function testZendHttpClient()
    {
        $beginn = microtime(true);

        $i = 0;
        while($i < self::LOOP)
        {
            $urlClient = new Zend_Http_Client(self::FEED);
            $contents = $urlClient->request()->getRawBody();

            ++$i;
        }

        $dauer = sprintf('%.3f', microtime(true) - $beginn);

        echo self::LOOP." ZEND_HTTP_CLIENT Anfragen in: ".
            $dauer." Sek. durchschnittlich ".
            ($dauer / self::LOOP)." Sek. pro Anfrage".PHP_EOL;
    }
}
</pre>
<p><strong>Das Testergebnis</strong></p>
<pre>
50 ZENDFEED Anfragen in: 4.007 Sek. durchschnittlich 0.08014 Sek. pro Anfrage
50 FILE_GET_CONTENTS Anfragen in: 4.016 Sek. durchschnittlich 0.08032 Sek. pro Anfrage
50 FOPEN Anfragen in: 6.295 Sek. durchschnittlich 0.1259 Sek. pro Anfrage
50 CURL Anfragen in: 3.564 Sek. durchschnittlich 0.07128 Sek. pro Anfrage
50 ZEND_HTTP_CLIENT Anfragen in: 3.999 Sek. durchschnittlich 0.07998 Sek. pro Anfrage
Time: 23 seconds, Memory: 9.75Mb
</pre>
<p></p>
<p>
Wie erwartet ist hier der Aufruf mit cURL schneller. Bei cURL habe ich jedoch das Problem, dass ich hier nicht genau weiß ob es sich um ein valides RSS-Feed handelt. Zudem muss ich das Ergebnis das cURL zurück liefert, irgendwie parsen um es weiter verarbeiten zu können. Diese Logik muss also zusätzlich implementiert werden. Ich habe mich für Zend_Feed_Reader entschieden und verzichte gerne auf bisschen Zeit und bekomme dafür mehr Sicherheit. Zudem der Zend_Feed_Reader kann das RSS-Feed validieren, löst entsprechenden Exceptions falls es irgendwelche Probleme mit dem RSS-Feed gibt und liefert das Ergebnis als eine DOMDocument Instanz zurück. Und das ist ja spitze! Anhand der DOMDocument Instanz kann ich nun ganz bequem über den Datenbestand iterieren.</p>
]]></content:encoded>
			<wfw:commentRss>http://krsteski.de/php-tricks-und-tipps/curl-vs-fopen-vs-zf.html/feed</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Web-Rocker&#8217;s Showdown</title>
		<link>http://krsteski.de/externe-news-posts/web-rocker-s-showdown.html</link>
		<comments>http://krsteski.de/externe-news-posts/web-rocker-s-showdown.html#comments</comments>
		<pubDate>Wed, 19 Jan 2011 08:04:26 +0000</pubDate>
		<dc:creator>Gjero Krsteski</dc:creator>
				<category><![CDATA[Externe News & Posts]]></category>
		<category><![CDATA[Benchmarking]]></category>
		<category><![CDATA[Cucumber]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://krsteski.de/?p=539</guid>
		<description><![CDATA[Diesmal habe ich was ganz anderes für euch. Ich würde euch gerne den Blog web-rocker.de meines Teamkollegen, Marco Fischer vorstellen. Marco liebt es Sachen mit und im PHP auseinander zu nehmen, sie zu vergleichen und zu messen. Außerdem ist er in Moment sehr fasziniert von Ruby On Rails. Sein Blog ist noch ganz frisch, und [...]]]></description>
			<content:encoded><![CDATA[<p>Diesmal habe ich was ganz anderes für euch. Ich würde euch gerne den Blog web-rocker.de meines Teamkollegen, Marco Fischer vorstellen. Marco liebt es Sachen mit und im PHP auseinander zu nehmen, sie zu vergleichen und zu messen. Außerdem ist er in Moment sehr fasziniert von Ruby On Rails. Sein Blog ist noch ganz frisch, und eigentlich wollte ich etwas warten bis sein Blog gut gewachsen ist bevor ich ihn euch vorstelle. Jedoch die paar Themen über er bereits jetzt geschrieben hat, sind sehr heiß und bewirken Appetit auf mehr. Aktuelle Artikel sind: <a href="http://web-rocker.de/2011/01/php-applikationen-mit-cucumber-testen/">PHP-Applikationen mit Cucumber testen</a>, <a href="http://web-rocker.de/2011/01/zend-framework-vs-ruby-on-rails/">Zend Framework vs. Ruby On Rails</a> und <a href="http://web-rocker.de/2011/01/apache-2-itk-mpm/">Apache 2 ITK MPM</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://krsteski.de/externe-news-posts/web-rocker-s-showdown.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Crashkurs &#8211; Testgetriebene Entwicklung</title>
		<link>http://krsteski.de/externe-news-posts/crashkurs-testgetriebene-entwicklung.html</link>
		<comments>http://krsteski.de/externe-news-posts/crashkurs-testgetriebene-entwicklung.html#comments</comments>
		<pubDate>Sat, 18 Dec 2010 16:12:56 +0000</pubDate>
		<dc:creator>Gjero Krsteski</dc:creator>
				<category><![CDATA[Externe News & Posts]]></category>
		<category><![CDATA[Bildung]]></category>
		<category><![CDATA[Lernbereitschaft]]></category>
		<category><![CDATA[PHPUnit]]></category>
		<category><![CDATA[TDD]]></category>
		<category><![CDATA[Test Driven Development]]></category>
		<category><![CDATA[Testgetriebene Entwicklung]]></category>

		<guid isPermaLink="false">http://krsteski.de/?p=469</guid>
		<description><![CDATA[Letzte Woche habe ich einen zweitägigen Workshop zum Thema &#8220;Testgetriebene Entwicklung&#8221; bei mir im Unternehmen verantwortlich gestaltet. Ich wollte euch hierüber alles schreiben: Was bedeutet das Thema? Was ist Testgetriebene Entwicklung? Wie geht man vor? Und vieles mehr.
Mein Tipp: schaut euch, wie ich das gemacht habe, vier geniale Videos bei youtube.com an. Die Videos sind [...]]]></description>
			<content:encoded><![CDATA[<p>Letzte Woche habe ich einen zweitägigen Workshop zum Thema &#8220;Testgetriebene Entwicklung&#8221; bei mir im Unternehmen verantwortlich gestaltet. Ich wollte euch hierüber alles schreiben: Was bedeutet das Thema? Was ist Testgetriebene Entwicklung? Wie geht man vor? Und vieles mehr.<br />
Mein Tipp: schaut euch, wie ich das gemacht habe, vier geniale Videos bei youtube.com an. Die Videos sind von Henning Koch von der makandra GmbH und vermitteln euch alles Wissenswerte zum Thema Testgetriebene Entwicklung. Laut Henning Koch richten sich die vier Video-Crashkurse an Software-Entwickler, die einen schnellen Einstieg in die Testgetriebene Entwicklung suchen. Die vorgestellten Techniken sind für alle Sprachen relevant, egal ob Ruby on Rails, PHP, Java, Python, C# oder andere. Die Themen beinhalten unter anderem: Was sind Tests? Ein einfaches Beispiel, Testgetriebenes Implementieren von neuen Features, Testgetriebenes Bugfixing, den Überblick behalten: Kontextbeschreibungen, mit Datenbanken testen, Testumgebungen, Testdaten effizient erzeugen, Factories, Code mit Nebenwirkungen bändigen, Stubs und Mocks, Das Frontend (die GUI) testen, Scriptbare Browser.<span id="more-469"></span></p>
<p><strong>TDD Crashkurs #1</strong><br />
<object width="640" height="385"><param name="movie" value="http://www.youtube.com/v/zSOvpOxbe08?fs=1&amp;hl=de_DE&amp;rel=0"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/zSOvpOxbe08?fs=1&amp;hl=de_DE&amp;rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"></embed></object></p>
<p><strong>TDD Crashkurs #2</strong><br />
<object width="640" height="385"><param name="movie" value="http://www.youtube.com/v/jyElDp98HdI?fs=1&amp;hl=de_DE&amp;rel=0"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/jyElDp98HdI?fs=1&amp;hl=de_DE&amp;rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"></embed></object></p>
<p><strong>TDD Crashkurs #3</strong><br />
<object width="640" height="385"><param name="movie" value="http://www.youtube.com/v/95XBNzOWsvc?fs=1&amp;hl=de_DE&amp;rel=0"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/95XBNzOWsvc?fs=1&amp;hl=de_DE&amp;rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"></embed></object></p>
<p><strong>TDD Crashkurs #4</strong><br />
<object width="640" height="385"><param name="movie" value="http://www.youtube.com/v/OTs6K8l0J-Q?fs=1&amp;hl=de_DE&amp;rel=0"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/OTs6K8l0J-Q?fs=1&amp;hl=de_DE&amp;rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"></embed></object></p>
<p>p.s.<br />
2010 neigt sich dem Ende zu und ich habe zwei Wochen Urlaub. Daher ist dies der letzte Beitrag für dieses Jahr. 2011 geht es wie gewohnt weiter. Ich wünsche meinen Lesern ein frohes Fest und einen guten Rutsch ins Jahr 2011.</p>
]]></content:encoded>
			<wfw:commentRss>http://krsteski.de/externe-news-posts/crashkurs-testgetriebene-entwicklung.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic Page Served (once) in 0.544 seconds -->

