Traumtyp Phing
Kommentare

Eine erste Buildfile
In dem Beispiel aus Listing 1 geben wir als Erstes das project-Tag an, das neben einem eindeutigen Namen auch ein default-Target enthalten kann, das ausgeführt wird, wenn kein anderes

Eine erste Buildfile

In dem Beispiel aus Listing 1 geben wir als Erstes das project-Tag an, das neben einem eindeutigen Namen auch ein default-Target enthalten kann, das ausgeführt wird, wenn kein anderes Target zur Ausführung angegeben wurde. Innerhalb des Projekts können nun beliebig viele Targets definiert werden. In Listing 1 ist nur ein Target definiert, das aus einer Abfrage des Zielorts besteht, wofür der propertyprompt-Task genutzt wird. Dieses benötigt neben einem Namen für die Variable, in der die Benutzereingabe gespeichert wird, einen Ausgabetext und zusätzlich eine Vorbelegung, die genutzt wird, wenn keine reale Eingabe getätigt wird. Sobald der Speicherort erfragt ist, kann mit dem available-Task dessen Existenz geprüft und das Ergebnis wiederum in einer Variable abgelegt werden. Sie wird im folgenden if-Task ausgewertet, und nur bei Existenz des Zielverzeichnisses wird ein entsprechendes Archiv unter Zuhilfenahme des tar-Tasks erstellt. Sollte das Verzeichnis nicht existieren, so wird mithilfe des fail-Task eine entsprechende Fehlerausgabe erzeugt.

Bei näherer Betrachtung des tar-Tasks fällt auf, dass er ein so genanntes FileSet enthält. Es repräsentiert dabei Ordnerstrukturen im Dateisystem und ermöglicht White- und Black-Listing. So steht das *-Symbol bei einmaligem Vorkommen für eine beliebige Zeichenfolge. Bei doppeltem Vorkommen ändert sich die Bedeutung zu einem rekursiven Suchmuster, es werden also alle Unterordner durchsucht. Ebenso wie per include festgelegt wird, welche Dateien mit aufgenommen werden können, ist es möglich, per exclude entsprechende Ausschlusskriterien festzulegen. FileSets können zudem mit einer id gekennzeichnet werden und dann innerhalb jedes Tasks, der FileSets unterstützt, mit refid wiederverwendet werden.

Was Ant fehlt

Dem mit Ant vertrauten Leser dürfte das Beispiel bekannt vorkommen, mit einer einzigen Ausnahme: Der if-Task existiert in dieser Weise für Ant nicht. Doch gerade er ermöglicht semiintelligente Skripte, die aufgrund von Rückgabewerten entsprechende Aktionen durchführen. So wäre es beispielsweise denkbar, statt einer Fehlerausgabe hinsichtlich der Nichtexistenz des Speicherorts, eben diesen anzulegen und fortzufahren.

Neben den Möglichkeiten, die sich durch die Conditions ergeben, bietet Phing einen weiteren großen Vorteil gegenüber Ant: Zwar bringen sowohl Phing als auch Ant viele Tasks schon von Haus aus mit, jedoch gibt es Projekte, in denen es nicht ausreicht, einen der vorhandenen Tasks zu nutzen. Das ist zum Beispiel der Fall, wenn ein Shopsystem verschiedene Layouts einsetzt, die jeweils unterschiedliche Versionen einer JavaScript-Bibliothek nutzen. Eine Möglichkeit bestünde nun darin, die Information, welches Layout welche Bibliotheksversion voraussetzt, innerhalb einer Konfigurationsdatei zu speichern. Da jedoch diese Information zumeist bereits im PHP-Code vorhanden ist, bietet es sich hier an, sie nicht zu duplizieren und so den Wartungsaufwand gering zu halten. Genau in dieser Situation kann Phing punkten. Denn da eigene Tasks in PHP implementiert werden können, kann hier auch direkt auf den eigenen PHP-Code zugegriffen werden. Wie genau, soll dieser Artikel erklären.

Was Phing (noch) nicht hat

