<?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>ebene7 &#187; Reflection</title>
	<atom:link href="http://blog.ebene7.com/schlagwort/reflection/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.ebene7.com</link>
	<description></description>
	<lastBuildDate>Tue, 04 Jun 2013 18:57:27 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Symfony2: Event-Listener über Annotations registrieren</title>
		<link>https://blog.ebene7.com/2012/02/27/symfony2-event-listener-ueber-annotations-registrieren/</link>
		<comments>https://blog.ebene7.com/2012/02/27/symfony2-event-listener-ueber-annotations-registrieren/#comments</comments>
		<pubDate>Mon, 27 Feb 2012 05:00:04 +0000</pubDate>
		<dc:creator>Daniel</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Annotations]]></category>
		<category><![CDATA[Eventhandling]]></category>
		<category><![CDATA[Observer]]></category>
		<category><![CDATA[Reflection]]></category>
		<category><![CDATA[Symfony2]]></category>

		<guid isPermaLink="false">http://blog.ebene7.com/?p=3449</guid>
		<description><![CDATA[In den letzten Monaten habe ich hier leider nicht sehr viel geschrieben und freue mich daher um so mehr, wenn ich dann mal wieder über ein spanndendes Thema schreiben kann. So dann auch, wie ich finde, heute. Es geht um &#8230; <a href="https://blog.ebene7.com/2012/02/27/symfony2-event-listener-ueber-annotations-registrieren/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>In den letzten Monaten habe ich hier leider nicht sehr viel geschrieben und freue mich daher um so mehr, wenn ich dann mal wieder über ein spanndendes Thema schreiben kann. So dann auch, wie ich finde, heute.</p>
<p>Es geht um zwei einfache, aber zugleich sehr nützliche Dinge, die das PHP-Framework Symfony2 mitbringt: Ein einfaches, flexibles Event-Handling und Annotations.<span id="more-3449"></span></p>
<p>Für mich waren die letzten drei Wochen auch die ersten Gehversuche mit Symfony2 und bislang wurden alle meine Erwartungen bestätigt. Das Framework ist sehr flexibel, aber auch recht komplex. Wer allerdings andere MVC-Frameworks wie Zend kennt, der wird sich auch hier schnell zurecht finden.</p>
<p>So, genug Lob an der Stelle. Neben der positiven Eigenschafft eines Eventhandlingsystems fiel mir auch gleich die Art auf, wie sich in Symfony2 Controllerrouten mit Annotations konfigurieren lassen.</p>
<p>Leider habe ich nichts Vergleichbares für Eventlistener gefunden und die klassische Konfiguration über YML gefiel mir dann auch nicht mehr so recht. Das nahm ich dann also gleich mal als praxisnahes Kennenlernbeispiel her und habe mir da was gebaut.</p>
<p>Als erstes braucht es immer einen Auslöser, damit die Annotations verarbeitet werden. Dafür habe ich mich an das Event &#8220;kernel.request&#8221; gehängt, das noch vor dem Kontrolleraufruf verarbeitet werden kann.</p>
<p>Einen speziellen Listener hatte ich erst dazwischen, habe es dann später aber wieder etwas vereinfacht. Mein Code entspricht vielleicht nicht allen Symfony-Richtlinien, aber zum Arbeiten und Erklären reicht es aus.</p>
<p>Zum Verarbeiten der Annotations habe ich eine einfache Klasse aufgebaut und mich dabei an den &#8220;Route()&#8221;-Annotations orientiert. Die Methode &#8220;findClass()&#8221; ist aus bestehendem Code &#8220;recycled&#8221;, da sie anders schwerer erreichbar wäre.</p>
<pre>&lt;?php
namespace MyNamespace\Annotations;

use Doctrine\Common\Annotations\Reader;
use Symfony\Component\EventDispatcher\EventDispatcher;

class AnnotatedEventClassLoader
{
  private $reader;
  private $eventDispatcher;
  private $eventAnnotationClass  = 'MyNamespace\\Annotations\\Event';

  /**
   * Constructor
   *
   * @param    Reader $reader
   * @param    EventDispatcher $eventDispatcher
   * @return    void
   */
  public function __construct(
    Reader $reader, EventDispatcher $eventDispatcher) {
    $this-&gt;reader = $reader;
    $this-&gt;eventDispatcher = $eventDispatcher;
  }

  public function init() {
    $listenersDir = realpath(__DIR__ . '/../../Listener');

    $iterator = new \RecursiveIteratorIterator(
      new \RecursiveDirectoryIterator($listenersDir),
        \RecursiveIteratorIterator::LEAVES_ONLY);

    foreach ($iterator as $file) {
      if (!$file-&gt;isFile() || '.php' !== substr($file-&gt;getFilename(), -4)) {
        continue;
      }

      if ($class = $this-&gt;findClass($file)) {
        $reflClass = new \ReflectionClass($class);
        if ($reflClass-&gt;isAbstract()) { continue; }

        foreach($reflClass-&gt;getMethods() as $method) {
          $annotation = $this-&gt;reader-&gt;getMethodAnnotation($method, $this-&gt;eventAnnotationClass);
          if (!$annotation instanceof Event || empty($annotation)) { continue; }
          $this-&gt;registerEvents($annotation, $class, $method-&gt;getName());
        }
      }
    }
  }

  /**
   *
   * @param    \MyNamespace\Annotations\Event $eventAnnotation
   * @param    string|object $class
   * @param    string $method
   * @return   \MyNamespace\Annotations\AnnotatedEventClassLoader
   */
  public function registerEvents($eventAnnotation, $class, $method) {
    $listener = is_object($class) ? $class : new $class();

    foreach ($eventAnnotation-&gt;getEvents() as $event) {
      $this-&gt;eventDispatcher-&gt;addListener(
        $event, array($listener, $method), $eventAnnotation-&gt;getPriority()
      );
    }
    return $this;
  }

  /**
   * Returns the full class name for the first class in the file.
   *
   * @param string $file A PHP file path
   *
   * @return string|false Full class name if found, false otherwise
   */
  protected function findClass($file) {
    $class = false;
    $namespace = false;
    $tokens = token_get_all(file_get_contents($file));
    for ($i = 0, $count = count($tokens); $i &lt; $count; $i++) {
      $token = $tokens[$i];

      if (!is_array($token)) { continue; }

      if (true === $class &amp;&amp; T_STRING === $token[0]) {
        return $namespace.'\\'.$token[1];
      }

      if (true === $namespace &amp;&amp; T_STRING === $token[0]) {
        $namespace = '';
        do {
          $namespace .= $token[1];
          $token = $tokens[++$i];
         } while ($i &lt; $count &amp;&amp; is_array($token) &amp;&amp; in_array($token[0], array(T_NS_SEPARATOR, T_STRING)));
       }

       if (T_CLASS === $token[0]) {
         $class = true;
       }

       if (T_NAMESPACE === $token[0]) {
         $namespace = true;
       }
     }

     return false;
   }
}</pre>
<p>Damit der Loader später auch seinen Dienst tut, muss er noch als Listener für das &#8220;kernel.request&#8221;-Event bekannt gemacht werden. Dafür habe ich die Datei &#8220;app/config/annotations.xml&#8221; mit folgendem Inhalt angelegt.</p>
<pre>&lt;?xml version="1.0" ?&gt;

&lt;container xmlns="http://symfony.com/schema/dic/services"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"&gt;

  &lt;parameters&gt;
    &lt;parameter key="myapp.event.loader.class"&gt;MyNamespace\Annotations\AnnotatedEventClassLoader&lt;/parameter&gt;
  &lt;/parameters&gt;

  &lt;services&gt;
    &lt;service id="myapp.event.loader"&gt;
      &lt;tag name="kernel.event_listener" event="kernel.request" method="init" /&gt;
      &lt;argument type="service" id="annotation_reader" /&gt;
      &lt;argument type="service" id="event_dispatcher" /&gt;
    &lt;/service&gt;
  &lt;/services&gt;
&lt;/container&gt;</pre>
<p>Die Konfiguration muss dann auch noch der AppKernel-Klasse mitgeteilt werden und damit ist der Loader eingebunden.</p>
<pre>class AppKernel extends Kernel {
  // other methods...

  public function registerContainerConfiguration(LoaderInterface $loader) {
    // ...
    $loader-&gt;load(__DIR__ . '/config/annotations.xml');
  }
}</pre>
<p>Annotations werden durch eigene Klassen verarbeitet und können dadurch mit beliebiger Logik versehen werden. Alles was einer Annotation als Parameter mitgegeben wird, bekommt die Klasse als Array im Constructor übergeben.</p>
<p>Für die Registrierung von Eventlistener brauchte ich nur die Events, die hier kommasepariert übergeben werden und gegebenenfalls noch die Priotität, in dem Fall optional. Die Klasse selber wird auch durch eine Annotation als solche gekennzeichnet.</p>
<pre>&lt;?php
namespace MyNamespace\Annotations;

/**
 * @Annotation
 */
class Event {
  private $events = array();
  private $priority = 0;

  public function __construct($options) {
    if (isset($options['value'])) {        
      $this-&gt;setEvents($options['value']);
    }

    $this-&gt;setPriority(
      isset($options['priority']) ? $options['priority'] : 0 );
  }

  public function getEvents() {
    return $this-&gt;events;
  }

  public function setEvents($events) {
    if(!is_array($events)) {
      $events = array_map('trim', explode(',', $events));
    }
    $this-&gt;events = $events;
    return $this;
  }

  public function getPriority() {
    return $this-&gt;priority;
  }

  public function setPriority($priority) {
    $this-&gt;priority = (int)$priority;
      return $this;
  }
}</pre>
<p>Nun sollte alles an PHP-Code und Konfiguration an seinem Platz sein, aber wie funktioniert das jetzt genau?</p>
<p>Durch das Event wird der Loader benachrichtigt, dass die Listener initialisiert werden sollen. Dazu wird das Verzeichnis &#8220;Listener&#8221; nach Klassen mit Annotations durchsucht, welche dann von der Annotation-Event-Klasse verarbeitet werden. Innerhalb des Loader werden dann die Listener für die Events registriert und können von da an verwendet werden.</p>
<p>Die Untersuchung des PHP-Codes funktioniert mit Reflections, was ansich immer den Ruf hat langsam zu sein. Der AnnotationReader verfügt über ein eigenes Caching und macht sich daher zeitlich nicht negativ bemerkbar.</p>
<p>Der Loader durchsucht das gesamte Verzeichnis in beliebiger Tiefe nach Dateien, daher bleibt man in der Namensvergabe frei.</p>
<p>Ein Listener könnte dann zum Beispiel so aussehen:</p>
<pre>&lt;?php
namespace MyNamespace\Bundle\Listener\Deep\Folder\Structure\Test;

use MyNamespace\Annotations\Event;

class Listener {
    /**
     * @Event("foo.bar, bar.foo", priority="1")
     * @param type $event
     */
    public function onFooBar1($event) {
        echo __METHOD__;
    }

    /**
     * @Event("foo.bar", priority="20")
     * @param type $event
     */
    public function onFooBar2($event) {
        echo __METHOD__;
    }
}</pre>
<p>Ein Aufruf aus dem Kontroller funktioniert dann wie gewohnt über den EventDispatcher, der fast überall als Service erreichbar ist.</p>
<pre>public function indexAction() {        
  $this-&gt;get('event_dispatcher')-&gt;dispatch('foo.bar');
}

// oder</pre>
<pre>public function indexAction() {
  $someEvent = new SomeEvent();       
  $this-&gt;get('event_dispatcher')-&gt;dispatch('foo.bar', $someEvent);
}</pre>
<p>Wie gesagt sind das meine ersten Versuche mit Symfony und der Code ist nicht optimiert. Die Funktionen des Loaders sollten noch weiter in Unterobjekte verteilt werden, damit das ganze noch flexibler wird und auch der gecodete Teil mit dem Verzeichnisnamen ist verbesserungfähig.</p>
<p>Ich konnte dabei wieder ein paar Sachen lernen und vielleich spart es ja dem einen oder anderen von euch etwas Zeit, falls ihr das braucht.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.ebene7.com/2012/02/27/symfony2-event-listener-ueber-annotations-registrieren/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Array als Parameterliste an den Konstruktor übergeben</title>
		<link>https://blog.ebene7.com/2011/03/21/array-als-parameterliste-an-den-konstruktor-uebergeben/</link>
		<comments>https://blog.ebene7.com/2011/03/21/array-als-parameterliste-an-den-konstruktor-uebergeben/#comments</comments>
		<pubDate>Mon, 21 Mar 2011 05:00:51 +0000</pubDate>
		<dc:creator>Daniel</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Tipps und Tricks]]></category>
		<category><![CDATA[Callback]]></category>
		<category><![CDATA[Factory]]></category>
		<category><![CDATA[Parameter]]></category>
		<category><![CDATA[Reflection]]></category>

		<guid isPermaLink="false">http://blog.ebene7.com/?p=2797</guid>
		<description><![CDATA[In verschiedenen Fällen kann/muss man, z.B. Callback-Funktionen/Methoden mit einer variablen Parameterliste aufrufen. Was aber, wenn wir ein Objekt durch eine Factory erstellen wollen und der Konstruktor Parameter erwartet? Die Funktionen call_user_func() oder call_user_func_array() fallen leider aus, da wir noch kein &#8230; <a href="https://blog.ebene7.com/2011/03/21/array-als-parameterliste-an-den-konstruktor-uebergeben/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>In verschiedenen Fällen kann/muss man, z.B. Callback-Funktionen/Methoden mit einer variablen Parameterliste aufrufen. Was aber, wenn wir ein Objekt durch eine Factory erstellen wollen und der Konstruktor Parameter erwartet?</p>
<p>Die Funktionen <a href="http://de2.php.net/manual/de/function.call-user-func.php" target="_blank">call_user_func()</a> oder <a href="http://de2.php.net/manual/de/function.call-user-func-array.php" target="_blank">call_user_func_array()</a> fallen leider aus, da wir noch kein Callback-Objekt haben. Es gibt aber einen sehr einfachen Weg mit Reflections.<span id="more-2797"></span></p>
<p>Zuerst ein paar einfache Klassen zum Testen. Von der Klasse Foo soll ein Objekt erstellt werden und diese erwartet je ein Objekt vom Typ Bar und Baz als Parameter im Konstruktor.</p>
<pre>&lt;?php
class Bar {}
class Baz {}

class Foo
{
  public function __construct(Bar $bar, Baz $baz)
  {
    echo '&lt;pre&gt;' . print_r(func_get_args(), true) . '&lt;/pre&gt;';
  }
}</pre>
<p>Nun wird das Objekt über eine sehr einfache Factory-Methode erzeugt und dabei wird die Parameterliste von create() an den Konstruktor weitergegeben.</p>
<pre>&lt;?php
class Factory
{
  public static function create($type)
  {
    $args = func_get_args();
    array_shift($args);
    $class = new ReflectionClass($type);
    return $class-&gt;newInstanceArgs($args);
  }
}

$foo = Factory::create('Foo', new Bar(), new Baz());</pre>
<p>So einfach ist es. Über die Reflection-Klasse wird ein neues Objekt erzeugt.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.ebene7.com/2011/03/21/array-als-parameterliste-an-den-konstruktor-uebergeben/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Enums in PHP (sicher ist sicher)</title>
		<link>https://blog.ebene7.com/2010/10/08/enums-in-php-sicher-ist-sicher/</link>
		<comments>https://blog.ebene7.com/2010/10/08/enums-in-php-sicher-ist-sicher/#comments</comments>
		<pubDate>Fri, 08 Oct 2010 05:00:52 +0000</pubDate>
		<dc:creator>Daniel</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Qualitätssicherung]]></category>
		<category><![CDATA[CustomLibrary]]></category>
		<category><![CDATA[Enum]]></category>
		<category><![CDATA[Magic Methods]]></category>
		<category><![CDATA[Reflection]]></category>

		<guid isPermaLink="false">http://blog.ebene7.com/?p=1586</guid>
		<description><![CDATA[Wer hin und wieder auch mal in anderen Sprachen wie z.B. Java programmiert, dem werden Enums sicher nicht fremd sein. Wäre es nicht schön, ähnliches auch unter PHP zur Verfügung zu haben, um nicht jeden Status als String oder Int &#8230; <a href="https://blog.ebene7.com/2010/10/08/enums-in-php-sicher-ist-sicher/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Wer hin und wieder auch mal in anderen Sprachen wie z.B. Java programmiert, dem werden Enums sicher nicht fremd sein. Wäre es nicht schön, ähnliches auch unter PHP zur Verfügung zu haben, um nicht jeden Status als String oder Int zu übergeben? Tja, das Leben ist aber kein Ponyhof und wenn PHP das nicht anbietet, dann schaffen wir uns eben eine eigene Lösung um besser und (typ-)sicherer zu programmieren.<span id="more-1586"></span></p>
<p>Wir kennen es sicher alle: Wir haben irgendein Objekt und wollen dessen Status oder einen bestimmten Wert setzen oder abfragen. Was passiert sicher in 95% aller Fälle? Es wird ein String- oder Integer-Wert gesetzt oder verglichen. Aus meiner Sicht nicht besonders elegant und erst recht nicht sicher, da der Wert unnötig oft überprüft werden muss.</p>
<p>Mit Enums haben wir den Vorteil, dass wir bestimmte Werte vorgeben können und auch nur diese zu Verfügung stehen. Da wir unter PHP nur &#8220;normale&#8221; Klassen verwenden können, haben wir dadurch immer automatisch einen bestimmten Typen.</p>
<p>Wir wollen nun den Status eines Benutzers in unserem System definieren und legen uns dazu folgende Klasse mit den erforderlichen Werten an.</p>
<pre>&lt;?php
class UserStatus extends Custom_Enum
{
  const ACTIVE    = 1;
  const NOTACTIVE = 2;
  const DELETED   = 3;
}</pre>
<p>Damit können wir unseren Benutzern einen eindeutigen Status setzen.</p>
<pre>&lt;?php
class User
{
  public function setStatus(UserStatus $status)
  {
    // ..
  }
}

$user = new User();

$status = new UserStatus();
$user-&gt;setStatus($status-&gt;active());</pre>
<p>Durch das Type-Hinting können wir schon bei der Parameterübergabe den richtigen Objekttypen sicherstellen und müssen darauf später nicht mehr extra prüfen.</p>
<p>Die Klasse Custom_Enum funktioniert mit Reflektion und etwas PHP-Magie und ist bislang erst nur ein grober Entwurf. Ein paar Überprüfungen und eine bessere Fehlerbehandlung wären dann noch notwendige Maßnahmen.</p>
<pre>&lt;?php
abstract class Custom_Enum
{
  private $_constants;  // array
  private $_index;  // string

  public function  __construct()
  {
    $rc = new ReflectionClass($this);
    $this-&gt;_constants = $rc-&gt;getConstants();
  }

  public function __call($method, $args)
  {
    if (preg_match('/^is([a-zA-Z]*?)$/', $method, $matches)) {
      return $this-&gt;is($matches[1]);
    }
    $this-&gt;set($method);
    return $this;
  }

  public function __toString()
  {
    return (string)$this-&gt;getValue();
  }

  public function set($index)
  {
    $this-&gt;_index = strtoupper($index);
    return $this;
  }

  public function is($index)
  {
    return strtoupper($index) == $this-&gt;_index;
  }

  public function getValue()
  {
    return $this-&gt;_constants[$this-&gt;_index];
  }
}</pre>
<p>Ich finde das schon wesentlich besser als lose String- oder Integerwerte auszutauschen, aber vielleicht geht es ja noch besser und sicherer? Eure Ideen, Kritik, Fragen oder was auch immer sind dann in den Kommentaren gut aufgehoben. Also keine Scheu und diskutiert mit! <img src='https://blog.ebene7.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>https://blog.ebene7.com/2010/10/08/enums-in-php-sicher-ist-sicher/feed/</wfw:commentRss>
		<slash:comments>20</slash:comments>
		</item>
		<item>
		<title>Einfache UnitTests für Magento</title>
		<link>https://blog.ebene7.com/2010/08/24/einfache-unittests-fur-magento/</link>
		<comments>https://blog.ebene7.com/2010/08/24/einfache-unittests-fur-magento/#comments</comments>
		<pubDate>Tue, 24 Aug 2010 05:00:39 +0000</pubDate>
		<dc:creator>Daniel</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Qualitätssicherung]]></category>
		<category><![CDATA[AutoLoader]]></category>
		<category><![CDATA[Factory]]></category>
		<category><![CDATA[Magento]]></category>
		<category><![CDATA[PHPUnit]]></category>
		<category><![CDATA[Reflection]]></category>
		<category><![CDATA[Shop]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[Zend_CodeGenerator]]></category>

		<guid isPermaLink="false">http://blog.ebene7.com/?p=1118</guid>
		<description><![CDATA[Seit nun knapp fünf Monaten stelle ich mich der Herausforderung saubere Softwareentwicklung und Magento in Einklang zu bringen. Ein kleiner Schritt in diese Richtung war der Einsatz von UnitTests, um mögliche Fehler während der Entwicklung schneller zu finden. Viele Fehler &#8230; <a href="https://blog.ebene7.com/2010/08/24/einfache-unittests-fur-magento/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Seit nun knapp fünf Monaten stelle ich mich der Herausforderung saubere Softwareentwicklung und Magento in Einklang zu bringen. Ein kleiner Schritt in diese Richtung war der Einsatz von UnitTests, um mögliche Fehler während der Entwicklung schneller zu finden.</p>
<p><span id="more-1118"></span>Viele Fehler treten bei Magento schon durch eine fehlerhafte Konfiguration auf, was zur Folge haben kann, dass wir keine Instanz unser lokalisierten Klasse bekommen, sondern ein standard Magento-Objekt oder einen Fehler, weil keine passende Klasse gefunden wird.</p>
<p>So habe ich also angefangen, die Existenz der zu prüfenden Klassen zu testen und ob die Mage-Factory das gewünschte Objekt zurück gibt. Bei einigen Klassen ist es zudem auch noch wichtig, dass sie von der richtigen Elternklasse abgeleitet sind.</p>
<p>Da sich diese Tests immer wiederholten, zeichnete sich schnell ein Muster ab. Ebenso erfolgt auch die Namesgebung der Magentoklassen nach einem gewissen Muster. Was wäre also naheliegender, als beides miteinander zu verbinden und so in Zukunft eine Menge unnützer Schreibarbeit zu sparen?</p>
<p>Als erstes habe ich dafür einen erweiteren TestCase vom PHPUnit-TestCase abgeleitet, der mir weitere vereinfachte Asserts zur Verfügung stellt und die Tests dadurch auch etwas lesbarer macht.</p>
<pre>$this-&gt;assertClassExists($classname);</pre>
<p>statt</p>
<pre>$this-&gt;assertTrue(class_exists($classname));</pre>
<p>Auf diesem Wege lassen sich dann auch z.B. Getter und Setter einfach auf das gewünschte Standardverhalten prüfen.</p>
<p>Im nächsten Schritt habe ich einen Entity-TestCase abgeleitet, der als Basis für alle weiteren TestCases für Models, Blocks und Helper dienen soll. Dieser ist in sich schon ein wenig intelligenter und erkennt anhand des Namens der Test-Klasse, welche Klasse getestet werden soll, welchen Typ diese hat und welche die Standard-Elternklasse ist.</p>
<p>Diese Informationen werden, nebenbei bemerkt, durch protected Methoden ermittelt und könnten zu jeder Zeit durch noch speziellere TestCases wieder überschrieben werden.</p>
<p>Mit den nun vorliegenden Informationen ist diese TestCase-Klasse mächtig genug, um alleine durch Vererbung die grundlegenden Tests durchzuführen.</p>
<p>Für die jeweiligen Typen, z.B. Model habe ich dann noch weitere TestCases abgeleitet, um dort noch spezielle Methoden implementieren zu können.</p>
<p>Ein sehr einfacher Test für die Model-Klasse &#8220;Mein_Customer_Model_Customer&#8221; sähe dann beispielsweise so aus:</p>
<pre>class Mein_Customer_Model_CustomerTest extends Mein_Model_TestCase
{
}</pre>
<p>Dadurch würde nun automatisch getestet werden, ob der Autoloader die Klasse &#8220;Mein_Customer_Model_Customer&#8221; finden kann, diese von der Magento-Model-Klasse erbt und ob die Factory aufgrund der Konfiguration auch das richtige Objekt liefert.</p>
<p>Ich denke, dass es viel einfacher nicht mehr geht und das Argument gegen Tests damit dann auch aus der Welt sein sollte.</p>
<p>Kleiner Tipp: Wenn man es nun noch etwas konfortabler mag, dann kann man sich seine Models auch mit dem Zend_CodeGenerator bauen lassen und die Tests gleich mit.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.ebene7.com/2010/08/24/einfache-unittests-fur-magento/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Was tun gegen magische Zugriffsmethoden?</title>
		<link>https://blog.ebene7.com/2010/04/09/was-tun-gegen-magische-zugriffsmethode/</link>
		<comments>https://blog.ebene7.com/2010/04/09/was-tun-gegen-magische-zugriffsmethode/#comments</comments>
		<pubDate>Fri, 09 Apr 2010 20:30:22 +0000</pubDate>
		<dc:creator>Daniel</dc:creator>
				<category><![CDATA[Grundlagen]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Snippets]]></category>
		<category><![CDATA[Tipps und Tricks]]></category>
		<category><![CDATA[Eclipse]]></category>
		<category><![CDATA[IDE]]></category>
		<category><![CDATA[Magento]]></category>
		<category><![CDATA[Magic Methods]]></category>
		<category><![CDATA[NetBeans]]></category>
		<category><![CDATA[Reflection]]></category>
		<category><![CDATA[Shop]]></category>
		<category><![CDATA[Zugriffsmethode]]></category>

		<guid isPermaLink="false">http://blog.ebene7.com/?p=561</guid>
		<description><![CDATA[Im Moment bin ich mit voller Begeisterung dabei, mich in die Magento Shopsoftware einzuarbeiten. Dass die Entwickler jedoch nicht nur die Software, sondern auch den Programmcode sehr flexibel gestaltet haben, macht die Sache leider nicht einfacher. Viele Methoden sind mit &#8230; <a href="https://blog.ebene7.com/2010/04/09/was-tun-gegen-magische-zugriffsmethode/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Im Moment bin ich mit voller Begeisterung dabei, mich in die Magento Shopsoftware einzuarbeiten. Dass die Entwickler jedoch nicht nur die Software, sondern auch den Programmcode sehr flexibel gestaltet haben, macht die Sache leider nicht einfacher.</p>
<p>Viele Methoden sind mit <a href="http://de2.php.net/manual/de/language.oop5.overloading.php#language.oop5.overloading.methods" target="_blank"><code>__call()</code></a> implementiert und das hat die Nachteile, dass z.B. die <a href="http://de.wikipedia.org/wiki/Autovervollst%C3%A4ndigen" target="_blank">Codecompletion</a>, <a href="http://de.wikipedia.org/wiki/Reflection_%28Programmierung%29" target="_blank">Reflection</a>, automatische Dokumentation oder die Navigation in der IDE ausgehebelt werden.</p>
<p>Zum Glück können wir das ja zum Teil in vererbten Klassen wieder abfangen, ohne die ursprünglichen Klassen umschreiben zu müssen, z.B. weil wir die fremden Libraries zwecks späterer Updates nicht verändern wollen.</p>
<pre>&lt;?php

class A
{
  public function __call($method, $args)
  {
    echo __METHOD__ . ' ' . $method;
  }
}

class B extends A
{
  public function machWas()
  {
    parent::machWas();
  }
}

$b = new B();
$b-&gt;machWas();</pre>
<p>Die Methode <code>B::machWas()</code> delegiert den Aufruf an die magische Methode, wobei sich das Verhalten nach außen nicht verändert, jedoch alles andere wieder funktionieren wird.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.ebene7.com/2010/04/09/was-tun-gegen-magische-zugriffsmethode/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
