XML-Parser-Beispiel

Mit Hilfe des XML-Parser lässt sich eine Workflow-Struktur als XML-Datei beschreiben.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Definition des Workflows -->
<workflow>

    <!-- Mit diesem Tag kann der Eintritszustand in den Workflow festgelegt werden. 
       Besitzt ein Objekt kein Status, so wird dieser Status angenommen.
       class, state-class und step-class sind optional.
    -->
    <admission state="created" 
               step="create" 
               class="\Alvine\Application\Workflow\Admission"
               state-class="\Alvine\Application\Workflow\State" 
               step-class="\Alvine\Application\Workflow\Step" 
               container-class="\MyNamespace\Item" />

    <!-- Definition aller vorhandenen Status die ein Objekt annehmen kann -->
    <states> 
        <!-- Ein Status muss einen Namen besitzen, es kann eine von \Alvine\Application\Workflow\State
        abgeleitete Klasse angegeben werden. -->
        <state name="created" class="\Alvine\Application\Workflow\State" />
        <state name="start" />
        <state name="end" />
    </states> 

    <!-- Steps definieren die Aktionen, die beim Übergang von einem Status,
    zu einem anderem Status gegangen werden. -->
    <steps> 
        <!-- Jeder Step muss einen eindeutigen Namen enthalten, zusätzlich
        kann noch eine von \Alvine\Application\Workflow\Step abgeleitetet Klasse 
        definiert werden. -->

        <!-- Dieser Step wird in <admission> referenziert und beim Erstellen eines
        Containers getriggert. -->
        <step name="create"> 
            <actions>
                <!-- Es können Aktionen, die beim Übergang ausgeführt werden, definiert werden. -->
                <action class="\MyNamespace\MyCreateAction" />
            </actions>
        </step>

        <step name="step1" class="\Alvine\Application\Workflow\Step"> 
            <actions>
                <!-- Es können Aktionen, die beim Übergang ausgeführt werden, definiert werden. -->
                <action class="\MyNamespace\MyAction" />
            </actions>
            <validations> 
                <!-- Mittels Validatoren kann geprüft werden, ob ein Statusübergang möglich ist. -->
                <validation class="\MyNamespace\MyValidation" />
            </validations> 
        </step>

        <step name="step2"> 
            <actions>
                <action class="\MyNamespace\MyAction" />
            </actions>
        </step>
    </steps>

    <!-- Automation des Workflows;
         Mit diesen Regeln lassen sich unabhänging von einem
         Übergang Änderungen an einem Container vornehmen -->
    <automation>
        <rules>
            <!-- Jede Regel verfügt über Bedingungen (die alles zutreffen müssen)
                 und Aktionen, die ausgeführt werden. Diese Regel wird
                 automatisch nach jeder Transition (\Alvine\Application\Workflow\Event\EndTransition)
                 ausgeführt -->
            <rule class="\Alvine\Application\Workflow\Automation\Rule" 
                  name="my-rule" 
                  on="\Alvine\Application\Workflow\Event\EndTransition">
                <conditions>
                    <!-- Beide Bedingungen müssen erfüllt sein; die Methode müssen
                         true zurück geben -->           
                    <condition class="\MyNamespace\AutomationCondition1" />
                    <condition class="\MyNamespace\AutomationCondition2" />
                </conditions>
                <actions>
                    <!-- Diese Aktionen werden ausgeführt -->
                    <action class="\MyNamespace\AutomationMyAction1" />
                    <action class="\MyNamespace\AutomationMyAction2" />
                </actions>
            </rule>
        </rules>
    </automation>     

    <!-- transitions definieren eine Zustandsänderung eines Objektes. -->
    <transitions>
        <!-- Ein Übergang hat einen eindeutigen Namen und einen Zielstatus.
        Wird kein Ausgagsstatus (from) definiert, so ist dieser Übergang 
        von allen Status aus möglich. -->
        <transition name="transfer1" to="start" />
        <!-- Zusätzlich kann noch ein Step und ein Ausgangsstatus definiert werden.
        Es kann eine von \Alvine\Application\Workflow\Transition abgeleitete Klasse verwendet werden. -->
        <transition name="transfer2" class="\Alvine\Application\Workflow\Transition" from="start" to="end" with="step2">
            <!-- Überprüfung vor dem Übergang -->
            <validations> 
                <!-- Mittels Validatoren kann geprüft werden, ob ein Statusübergang möglich ist. -->
                <validation class="\MyNamespace\MyTransitionValidation" />         
            </validations> 

            <!-- Fehler / Exceptions abfangen -->            
            <exceptions>
                <exception match="\Alvine\Core\FrameworkException" >
                    <!-- Exception ignorieren -->
                    <handler class="\Alvine\Application\Workflow\Transition\Exception\Handler\ThrowAway" />
                </exception>
            </exceptions>


        </transition>
    </transitions>