Einer der Tasks, die mir in Phing wirklich fehlen, ist der Apply-Task [7], den Ant bietet. Er ermöglicht es, einen Befehl auf mehrere Dateien anzuwenden, beispielsweise um alle JavaScript-Dateien zu minimieren. Daher soll das hier als Beispiel dienen, um zu zeigen, wie man eigene Phing-Tasks realisieren kann. Da ein kompletter Nachbau des Apply-Tasks aus Ant diesen Artikel sprengen würde, konzentrieren wir uns auf den wesentlichen Teil: Unser Apply-Task soll einen Befehl entgegennehmen, optional Argumente, die diesem Befehl übergeben werden, und ein FileSet beinhalten. Zusätzlich soll noch eine Rückgabe gespeichert werden: ob der Task korrekt ausgeführt wurde oder nicht. Den Variablennamen, in dem die Rückgabe abgelegt wird, soll der Nutzer selbst bestimmen können. Da die meisten Befehle mehr als eine Option übergeben bekommen können, müssen wir das ebenfalls beachten. Außerdem wäre es noch hilfreich, ein Flag setzen zu können, das ausführlichere Ausgaben auf der Konsole ermöglicht. Sämtliche Dateien, die in diesem Artikel genutzt werden, finden Sie auf der Heft-CD. Dort, innerhalb des Ordners PHPMagazin, gibt es die Unterordner Tasks und Types. In Tasks wird unser Task abgelegt und in Types demzufolge unser Type. Was unter einem Type zu verstehen ist, soll an anderer Stelle folgen.

Phing bietet leider kein Autoloading, daher muss die Datei, die die Task-Klasse enthält, zuerst per require_once geladen werden (Listing 2). Ebenfalls wollen wir die Klasse BuildException laden, die zum Einsatz kommt, wenn etwas schief gegangen ist. Danach kann unsere Klasse von der Task-Klasse des Phing-Frameworks erben. Das kann direkt geschehen, indem Task extended wird oder indirekt indem von einem bereits bestehenden Task geerbt wird. Die Benennung der eigenen Tasks geschieht in CamelCase-Schreibweise mit einem angehängten Task.

Listing 2
_executeable = $_exec;
    }
    
    public function setReturnProperty($_property)
    {
        $this->_returnProperty = $_property;
    }
    
    public function setVerbose($_verbose)
    {
        $this->_verbose = StringHelper::booleanValue($_verbose);
    }
    
    public function createFileSet()
    {
        
        $num = array_push($this->_files, new FileSet());

        return $this->_files[$num-1];
    }
    
    public function createArg()
    {
        $num = array_push($this->_args, new Arg());
        
        return $this->_args[$num-1];
    }
    
    public function main()
    {
        if (!$this->_executeable) {
            throw new BuildException(
                'No executeable was set for apply command'
            );
        }
        
        if (0 == count($this->_files)) {
            throw new BuildException(
                'No files where given to apply command on'
            );
        }
    
        $command = $this->_executeable . ' ';
        
        foreach ($this->_args as $arg) {
            $command .= $arg->getValue();
        }
        
        foreach ($this->_files as $fileset) {
            $ds = $fileset->getDirectoryScanner($this->getProject());
            $files = $ds->getIncludedFiles();
            $dir = $fileset->getDir($this->getProject())->getAbsolutePath();
            
            foreach ($files as $file) {
                $path = realpath($dir . DIRECTORY_SEPARATOR . $file);
                if ($this->_verbose) {
                    $message = sprintf(
                        'apply %s to %s',
                        str_replace(array('\', 'n', 'r', PHP_EOL), '', $command),
                        $path
                    );
                    $this->log($message , Project::MSG_VERBOSE);
                }

                exec($command . ' ' . $path, $output);
                $output = implode(PHP_EOL, $output);
                array_push($this->_output, $output);
                $this->log($output, Project::MSG_VERBOSE);
            }
        }

        if ($this->_returnProperty) {
            $this->project->setProperty(
                $this->_returnProperty,
                implode(';', $this->_output)
            );
        } else {
            $this->log(implode(PHP_EOL . PHP_EOL, $this->_output), Project::MSG_INFO);
        }
    }
}

Auf den folgenden Seiten:

  • Erste Schritte
  • Dateien und Ordner
  • Ein Traumtyp
  • Den Task das Laufen lehren
  • Unser Task lernt sprechen
  • Fazit
  • Info zum Autoren
Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -