>> Inhaltsverzeichnis >> Anleitung für Entwickler

Kapitel 5: Editieren des PHP-Quellcodes

Der folgende Abschnitt soll Ihnen zeigen, wie Sie den PHP-Quellcode eines Plugins editieren können, um neue Funktionen hinzuzufügen oder existierende Funktionen Ihren Bedürfnissen anzupassen.

In diesem Abschnitt des Tutorials soll ein automatisch generierter RSS-Feed für die 10 neuesten Einträge des Blog implementiert werden. Das Tutorial führt Sie durch die verschiedenen Schritte der Erstellung.

Definieren einer neuen Aktion im Interface-Abschnitt der Konfigurationsdatei

Bevor Sie den PHP-Quellcode editieren, sollten Sie die neue Aktion in der Schnittstelle des Plugins eintragen. Nur dann kann diese auch über den Webbrowser aufgerufen werden. Dies ist dient einerseits der Performance und ist andererseits eine zusätzliche Sicherheitsmaßnahme, um das absichtliche Einschleusen fremden PHP-Codes in die Anwendung zu erschweren, das versehentliche Ausführen von Code zu verhindern und insgesamt Plugins damit sicherer zu machen.

Um diesen Schritt durchzuführen, öffnen Sie die Datei "plugins/blog.config" in PSPad (beachten Sie, dass sie den Namen "blog" durch den Namen Ihres Plugins ersetzen müssen). Wählen Sie den Highlighter "Yana Framework" aus.

Fügen Sie im Container "INTERFACE" eine neue Aktion ein (Shortcut "act"). Nennen Sie diese Aktion "blog_rss".

Ihr Quellcode sollte nun die folgende neue Aktion enthalten:

<INTERFACE>
[...]
	<BLOG_RSS>
		<TYPE>default</TYPE>
		<MODE>0</MODE>
		<PERMISSION>0</PERMISSION>
		<TEMPLATE>INDEX</TEMPLATE>
		<INSERT></INSERT>
		<ONSUCCESS>
			<TEXT>200</TEXT>
			<GOTO></GOTO>
		</ONSUCCESS>
		<ONERROR>
			<TEMPLATE>ALERT</TEMPLATE>
			<TEXT>500</TEXT>
			<GOTO></GOTO>
		</ONERROR>
	</BLOG_RSS>
[...]
</INTERFACE>

Nicht alle der automatisch eingefügten Felder werden benötigt. Passen Sie die Definition der Aktion nun wie folgt an:

<INTERFACE>
[...]
	<BLOG_RSS>
		<TYPE>read</TYPE>
		<MODE>0</MODE>
		<TEMPLATE>NULL</TEMPLATE>
		<PERMISSION>0</PERMISSION>
	</BLOG_RSS>
[...]
</INTERFACE>

Das Feld "TYPE" mit dem Wert "read" hat etwas mit dem Verhalten dieser Aktion zu tun. Dieser Typ drückt aus, dass diese Aktion lediglich lesend auf die Datenbank zugreifen muss. Diese Information kann für eine bessere Performance ausgenutzt werden.

Das Feld "MODE" mit dem Wert "0" sagt aus, dass der normale Betriebsmodus verwendet wird. (Mit dem Wert "1" startet das Programm für diese Aktion im "abgesicherten Modus").

Das Feld "PERMISSION" mit dem Wert "0" sagt aus, dass jeder Besucher auf diese Aktion zugreifen darf, ohne besondere Anforderungen an seinen Sicherheitslevel.

Das Feld "TEMPLATE" gibt an, welches Template für die Aktion ausgewählt werden soll. Zwar ist es möglich das Template auch aus dem PHP-Code heraus auszuwählen, allerdings ist die Verwendung dieses Feldes in der Konfigurationsdatei übersichtlicher und einfacher.
Das hier ausgewählte Template "NULL" ist ein vordefinierter Spezialwert. Er drückt aus, dass "kein Template" ausgewählt werden soll. Dies ist in diesem Fall auch nicht erforderlich, weil die Ausgabe durch einen automatischen RSS-Generator, ohne Template, erzeugt werden wird.

Damit sind die Vorbereitungen abgeschlossen der folgende Abschnitt wird sich mit der Implementierung im PHP-Quellcode beschäftigen.

Schreiben des PHP-Quellcodes

Öffnen Sie die Datei "plugins/blog/plugin.php" in PSPad (beachten Sie, dass sie den Namen "blog" durch den Namen Ihres Plugins ersetzen müssen). Wählen Sie den Highlighter "PHP" aus.

