Rewrite & Extending Concepts

Warum Ersetzen und Erweitern

Der heutige Standard in der Entwicklung von PHP Anwendungen mit einer Anforderung wie Bricks macht es erforderlich, dass die Anwendung flexibel erweitert werden kann und einen modulare Aufbau nachweist.

Hierdurch können Third-Party-Entwickler und der mit der Pflege beauftragte eingriffe in das System vornehmen ohne dabei das originäre Programm abändern zu müssen. Das sorgt für Code Qualität und macht die Anwendung wartbar.

Bricks bietet vier Konzepte zur Lösung dieser Anforderung:

  • Eventbasierte Erweiterung für spezifizierte Teile der Anwendung:
    Events in Klassen bieten einen definierten Einsprungpunkt in den Programmfluss. Je nach Intention des Events können so verschiedene Operationen ausgeführt werden.
    Der Vorteil bei eventbasierter Erweiterung ist, dass hier keine Klassen ersetzt werden müssen um Funktionalität hinzuzufügen.
  • Klassen via Eventizr für alle gleichermaßen zugänglich machen:
    Der Eventizr von Bricks verwendet kompilierte Klassen um Einsprungpunkte in die Anwendung zu ermöglichen die für jeden auf der selben Klasse basieren und somit ebenfalls ohne die Klasse zu ersetzen Funktionalität hinzufügen lässt. Das Konzept ist ähnlich dem AOP-Ansatz aus Java. Jede Methode einer Klasse erhält hierbei pre und post Einstiegspunkte und ermöglichen es sogar, den gesamten Funktionskörper zu ersetzen.
  • Ersetzen durch überschreiben:
    Die Klasse wird hierbei vollständig ersetzt. Ein Beispiel wäre hier eine abstrakte Klasse die von anderen Klassen vererbt wird. Diese Parent-Klassen lassen sich nicht erweitern, da diese als Abhängigkeit anderer Klassen definiert sind.
  • Klassen erweitern:
    Liegen Klassen ohne Vererbungshierarchie zur Verfügung, kann man diese mit eigenen Klassen extenden. bricks-cmf/di-service bekommt diese Änderung mitgeteilt und weiß welche Klasse zu verwenden ist.

Vor und Nachteile

Bei der eventbasierten Erweiterung hat man die wohl größten Vorteile darin, dass die Anpassungen überall verfügbar sind und keine Vererbungshierarchien beachtet werden müssen.

Der Vorteil des Überschreibens liegt darin, dass man ohne Kompromisse Zugriff auf den Code hat. Bringt aber auch den Nachteil mit, dass diese Anpassungen bekannt sein müssen wenn Konflikte auftreten und das der Code bei Änderungen gepflegt werden muss.

Welche Möglichkeiten bietet mir Bricks

Bricks und das Bricks CMS, genauer bricks-cms/skeleton bieten vordefinierte Möglichkeiten um das System zu erweitern.
Zum einen gibt es eine spezielle Verzeichnisstruktur die es ermöglicht Klassen einfach zu ersetzen und zum anderen bietet es die Möglichkeit jede Klasse mit Events auszustatten.

  • Das custom-Verzeichnis:
    Unter Beachtung des Dateipfads kann jede beliebige Datei in das Verzeichnis abgelegt werden um so die betreffende Klasse zu überschreiben.
  • Das compile-Verzeichnis:
    Im compile-Verzeichnis werden in ähnlicher Weise Dateien abgelegt. Der Eventizr und der LazyLoader kompilieren Ihre Klassen in dieses Verzeichnis und verwenden einen eigenen Namespace oder eine kopie der Klasse.
    Die überprüfung der MD5 Dateisummen hält das Verzeichnis automatisch auf dem aktuellen Stand.
  • Das packages-Verzeichnis:
    Das packages-Verzeichnis dient dem ersetzen von Composer Paketen.
  • Klassen ersetzen zur Laufzeit via Aliases:
    Der Bricks-Di-Service unterstütz das ersetzen von Klassen zur Laufzeit. Immer wenn die Di zur Erstellung von Objekten verwendet wird, weiß diese welche Klassen zu verwenden sind.
  • Klassen ersetzen über den Autloader
    Über den Composer Autoloader können Klassenpfade neu definiert werden um eine Klasse so Systemweit zu überschreiben.

Eine Klasse via Di überschreiben

<?php
namespace MyNamespace;

use 
BricksCmf\ConfigService\ConfigServiceInterface;
use 
BricksCmf\DiService\DiServiceInterface;

class 
MyClass
{
    
/** @var BricksCmf\DiService\DiServiceInterface $di **/
    
protected $di;

    public function 
__construct(ConfigServiceInterface $configDiServiceInterface $di)
    {
        
$this->di $di;
        
$config->set('default.bricks/di.aliases.Class\\To\\Rewrite''MyNamespace\\MyRewriteClass');
    }

    public function 
getTheClassInstance()
    {
        return 
$this->di->get('Class\\To\\Rewrite');
    }
}

$myObject = new MyClass;
echo 
get_class($myObject->getTheClassInstance()); // returns MyNamespace\MyRewriteClass

Eine Klasse für Events erweitern

<?php
namespace MyNamespace;

use 
BricksCmf\EventizrService\EventizrServiceInterface;
use 
BricksCmf\DiService\DiServiceInterface;

class 
MyClass
{
    
/** @var BricksCmf\DiService\DiServiceInterface $di **/
    
protected $di;

    public function 
__construct(EventizrServiceInterface $eventizrDiServiceInterface $di)
    {
        
$this->di $di;
        
$eventizr->eventize('Class\\To\\Rewrite');
    }

    public function 
getTheClassInstance()
    {
        return 
$this->di->get('Class\\To\\Rewrite');
    }
}

$myObject = new MyClass;
echo 
get_class($myObject->getTheClassInstance()); // returns BricksCompile\Eventizr\Class\To\Rewrite

Jetzt ist es möglich sich via Event einzuhängen.

<?php
    
namespace MyNamespace;

    use 
BricksCmf\EventManager\EventManagerInterface;
    use 
BricksCmf\DiService\DiServiceInterface;

    class 
MyClass
    
{

        
/** @var BricksCmf\DiService\DiServiceInterface $di **/
        
protected $di;

        public function 
__construct(EventManagerInterface $eventManagerDiServiceInterface $di)
        {
            
$this->di $di;
            
$eventManager->addListener('Class\\To\\Rewrite::yourMethod.pre', function($event) {
                echo 
'got event ' $event->getEventName();
            });
        }

        public function 
createAndCallClass() : void
        
{
            
$classToRewriteObject $this->di->get('Class\\To\\Rewrite');
            
$classToRewriteObject->yourMethod();
        }

    }

    
$myObject = new MyClass;
    
$myObject->createAndCallClass(); // will echo "got event Class\To\Rewrite::yourMethod.pre"

Ziemlich einfach, oder?