</workflow>

Das folgende Programm verwendet die XML und führt die definierten Übergänge aus. Zuerst wird eine Klasse die bearbeitet werden soll definiert.

class Item implements \Alvine\Application\Workflow\Container {

    use \Alvine\Application\Workflow\Container\Implementation;
}

Nun brauchen wir noch eine Aktion für den Übergang

class MyAction extends \Alvine\Core\Alvine implements \Alvine\Application\Workflow\Action {

    public function run(\Alvine\Application\Workflow\Container $container): \Alvine\Application\Workflow\Container {
        echo "tue etwas ...\n";

        return $container;
    }

    public static function getInstanceFromParameterMap(\Alvine\Types\Map\ParameterMap $data): \Alvine\Application\Workflow\Action {
        return new static();
    }

}

class MyCreateAction extends \Alvine\Core\Alvine implements \Alvine\Application\Workflow\Action {

    public function run(\Alvine\Application\Workflow\Container $container): \Alvine\Application\Workflow\Container {
        echo "erstelle einen Container ...\n";

        return $container;
    }

    public static function getInstanceFromParameterMap(\Alvine\Types\Map\ParameterMap $data): \Alvine\Application\Workflow\Action {
        return new static();
    }

}

und eine Überprüfung.

class MyValidation extends \Alvine\Core\Alvine implements \Alvine\Application\Workflow\Step\Validation {

    public function isValid(\Alvine\Application\Workflow\Container $container): bool {
        return true;
    }

}

class MyTransitionValidation extends \Alvine\Core\Alvine implements \Alvine\Application\Workflow\Transition\Validation {

    public function isValid(\Alvine\Application\Workflow\Transition $transition, \Alvine\Application\Workflow\Container $container): bool {
        return true;
    }

}

Jetzt wird die oben definierte XML-Datei eingelesen und der Workflow initialisiert.

$processor=(new \Alvine\Application\Workflow\Parser\XMLParser)->parse($xml);

Jetzt führen wir einige Übergänge aus.

$container=$processor->create(1224);

echo "Aktueller Status des Containers: ".$container->getState()."\n";
echo "------------------------------------------\n\n";
echo "Aktueller Status des Containers: ".$container->getState()."\n";

/** Ersten Übergang ausführen */
echo "Übergang transfer1\n";
$processor->run($container, 'transfer1');

echo "Neuer Status des Containers: ".$container->getState()."\n";
echo "------------------------------------------\n\n";
echo "Aktueller Status des Containers: ".$container->getState()."\n";

/** Zweiten Übergang ausführen */
echo "Übergang\n";
$processor->run($container, 'transfer2');

echo "Neuer Status des Containers: ".$container->getState()."\n";
echo "------------------------------------------\n\n";
echo "Aktueller Status des Containers: ".$container->getState()."\n";

/** Übergang ausführen */
echo "Übergang transfer1\n";
$processor->run($container, 'transfer1');

echo "Neuer Status des Containers: ".$container->getState()."\n";
echo "------------------------------------------\n\n";
echo "Aktueller Status des Containers: ".$container->getState()."\n";

/** Übergang ausführen */
echo "Übergang transfer2\n";
$processor->run($container, 'transfer2');

echo "Neuer Status des Containers: ".$container->getState()."\n";
echo "------------------------------------------\n\n";
echo "Aktueller Status des Containers: ".$container->getState()."\n";

/** Übergang von end -> end ist nicht erlaubt */
try {
    echo "Übergang von end zu end (nicht definiert)\n";
    $processor->run($container, 'transfer2');
} catch(\Alvine\Application\Workflow\Transition\NotPermittedException $x) {
    echo "Nicht erlaubt!";
}

echo "Neuer Status des Containers: ".$container->getState()."\n";
echo "------------------------------------------\n\n";
echo "Aktueller Status des Containers: ".$container->getState()."\n";

/**
 * Ausgabe: 
 * start
 * tue etwas ...
 * end
 * start
 * tue etwas ...
 * end
 * Nicht erlaubt!
 */