Fügen Sie in die Klasse "plugin_blog" eine neue Funktion mit dem Namen "blog_rss" ein. Um sich Schreibarbeit zu ersparen, können Sie folgende Vorlage verwenden.

<?php 
class plugin_blog extends plugin
{
// [...]
    /**
     * blog_rss
     *
     * returns bool(true) on success and bool(false) on error
     *
     * Type:        read
     * Permission:  0
     * Templates:   NULL
     *
     * @access  public
     * @return  bool
     * @name    plugin_blog::blog_rss()
     * @param   array  $ARGS  array of params passed to the function
     */
    function blog_rss ($ARGS)
    {

    }
// [...]
}
?>

Meine persönliche Empfehlung ist, Quellcode, denn Sie geschrieben haben, stets sofort zu dokumentieren, solange Ihnen der Aufbau und die Funktionsweise noch frisch im Gedächtnis sind. Dies hat zudem den unschätzbaren Vorteil, dass Sie sich dadurch zwangsweise noch einmal mit dem gerade beschriebenen Code beschäftigen müssen.

Abfrage der Datenbank

Um einen RSS-Feed der 10 aktuellsten Beiträge zu erstellen, müssen Sie zunächst erst einmal die 10 aktuellsten Beiträge aus der Datenbank laden. Dazu erstellen Sie ein Datenbankabfrage der Tabelle "blog". Durch Setzen der Limit-Klausel auf "10" limitieren Sie die Anzahl der Einträge des Resultsets auf maximal 10 Stück. Damit Sie tatsächlich die 10 neuesten Einträge erhalten, sortieren Sie die Einträge absteigend nach dem Datum Ihrer Erstellung. Das heißt nach der Spalte "blog_created". Verwenden Sie dazu "order_by" und "desc".

Der folgende Quellcode demonstriert diese Abfrage.

<?php 
/* get entries from database */
$query = array(
    'key' => 'blog.*',
    'order_by' => 'blog_created',
    'desc' => true,
    'limit' => 10
);
$rows $this->database->get($query);
?>

Wie Sie in diesem Beispiel sehen, verfügt das Yana Framework über einen Query-Generator zum Erzeugen einfacher SQL-Statements. Sie benötigen also keinerlei SQL-Kenntnisse, um mit dem Yana Framework Abfragen auf einer Datenbank durchführen zu können. Das Mapping der tatsächlichen SQL-Statements auf die passende Syntax des eigentlichen DBMS erledigt das Framework ebenfalls automatisch.

Der Query-Generator ist außerdem in der Lage, automatisiert einfache Sicherheitsprüfungen durchzuführen, automatisches Quoting für Eingabewerte zu realisieren und SQL-Abfragen auf Plausibilität zu prüfen, ohne diese an die Datenbank zu senden. Dies reduziert zusätzlich das Risiko von SQL-Injections und erhöht dadurch die Sicherheit Ihrer Anwendung.

Das Resultset, hier $rows, besteht aus einem mehrdimensionalen, assoziativen Array. Jeder Eintrag entspricht einer Zeile. Jede Zeile wiederum hat jeweils einen Eintrag pro Spalte.

Ein Aufruf von var_export($rows) demonstriert dies. Die Ausgabe lautet wie folgt:

<?php 
array (
  =>
  array (
    'BLOG_TITLE' => 'Testeintrag',
    'BLOG_TEXT' => 'Das ist ein Test[br]Das ist ein Test[br]Das ist ein Test[br]Das ist ein Test',
    'BLOG_AUTHOR' => 'Thomas Meyer',
    'BLOG_CREATED' => '1173276511',
    'PROFILE_ID' => 'default',
    'BLOG_ID' => 2,
  ),
  =>
  array (
    'BLOG_TITLE' => 'Testeintrag',
    'BLOG_TEXT' => 'Das ist ein Test.[br]Nur ein Test.[br][br]Neue Zeile.',
    'BLOG_AUTHOR' => 'Thomas Meyer',
    'BLOG_CREATED' => '1173276442',
    'PROFILE_ID' => 'default',
    'BLOG_ID' => 1,
  ),
)
?>

Wie Sie am Index des Arrays sehen, sind die Einträge in umgekehrter Reihenfolge, also absteigend, sortiert. Die Spaltennamen werden in Großbuchstaben ausgegeben.

Weitere Details und Codebeispiele finden Sie im Kochbuch für Entwickler, Kapitel Datenbanken und in der API-Dokumentation der Klasse "DbStream".

Zur Repräsentation von RSS-Feeds dient die Klasse "RSS". Um einen neuen Feed zu erzeugen, sollte als nächster Schritt eine Instanz dieser Klasse erzeugt werden.

