PHPackages                             kaiwessela/blog - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. kaiwessela/blog

ActiveLibrary

kaiwessela/blog
===============

My personal blog system.

v0.12.2-beta(5y ago)11PHP

Since Mar 16Pushed 1y ago1 watchersCompare

[ Source](https://github.com/kaiwessela/Octopus)[ Packagist](https://packagist.org/packages/kaiwessela/blog)[ RSS](/packages/kaiwessela-blog/feed)WikiDiscussions master Synced today

READMEChangelog (10)Dependencies (2)Versions (9)Used By (0)

Blog
====

[](#blog)

Mein persönliches Blog-System

Einführung
----------

[](#einführung)

### Für wen ist Blog gemacht?

[](#für-wen-ist-blog-gemacht)

Blog ist ein kleines, leichtgewichtiges Content-Management-System, das für Menschen geeignet ist, die bereits Erfahrung mit HTML und CSS haben und die Freiheit und Möglichkeiten dieser Sprachen bei der Gestaltung ihrer Seite voll ausnutzen möchten, andererseits aber nicht auf die Verwaltung ihrer Inhalte durch ein ordentliches CMS verzichten können.

Die meisten bekannten Content-Management-Systeme sind mittlerweile völlig überladen. Wer eine Website mit Wordpress erstellen möchte, hat die Wahl zwischen vorhandenen Themes, die leider fast immer gleich aussehen – wer Ahnung hat, erkennt eine Wordpress-Seite oft schon auf den ersten Blick – und oftmals auch qualitativ zu Wünschen übrig lassen, oder man erstellt sein eigenes Theme und arbeitet sich in eine komplizierte und undurchsichtige Templating-Sprache ein – die aber am Ende auch nur zu HTML geparst wird. Warum nicht direkt HTML und CSS benutzen?

Aus diesem Wunsch heraus entstand Blog. Mein Ziel war und ist es, ein System zu entwickeln, mit dem man mit geringer Einarbeitungszeit und bekannten Werkzeugen eine selbstgeschriebene Website mit einer funktionalen, einfach zu bedienenden Inhaltsverwaltung kombinieren kann.

### Prinzipien

[](#prinzipien)

#### Trennung von Logik und Darstellung

[](#trennung-von-logik-und-darstellung)

Blog ist nach dem bekannten und bewährten [Model-View-Controller](https://de.wikipedia.org/Model_View_Controller)-Modell aufgebaut. Dieses Modell teilt ein Programm in drei Schichten ein: Die Datenbankschicht *(Model)*, die sich um die Kommunikation mit der Datenbank kümmert, die Logikschicht *(Controller)*, in der die eigentlichen Berechnungen ausgeführt werden, und die Darstellungsschicht *(View)*, die sich ausschließlich um die Darstellung kümmert.

Die Darstellungsschicht besteht in Blog aus den Templates. Die Trennung von Logik und Darstellung bedeutet, dass in den Templates nichts mehr programmiert wird, sondern nur die im Controller berechneten Inhalte und Variablen eingefügt werden. Dadurch bleiben die Templates aufgeräumt und einfach zu verstehen.

#### Benutzung bekannter Techniken

[](#benutzung-bekannter-techniken)

Blog zwingt den Benutzer nicht, irgendeine unnötige neue Templating-Sprache zu erlernen oder sich in komplizierte Texteditoren einzuarbeiten, sondern setzt lediglich Kenntnisse in HTML, CSS, Markdown und ein Minimum an PHP voraus (letzteres ist im Zweifelsfall in einer halben Stunde erlernbar).

Neue Blogeinträge oder ähnliches schreibt man ganz einfach in Markdown oder wahlweise in HTML und muss sich nicht auf einen komplizierten WYSIWYG-Editor einstellen, bei dem man oft genug eben nicht das bekommt, was man sieht (oder zumindest nicht was man möchte).

Die Templates verwenden, ganz klassisch, HTML mit Inline-PHP. Durch die Trennung von Logik und Darstellung muss man nicht mehr PHP beherrschen als einfache, einteilige `if`-Abfragen, `foreach`-Schleifen, `include`-Anweisungen (für Untertemplates) und die Inline-Ausgabe-Syntax `` zum Ausgeben des Variablenwertes. Das ist wirklich schon alles.

Grundaufbau
-----------

[](#grundaufbau)

### Objekte und Klassen

[](#objekte-und-klassen)

Datensätze, z.B. Blogeinträge, Seiten und Termine, werden als Objekte gespeichert. Es gibt verschiedene Klassen von Objekten, beispielsweise die Klasse *»Post«* für Blogeinträge, *»Page«* für statische Seiten und *»Event«* für Termine.

Ein Objekt hat immer mehrere Eigenschaften. Manche Eigenschaften, z.B. die ID, besitzen alle Objekte, egal welcher Klasse, andere, z.B. die Eigenschaft `title`, sind klassenspezifisch, werden also nur von Objekten einer bestimmten Klasse, in diesem Fall *»Post«*, verwendet.

#### Allgemeine Eigenschaften

[](#allgemeine-eigenschaften)

Die Eigenschaften `id` und `longid` sind allgemein, das heißt, dass sie von Objekten aller Klassen verwendet werden. Im Folgenden erkläre ich diese beiden wichtigen Eigenschaften genauer.

##### id

[](#id)

Bei der Erstellung eines Objekts, also zum Beispiel beim Anlegen einer neuen Seite oder beim Schreiben eines neuen Blogeintrags, wird eine eindeutige `id` automatisch generiert und dem neuen Objekt zugewiesen. Sie ist zufällig, nicht änderbar und besteht aus 8 hexadezimalen Zeichen.

Die `id` lässt sich sehr gut für Shortlinks verwenden, beispielsweise könnte man über  zum Eintrag *Mein erster Artikel* gelangen.

##### longid

[](#longid)

Die `longid` hat ebenso wie die `id` die Funktion, ein Objekt eindeutig zu identifizieren. Allerdings kann der Ersteller des Objekts, also der Autor eines Blogeintrags etwa, die `longid` selbst festlegen. Zweck der `longid` ist, ein eindeutiger, aber gut lesbarer Identifikator eines Objekts zu sein. Deshalb wird sie beispielsweise in der URL-Leiste zum Aufrufen eines Objekts verwendet. Die URL  lässt den Inhalt eben viel besser erahnen als .

Die `longid` muss, um eindeutig von einer `id` unterscheidbar zu sein, mindestens 9 Zeichen lang sein (maximal 60) und darf nur aus lateinischen Buchstaben *(a-z/A-Z)*, den Ziffern *0-9* und Bindestrichen *(-)* bestehen. Sie kann, nachdem sie einmal gesetzt wurde, nicht mehr geändert werden. Dies würde dem Webstandard zuwiderlaufen, dass eine Ressource immer über die gleiche Adresse erreichbar sein sollte.

Routing
-------

[](#routing)

Blog erlaubt es dem Anwender, frei festzulegen, wie die URLs seiner Seite strukturiert sein sollen. Aus diesem Grund gibt es die Routing-Konfiguration *»routes.json«*. Sie sorgt dafür, dass ein bestimmter Pfad (z.B. */artikel/mein-erster-artikel*) der richtigen Seite zugeordnet wird.

Außerdem kann der Anwender entscheiden, wie die Seite aufgebaut sein soll, beispielsweise welches Template und welche Controller geladen werden sollen.

### Pfadnotation

[](#pfadnotation)

Eine Route beginnt immer mit der Notation des zugehörigen Pfades. So soll z.B. beim Aufruf von `artikel` eine Liste von Blogeinträgen angezeigt werden, bei `artikel/mein-erster-artikel` jedoch der einzelne Artikel mit der longid mein-erster-artikel.

#### Statische Form

[](#statische-form)

Im einfachsten Fall schreibt man also:

```
{
	"artikel": {
		…
	},
	"artikel/mein-erster-artikel": {
		…
	},
	…
}

```

Damit hat man den beiden Routen gültige Pfade zugewiesen. Nun wäre es aber sehr aufwändig und unpraktisch, jeden neuen Artikel einzeln in die Routing-Tabelle einzutragen. Deshalb gibt es verschiedene Arten, Platzhalter in die Pfadnotation einzubauen.

#### Wildcards

[](#wildcards)

Es gibt folgende Wildcards, die alleinstehend gültige Pfadnotationen sind: `/` und `*`.

`/` steht dabei für einen leeren Pfad, wird also in dem Fall aufgerufen, wenn der Benutzer  aufruft. Typischerweise wird dann die Startseite angezeigt.

`*` steht für alle Pfade, die es irgendwie geben könnte. Es ist also eine *Catch-All*-Notation, die dann aufgerufen wird, wenn die Pfadnotationen aller vorherigen Routen nicht zutreffend waren. Deshalb sollte die Route mit dieser Wildcard ganz am Ende der Routing-Datei stehen, weil eventuell nachfolgende Routen sonst gar nicht aufgerufen werden könnten.

#### PathPatterns

[](#pathpatterns)

Ein PathPattern ist eine Pfadnotation, die einfache Platzhalter ermöglicht. So würde man unser obiges Beispiel `artikel/mein-erster-artikel` eher mit dem PathPattern `artikel/*` beschreiben. Dieses PathPattern trifft auf alle Artikel zu, also auch beispielsweise auf `artikel/urlaubsgruesse`, `artikel/meine-lebensgeschichte` und `artikel/zum-geburtstag`. Im Folgenden beschreibe ich diese Notation genauer.

*Zum Überprüfen:* Ein gültiges PathPattern wird durch diesen regulären Ausdruck definiert: `^([A-Za-z0-9-]+|[\*#]({[0-9]+,?[0-9]*})?)(\/([A-Za-z0-9-]+|[\*#]({[0-9]+,?[0-9]*})?))*([\*#]*\??)?$`

##### Segmente

[](#segmente)

Ein Pfad besteht aus mehreren Segmenten. In unserem Beispiel `artikel/urlaubsgruesse` sind `artikel`und `urlaubsgruesse` jeweils ein Segment. Segmente werden durch Schrägstriche (`/`) verbunden. Am Anfang und am Ende stehen übrigens keine Schrägstriche.

##### Platzhalter

[](#platzhalter)

Im PathPattern können Segmente nun durch Platzhalterzeichen ersetzt werden:

- `*` steht für jedes beliebige Zeichen innerhalb eines Segments.
- `#` steht für eine Zahl beliebiger Größe.

**Vorsicht:** Es darf immer nur genau ein Platzhalter für genau ein Segment stehen, niemals mehrere Platzhalter im gleichen Segment, Platzhalter gemeinsam mit anderen Zeichen im gleichen Segment oder ein Platzhalter für mehrere Segmente.

So würde das PathPattern `artikel/*` also, wie schon oben gezeigt, auf alle beliebigen Pfade `artikel/[artikel-longid]` zutreffen, während das PathPattern `artikel/#` nur auf die Pfade `artikel/1`, `artikel/2`, `artikel/3`, … zutrifft. Letzteres nutzt man beispielsweise, wenn die Artikelliste grundsätzlich über die URL  zu erreichen ist, es jedoch so viele Artikel gibt, dass man sie auf mehrere Seiten aufteilen muss und die zweite, dritte, vierte usw. Seite der Artikelliste einfach über eine angehängte Zahl (z.B. ) zu erreichen sein soll.

##### Quantifikatoren

[](#quantifikatoren)

Nun haben wir im vorherigen Beispiel aber ein Problem: Wenn wir das PathPattern `artikel/#`für die Artikelliste verwenden, landen Aufrufe von  (ohne Nummer) im Leeren. Wir müssten nun zwei Routen mit den Pfadnotationen `artikel` und `artikel/#` einrichten, obwohl sie eigentlich das gleiche Ziel haben (oder fast das gleiche, aber dazu kommen wir später).

Um dieses Problem zu lösen, gibt es Quantifikatoren. Sie stehen hinter den Platzhaltern und bestimmen, für welche Anzahl von Buchstaben oder Zahlen oder Zeichen der Platzhalter stehen soll.

Folgende Quantifikatoren gibt es:

- `{n}`, also `{1}`, `{2}` usw. bedeutet, dass der davorstehende Platzhalter nur für *n* Zeichen stehen darf, `artikel/#{1}` trifft also auf **artikel/1**, **artikel/2** oder **artikel/3** zu, nicht aber auf **artikel/11** oder **artikel/123**.
- `{n,m}`, also `{1,2}`, `{2,4}` usw. bedeutet, dass der davorstehende Platzhalter nur für *n bis m*Zeichen stehen darf. `artikel/#{2,4}` trifft also auf **artikel/12**, **artikel/567** oder **artikel/9999** zu, nicht aber auf **artikel/2** oder **artikel/12345**.
- `{n,}`: *m*, also der zweite Parameter, kann auch weggelassen werden. Dann steht dieser Quantifikator für *mindestens n* Zeichen. Die Umkehrung, die Zahl vor dem Komma (*n*) wegzulassen, funktioniert allerdings nicht.
- `?` ist ein Spezialfall. Dieser Quantifikator darf nur am Ende des letzten Segmentes stehen. Er macht den Platzhalter des letzten Segmentes optional. `artikel/#?` würde also sowohl auf die URL  als auch auf  zutreffen.

**Vorsicht:** Quantifikatoren dürfen nur hinter Platzhaltern stehen, niemals davor, alleine oder hinter sonstigen Zeichen.

#### Reguläre Ausdrücke (RegEx)

[](#reguläre-ausdrücke-regex)

Reguläre Ausdrücke sind die komplizierteste, aber auch mächtigste Form für die Pfadnotation. Anfänger werden sie wahrscheinlich noch nicht benötigen, geübten Nutzern ermöglichen sie aber fast absolute Freiheit in der Gestaltung ihrer Pfadkonfiguration. Sie beginnen mit `/^`, was den Anfang des Pfades kennzeichnet, und enden mit `$/` für das Ende. Dazwischen können alle bekannten RegEx-Suchmuster eingesetzt werden.

Zur Prüfung wird ausschließlich der Pfadabschnitt der URL herangezogen, also nicht der Host, nicht der Query-String (`?xy=z`) und auch nicht das Fragment (`#abc`). Außerdem werden eventuell vorhandene Schrägstriche an Anfang und Ende abgeschnitten. Der RegEx prüft aus der URL `https://example.org/artikel/mein-erster-artikel/?queryString=true#kapitel-2` also nur den Abschnitt `artikel/mein-erster-artikel`. Bei der Pfadnotation mittels RegEx sollte dies bedacht werden.

**Wichtig:** Die Schrägstriche (`/`), die die Pfadsegmente trennen, müssen doppelt mit Backslashes (`\`) escapt werden, einmal weil sie durch den JSON-Parser laufen, zum Zweiten, weil sie sonst als RegEx-Endzeichen missinterpretiert würden.

Blog verwendet intern nur RegEx, schreibt also statische Notationen, Wildcards und PathPatterns in reguläre Ausdrücke um. Daher dürften Teile der PathPatterns den RegEx-Experten auch schon bekannt vorkommen, sie wurden einfach davon übernommen. Beispiele für die interne Umschreibung sind:

- `artikel/mein-erster-artikel` wird zu `/^artikel\/mein-erster-artikel$/`
- `artikel/*` wird zu `/^artikel\/[^\/]+$/`
- `artikel/*{0,8}` wird zu `/^artikel\/[^\/]{0,8}$/`
- `artikel/#?` wird zu `/^artikel(\/[0-9]+)?$/`
- `/` (Wildcard) wird zu `/^$/`
- `*` (Wildcard) wird zu `/^.*$/`

### Ersetzungszeichen

[](#ersetzungszeichen)

In unserem Beispiel trifft die Route mit der Pfadnotation `artikel/#?` sowohl auf die URL  als auch auf die URL  zu. Das ist auch so gewollt. Allerdings möchten wir, dass beim Aufruf der zweiten URL eine andere Artikelliste angezeigt wird als in der ersten URL, schließlich führt die erste auf die erste Seite und die zweite auf die zweite Seite.

Wir haben bereits gelernt, dass wir `page`-Attribut dem Controller mitteilen können, welche Seite einer Liste wir erhalten möchten. Allerdings können wir das bisher nur statisch, wir können also nur die Werte `1`, `2`, `3`, … , `n` eintragen. In diesem Fall muss das `page`-Attribut aber dynamisch bestimmt werden, denn die gleiche Route trifft auf verschiedene Seiten zu.

Wenn wir uns noch einmal den Pfad anschauen, sehen wir ja, dass im zweiten Segment eigentlich schon steht, welche Seite wir aufrufen möchten. Wir müssen dem Router nur mitteilen, dass er dieses URL-Segment in das `page`-Attribut einsetzen soll. Dafür gibt es Ersetzungszeichen.

Ein Ersetzungszeichen entspricht dem Muster `?n`, ist also ein Fragezeichen gefolgt von einer Zahl. Die Zahl gibt an, auf welches Pfadsegment sich das Ersetzungszeichen bezieht. Im Falle von unserer Beispiel-Pfadnotation `artikel/#?` schreiben wir also `"page": "?2"`, weil im zweiten Pfadsegment die Information steht, die wir in das `page`-Attribut einsetzen möchten. Der Router erkennt nun dieses Ersetzungszeichen und setzt den Inhalt des Pfadsegmentes in das Attribut ein.

Das Ersetzungszeichen kann nicht überall verwendet werden, sondern bisher nur in den Attributen `template`, `identifier`, `page` und als Controller-Name (siehe Kapitel soundso). Allerdings kann es auch zwischen anderen Zeichen stehen, `"template": "seite-?2"` wäre also auch gültig.

*Übrigens:* Dass im Falle von  das zweite Pfadsegment fehlt, ist kein Problem. Der Router setzt für das `page`-Attribut automatisch den Wert `1` ein, falls das Pfadsegment leer ist.

Templating
----------

[](#templating)

Wie schon beschrieben, benutzt Blog ausschließlich HTML mit Inline-PHP, um Templates zu erstellen. Es ist nicht nötig, eine neue und komplizierte Templating-Sprache zu erlernen, die am Ende sowieso wieder zu HTML geparst wird.

Grundsätzlich gilt die Regel »Eine Route – Ein Template«. Für jede Route (also für jede unterschiedliche Seitenstruktur) soll es auch ein eigenes Template geben. Ein einfaches Template sieht vielleicht folgendermaßen aus:

```