<?php 
$rss = new RSS();
$rss->title 'Blog';
$rss->description 'the 10 most recent blog entries';
?>

Den Titel und die Beschreibung des RSS-Feed können Sie frei wählen. Verwenden Sie dazu die Eigenschaften "title" und "description", wie im Beispiel gezeigt.

Anschließend ist für jeden Eintrag der Datenbank ein Eintrag im RSS-Feed zu erstellen. Ein Eintrag wird repräsentiert durch die Klasse "RSSitem". Um einen Eintrag zu erzeugen, wird eine neue Instanz dieser Klasse erzeugt und diese mithilfe der Methode RSS::addItem() dem RSS-Feed hinzugefügt. Die Einträge werden im RSS-Feed in der Reihenfolge abgelegt, in welcher Sie eingefügt werden - unabhängig vom Datum, da das Datum lediglich ein optionales Feld ist.

Der folgende Quellcode demonstriert das Erzeugen der Einträge.

<?php 
foreach ($rows as $row)
{
    $item = new RSSitem();
    // Titel
    $item->title $row['BLOG_TITLE'];
    // Link
    $action 'blog_read_read_blog';
    $blog_id $row['BLOG_ID'];
    $link DisplayUtility::url("action=$action&blog_id=$blog_id"true);
    $link str_replace(session_name() . '=' session_id(), ''$link);
    $item->link $link;
    // Text
    $item->description $row['BLOG_TEXT'];
    // Datum
    $item->pubDate date('r'$row['BLOG_CREATED']);
    // Eintrag hinzufügen
    $rss->addItem($item);
} /* end foreach */
?>

Beachten Sie den Code zum Erzeugen des Links. Dieser verweist auf die Aktion, welche zur Darstellung von Einträgen des Blogs verwendet wird. Die Aktion lautet "blog_read_read_blog". Wie bereits erwähnt, wurde dieser Name automatisch generiert. Die Funktion "url()" der Utility-Klasse "DisplayUtility" erzeugt, wie der Name sagt, eine URL inklusive der Adresse des Servers, Name und Pfad des Skripts und aller erforderlicher Parameter.

Der daraus entstehende Link enthält allerdings auch die aktuelle Session-Id. Diese ändert sich naturgemäß bei jedem Aufruf. Eigentlich sollte dies kein Problem darstellen, allerdings gibt es bedauerlicherweise eine Schwäche moderner RSS-Reader. Denn einige (nicht alle) von diesen sind so programmiert, dass Sie den Link eines RSS-Eintrags gleichzeitig als eindeutige Id verwenden. Sogar wenn explizit eine GUID vorhanden ist, wird diese in der Regel ignoriert und trotzdem der Link benutzt. Aus diesem Grund darf er sich nicht verändern, weil diese Programme andernfalls durcheinander geraten. Das ist der Grund, warum die Session-Id unbedingt aus dem Link entfernt werden muss und das ist auch der Grund, warum der Primärschlüssel des Eintrags im Link auftauchen muss. Dies ist keine Einschränkung des Frameworks, sondern eine unangenehme Eigenschaft der RSS-Reader. Leider werden Sie, wohl oder übel, vorerst mit diesem Problem leben müssen.

Weitere Details zur Verwendung dieser Klasse finden Sie in der API-Dokumentation der Klassen "RSS" und "RSSitem".

Zum Vergleich zeigt der folgende Auszug den gesamten Quellcode der Aktion im Zusammenhang:

<?php 
/* get entries from database */
$query = array(
    'key' => 'blog.*',
    'order_by' => 'blog_created',
    'desc' => true,
    'limit' => 10
);
$rows $this->database->get($query);
$rss = new RSS();
$rss->title 'Blog';
$rss->description 'the 10 most recent blog entries';
foreach ($rows as $row)
{
    $item = new RSSitem();
    // Titel
    $item->title $row['BLOG_TITLE'];
    // Link
    $action 'blog_read_read_blog';
    $blog_id $row['BLOG_ID'];
    $link DisplayUtility::url("action=$action&blog_id=$blog_id"true);
    $link str_replace(session_name() . '=' session_id(), ''$link);
    $item->link $link;
    // Text
    $item->description $row['BLOG_TEXT'];
    // Datum
    $item->pubDate date('r'$row['BLOG_CREATED']);
    // Eintrag hinzufügen
    $rss->addItem($item);
} /* end foreach */
print utf8_encode($rss->toString());
exit(0);
?>

Wie Sie aus dem obigen Quellcode sehen konnten, wird in jedem Eintrag auf die Aktion "blog_read_read_blog" mit dem Parameter "blog_id" verwiesen. Bis jetzt besitzt diese Aktion allerdings diesen Parameter noch nicht. Der folgende Abschnitt wird demonstrieren, wie dieser Parameter dieser Aktion hinzugefügt wird.

Hinzufügen eines neuen Parameters

Suchen Sie in dem Dokument die Aktion "blog_read_read_blog". Diese sollte momentan den folgenden Quellcode aufweisen:

<?php 
/**
 * blog_read_read_blog
 *
 * returns bool(true) on success and bool(false) on error
 *
 * Type:        read
 * Permission:  0
 * Templates:   BLOG_READ_BLOG
 *
 * @access  public
 * @return  bool
 * @name    plugin_blog::blog_read_read_blog()
 * @param   array  $ARGS  array of params passed to the function
 */
function blog_read_read_blog ($ARGS)
{
    /* check input data */
    assert('is_array($ARGS);');
    settype($ARGS'array');

    /* global variables */
    global $YANA;

    /* do something */
    return true;
}
?>

Zunächst sollten Sie prüfen, ob der Parameter "blog_id" überhaupt gesetzt ist, da dies nicht immer der Fall sein muss. Weil die Spalte "blog_id" in der Tabelle "blog" vom Typ "integer" ist, sollten Sie außerdem prüfen, ob der Eingabewert numerisch ist. Außerdem sollten Sie den Typ des Eingabewertes in einen Integerwert ändern. Der folgende Quellcode demonstriert dies.

<?php 
if (isset($ARGS['blog_id']) && is_numeric($ARGS['blog_id'])) {
    $id = (int) $ARGS['blog_id'];
}
?>

An dieser Stelle kommt nun zum Tragen, dass Sie zuvor ( siehe oben ) im Template dieser Aktion für die Funktion "create" den Wert des Parameters "where" auf die Variable "$WHERE" gesetzt haben, denn dadurch können Sie nun durch Setzen der Variable "$WHERE" diesen Parameter beeinflussen.

Um den Wert einer Variable zu setzen rufen Sie die Funktion Yana::setVar auf. Setzen Sie den Wert der Variable auf "blog_id=$id".

<?php 
if (isset($ARGS['blog_id']) && is_numeric($ARGS['blog_id'])) {
    $id = (int) $ARGS['blog_id'];
    $YANA->setVar('WHERE'"blog_id=$id");
}
?>

Abschließend soll noch ein weiteres Feature demonstriert werden. Das Framework kann, falls ein RSS-Feed vorhanden ist, automatisch eine Grafik mit der Aufschrift "RSS" und einen Link auf den RSS-Feed der Anwendung einblenden. Um dieses Feature zu aktivieren, muss lediglich eine Variable gesetzt werden. Der Name dieser Variable lautet "RSS_ACTION" und ihr Wert sollte der Name der Aktion sein, welche den RSS-Feed erzeugt. In diesem Fall "blog_rss".

Die folgende Abbildung zeigt, wie dieses Symbol im Browser dargestellt wird.

Screenshot
Abbildung: Darstellung des Plugins mit RSS-Symbol (Firefox 2.0)

Zum Vergleich noch einmal der gesamte Quellcode der Aktion im Zusammenhang.

<?php 
/**
 * blog_read_read_blog
 *
 * returns bool(true) on success and bool(false) on error
 *
 * Type:        read
 * Permission:  0
 * Templates:   BLOG_READ_BLOG
 *
 * @access  public
 * @return  bool
 * @name    plugin_blog::blog_read_read_blog()
 * @param   array  $ARGS  array of params passed to the function
 */
function blog_read_read_blog ($ARGS)
{
    /* check input data */
    assert('is_array($ARGS);');
    settype($ARGS'array');

    /* global variables */
    global $YANA;
    $YANA->setVar('RSS_ACTION''blog_rss');
    if (isset($ARGS['blog_id']) && is_numeric($ARGS['blog_id'])) {
        $id = (int) $ARGS['blog_id'];
        $YANA->setVar('WHERE''blog_id=' $id);
    }
    return true;
}
?>

Vergleichen Sie Ihr Ergebnis mit dem obigen Quellcode.

Mit diesem Schritt ist die Erstellung Ihres ersten Plugins erfolgreich abgeschlossen.

Literaturhinweise
Falls Sie sich tiefer mit den in diesem Abschnitt behandelten Themen beschäftigen wollen, finden Sie Anleitung in folgenden Artikeln:

Autor: Thomas Meyer, www.yanaframework.net