Du brauchst vielleicht kein Framework für deine PHP Web API

Wenn du schon mal mit ASP.NET Web API, bzw. MVC gearbeitet hast und du dir ein ähnliches System in PHP wünschst, bist du hier komplett richtig!

Dabei verzichten wir auf komplexe Frameworks und halten alles schön leicht und simpel. Ideal für einfache Web APIs ohne Abhängigkeiten.

Inhalt

  1. Los geht’s!
  2. Der Basis-Controller
  3. Unser erster Controller
  4. Zurück zur index.php
  5. Schönere URLs
  6. Fertig!

Los geht’s!

Erstelle in deinem Lieblings-Codeeditor einen neuen Projektordner (ich nenne meinen Ordner „php-web-api“). Als Codeeditor bevorzuge ich übrigens Visual Studio Code, aber du kannst natürlich nehmen, womit du am liebsten arbeitest.

Der Startpunkt jeder PHP-Anwendung ist die Datei index.php, also legen wir diese als erstes an.

Die Ordner-Struktur sieht jetzt so aus:

  • 📄 index.php

Okay, index.php lassen wir allerdings erst noch leer, denn es geht erst einmal woanders weiter.

Der Basis-Controller

Wir definieren einmalig die Klasse Controller. Dafür legen wir zunächst den Ordner Classes an und erstellen die Datei Controller.php.

Unser Projekt-Ordner:

  • 📁 Classes
    • 📄 Controller.php
  • 📄 index.php

Yay, wir kommen zum Code-Part! Hier ist schon mal Controller mit einer Eigenschaft:

<?php

class Controller {
    protected $Action;
}

Controller->Action wird den Namen der Methode enthalten, die ausgeführt werden soll.

Fahren wir fort, indem wir den Konstruktor definieren:

<?php

class Controller {
    protected $Action;

    function __construct() {
        $this->Action = filter_input(INPUT_GET, "action");

        if (!$this->Action) {
            $this->Action = "Index";
        }
    
        // Let execute the method the user has set in the URL parameter
        $this->runActionMethod($this->Action);
    }
}

Welche Action (Controller-Methode) ausgeführt werden soll, wird über den URL-Parameter action (z.B. ?action=MyAction) festgelegt.
filter_input(INPUT_GET, "action") ist übrigens ähnlich wie $_GET["action"]. Sollte der Parameter nicht gesetzt sein, soll als Fallback immer die Index-Action ausgeführt werden.

$this->runActionMethod($this->Action) lässt die Action-Methode letztendlich ausführen. Controller->runActionMethod definieren wir jetzt:

<?php

class Controller {
    protected $Action;

    function __construct() {
        // ...
    }

    private function runActionMethod($actionMethod) {
        if (method_exists($this, $actionMethod)) {
            $reflection = new \ReflectionMethod($this, $actionMethod);
            if ($reflection->isPublic()) {
                // Bypasses parameters from URL (GET) to the action method
                $paramaters = [];
                foreach($reflection->getParameters() as $arg) {
                    array_push($paramaters, filter_input(INPUT_GET, $arg->name));
                }

                $response = call_user_func_array([$this, $actionMethod], $paramaters);

                // Will print the response as JSON to client
                header("Content-Type: application/json");
                echo json_encode($response);
            }
            else {
                throw new \RuntimeException("This Action Method is not public.");
            }
        }        
        else {
            throw new \RuntimeException("This Action Method does not exist.");
        }    
    }
}

Zunächst wird geprüft, ob die Action-Methode existiert. Außerdem soll die Methode nur ausgeführt werden können, wenn sie public ist. Um das zu prüfen, verwende ich ReflectionMethod. In den Zeilen 14-18 werden weitere Parameter aus der URL ausgelesen. Mit $response = call_user_func_array([$this, $actionMethod], $paramaters); wird die Action-Methode ausgeführt und auch die Parameter werden dabei übergeben. Der Rückgabewert der Methode wird anschließend im JSON-Format ausgegeben.

Unser erster Controller

Für die Controller legen wir extra einen Ordner im Root-Ordner an:

  • 📁 Classes
    • 📄 Controller.php
  • 📁 Controllers
    • 📄 HelloWorldController.php
  • 📄 index.php

Wie es sich für ein Tutorial gehört, nennen wir den Controller natürlich „HelloWorld“. Alle Controller enden immer mit dem Zusatz „Controller“.

In Controllers/HelloWorldController.php kommt jetzt das hier:

<?php

class HelloWorldController extends Controller {
    public function Index() {
        return "Hello";
    }

    public function SayHi($id) {
        return "Hi, $id!";
    }
}

Jede öffentliche Methode ist ein anzusprechender Endpunkt der Web API. Hier also Index und SayHi. SayHi kann mit $id außerdem noch ein Parameter mitgegeben werden.

Zurück zur index.php

Alle wichtigen Komponenten für die Web API haben wir angelegt: Die Basisklasse für alle Controller (Classes/Controller.php) und mit Controllers/HelloWorldController.php einen Beispiel-Controller. Nach dem gleichen Schema können im Controllers-Ordner noch beliebig viele weitere Controller hinzugefügt werden.

Jetzt gilt es diese Komponenten in der index.php zusammenzufassen, denn die ist ja noch leer. Füge folgenden Code ein:

<?php

require __DIR__ . "/Classes/Controller.php";

// Autoload Controller classes
$controllerFiles = scandir(__DIR__ . "/Controllers");
foreach ($controllerFiles as $currFile) {
    if ($currFile != "Controller.php" && strpos($currFile, "Controller") !== false) {
        require __DIR__ . "/Controllers/" . $currFile;
    }
}

$controllerParameter = filter_input(INPUT_GET, "controller");

if ($controllerParameter && $controllerParameter != "php") {
    $controllerClassName = $controllerParameter . "Controller";

    if (class_exists($controllerClassName, false)) {
        new $controllerClassName(true);
    }
    else {
        throw new \RuntimeException("This Controller does not exist");
    }
    exit;
}

Zunächst importieren wir die Basis-Klasse (Classes/Controller.php). In den Zeilen 5-11 werden alle Controller-Klassen importiert, indem alle PHP-Dateien eingebunden werden, die sich im Controllers-Ordner befinden und auf „Controller“ enden.

In den weiteren Zeilen wird der URL-Parameter controller abgerufen und basierend darauf der entsprechende Controller geladen und ausgeführt. Sollte der angefragte Controller nicht existieren, wird eine Fehlermeldung ausgegeben.

So. Also, im Grunde ist unsere Web API jetzt fertig. Um sie jetzt zu nutzen, können die Controller-Actions auf diese Weise angesprochen werden:

https://my-api.com/?controller=HelloWorld&action=SayHi&id=Tom

Schönere URLs

Okay, kann man so lassen. Sollte man aber vielleicht nicht. Viel schöner wäre doch so eine URL:

https://my-api.com/HelloWorld/SayHi/Tom

Dafür fügen wir dem Root-Ordner eine .htaccess-Datei hinzu:

  • 📁 Classes
    • 📄 Controller.php
  • 📁 Controllers
    • 📄 HelloWorldController.php
  • 📄 .htaccess
  • 📄 index.php

Und das fügst du ein:

RewriteEngine On

# For some webhostings you need to uncomment the next line
#RewriteBase /

# Server Routing Rule
# The routing structure is pretty flexible. Allowed is:

# /MyControllerName/MethodName/Value
RewriteRule ([-a-zA-Z0-9]+)/([-a-zA-Z0-9]+)/([-a-zA-Z0-9]+)/?$ index.php?controller=$1&action=$2&id=$3 [QSA]

# /MyControllerName/MethodName
RewriteRule ([-a-zA-Z0-9]+)/([-a-zA-Z0-9]+)/?$ index.php?controller=$1&action=$2 [QSA]

# /MyControllerName 
# In this case, the Index() method of the controller will be used. Of course this will only work when it's set.
RewriteRule ([-a-zA-Z0-9]+)/?$ index.php?controller=$1 [QSA]

Fertig!

Den gesamten Projektordner findest du auch nochmal auf GitHub: https://github.com/lgkonline/php-web-api
Nutze die Repo gerne als Vorlage, wenn du ein neues Projekt erstellen willst.

Wenn du Fragen hast oder Feedback hinterlassen willst, schreibe mir gerne auf Twitter über @lgkonline (EN) oder @3lgeekay (DE).

Quicktip: Browser für Create React App definieren

Create React App ist der einfachste Weg, ein React-Projekt zu erstellen. Ein Debug-Server wird bereits mitgeliefert. Mit npm start wird dieser gestartet und die App wird im Standardbrowser des Betriebssystems geöffnet.

Doch manchmal möchtest du vielleicht, dass die App in einem anderen Browser geöffnet wird. In meinem Fall nutze ich Safari zwar als Standardbrowser, zum Entwickeln nutze ich aber lieber andere Browser, wie Chrome oder Firefox, da diese meine Meinung nach, bessere Entwickler-Tools bieten.

So geht’s

Glücklicherweise kannst du den Browser selbst definieren, welcher bei npm start geöffnet werden soll.

  1. Erstelle eine neue Datei namens .env ins Root-Verzeichnis deines Projekts.
  2. Um Firefox als Browser zu setzen, schreibe folgendes in Datei:
BROWSER=Firefox

Der einzutragende Browser-Name kann sich von Plattform zu Plattform unterscheiden. Die Bezeichnung von Google Chrome beispielsweise unter macOS ist google chrome, unter Windows chrome und unter Linux google-chrome.

Tutorial: Neues React-Projekt mit Uisum

Mit Uisum habe ich eine Library zusammengestellt, die es (ursprünglich in erster Linie mir selbst) erleichtern soll, ein neues Web-App-Projekt mit React zu erstellen. Als Basis nutzt Uisum das beliebte CSS-Framework Bootstrap und liefert eine Reihe an Komponenten, die für moderne Mobile-First-Anwendungen hilfreich sind.

Beitragsbild: Pixabay, Quelle: Pexels

„Tutorial: Neues React-Projekt mit Uisum“ weiterlesen

CSS: Nutzt User dunkles oder helles Theme?

@media (prefers-color-scheme: dark) {
    :root {
        --body-bg: black;
        --body-bg-stronger: rgba(255,255,255,.3);
        --control-bg: rgba(255,255,255,.15);

        --body-color: rgba(255,255,255,.5);
        --front-color: white;
        --active-color: rgba(255,255,255, .9);
    }
}

Mit dem neuesten Update von macOS wird Safari 12.1 installiert. Damit ist Safari der erste Browser, der das neue CSS Media-Query-Feature prefers-color-scheme unterstützt.

Beitragsbild: cmonphotography, Quelle: Pexels

„CSS: Nutzt User dunkles oder helles Theme?“ weiterlesen

Lösung für Twenty Seventeens stockenden Header

Für eine lange Zeit habe ich für diesen Blog eine angepasste Version des WordPress Themes „Twenty Seventeen“ genutzt. Generell finde ich den Theme ziemlich nett, aber es gibt einen kleinen „Bug“ (?), der mich schon länger gestört hat.

There’s also an english version of this article on my Medium site. Beitragsbild: Pixabay, Quelle: Pexels

„Lösung für Twenty Seventeens stockenden Header“ weiterlesen

TheFink – Die Plattform für Denker und Macher

Wenn du zur deutschsprachigen Creator-Szene gehörst, hast du eventuell mal etwas von TheFink gehört (wenn nicht, auch nicht schlimm 😉). TheFink ist eine entstehende Plattform im Web für (in erster Linie) deutschsprachige kreative Menschen. Egal ob du gerne entwickelst, Dinge designst oder Musik produzierst. Auf TheFink sollst du die Möglichkeit bekommen, dich, dein Können und deine Ideen zu präsentieren und auch um coole Projekte zusammen mit anderen Leuten zu starten.

Während die Plattform selbst sich noch in Entwicklung befindet, hast du bereits jetzt die Möglichkeit, dich auf https://thefink.net zu registrieren (btw von mir entwickelt), anschließend kannst du bereits unserem Discord-Server beitreten und unseren Newsletter abonnieren, damit du was die Entwicklung angehst auf dem neuesten Stand gehalten wirst.

Mit Asiw und Uisum ans Ziel!

Für die Entwicklung der aktuellen Site kamen hauptsächlich zwei Libraries zum Einsatz:

  • Asiw fürs Server-Backend mit MVC-Pattern
  • Uisum als UI-Kit, die zusammen mit React und Bootstrap funktioniert

Diese beiden Libraries können frei verwendet werden, den Quellcode dazu gibt es auf GitHub. Asiw musste ich zwar stark abändern, da die Form, die ihr auf GitHub findet doch sehr abgespeckt ist. Ich denke aber, dass Asiw eine gute Basis für einen RESTful Service in PHP ist. Der Aufbau von Asiw ist übrigens sehr stark von ASP.NET Web API inspiriert, wenn du also damit mal gearbeitet hast, wirst du dich sehr schnell zurecht finden.

Für das Frontend kam Uisum als UI-Kit zum Einsatz. Uisum (kommt im übrigen aus dem Lateinischen und heißt soviel wie „betrachten“) liefert nützliche React-Komponenten für den Grundaufbau einer Web App. Für das Styling wird hauptsächlich Bootstrap genutzt. Der Vorteil: Durch das Abändern von SCSS-Variablen lässt das Aussehen sehr gut personalisieren.
Noch ein Tipp: Durch dieses Tool wird das Anpassen von Bootstrap-Variablen noch angenehmer: https://lgkonline.github.io/customize-bootstrap/

Mit diesem Hack verbesserst du die User Experience von CodePen

CodePen ist für mich eine geniale Plattformen, um sich inspirieren zu lassen und herauszufinden, was durch Web-Technologien alles möglich ist.

Was mich aber störte: die Pen-Gallerie. Gerade wenn man nur kurz in das Ergebnis hereinschauen wollte, dauert es sehr lange, weil die Seite für einen Pen ganz neu aufgebaut werden musste. Dann musste man wieder zurück zur Gallerie navigieren (was auch nochmal dauerte).

Also habe durch die Konsole des Browsers per JavaScript einen kleinen Hack geschrieben:

See the Pen CodePen gallery hack by Lars Gerrit Kliesing LGK (@lgkonline) on CodePen.

Der Code bewirkt, dass die Links zu den Pens jeweils in einen Iframe geöffnet werden, der dann in den Vordergrund gerät. Nach einem Klick außerhalb des Iframes wird dieser wieder geschlossen und man kann den nächsten Link auswählen.

Ein kleines Problem war, dass bei einem Wechsel auf eine andere Seite das Verhalten wieder zurück gesetzt wurde. Als Workaround wird mein Programm kurz nach einem Seitenwechsel erneut ausgeführt.

So nutzt du den Hack

Während du dich in der Gallerie von CodePen befindest, öffne die Konsole deines Browsers (unter Windows ist das meistens F12). Und füge dort den Code von oben ein.

Das kannst du so machen. Allerdings müsstest du das bei jedem Besuch wiederholen, da der Code ja nicht dauerhaft gespeichert wird. Durch Browser-Erweiterungen (wie z.B. dieser für Chrome) könntest du aber erreichen, dass der Hack jedes Mal ausgeführt wird.

Wie ihr Frosted Glass fürs Web nutzen könnt

Der sogenannte Frosted Glass-Effekt (oder von Microsoft auch Acrylic genannt) ist momentan sehr angesagt. Ich habe auch schon mal ein Video produziert, in dem ich gezeigt habe wie ihr den Effekt mit CSS erzeugen könnt. Leider gibt es mit dieser Technik noch ein paar Probleme, wodurch man sie noch nicht wirklich produktiv einsetzen kann.

Ein Problem ist, dass für die Technik immer ein Hintergrundbild definiert werden muss. Das setzt aber voraus, dass zwischen dem Element mit dem Effekt und dem Hintergrund nichts liegen darf.

Das Element mit dem Effekt ist nämlich nicht wirklich transparent, sondern hat nur den gleichen Hintergrund wie der Body.
Man könnte eine Art Screenshot machen und dieses als Hintergrund benutzen.
Das habe ich mal ausprobiert:

Wie im Beispiel beschrieben, nutze ich html2canvas um das HTML in ein Canvas zu konvertieren. Das Canvas wandel ich dann wiederum in ein Bild um, damit es als Hintergrundbild genutzt werden kann.
Das gesamte Beispiel gibt es hier: https://lgkonline.github.io/frosted-glass/

Die React Komponente react-acrylic geht hier den gleichen Weg, um den Effekt zu erzeugen. Es sei aber gesagt, diese Technik nicht sehr performant ist, da es sehr rechenintensiv ist ein Screenshot zu erzeugen und je nachdem wie viele Elemente es auf der Seite gibt, dauert es auch länger.

Ein anderes Problem ist, dass Browser wie Edge und Firefox beim Scrollen stocken, wenn ein Hintergrundbild als fixed gesetzt ist. Dafür habe ich leider noch keine Lösung gefunden. Diese Problematik wird hier auf Stack Overflow auch genannt.

Auch ist es doch sehr aufwändig, den Effekt zu erzeugen. Es gibt aber Möglichkeiten, sich die Arbeit zu erleichtern.
Auf CodePen habe ich ein Beispiel veröffentlicht, in dem ich eine React Komponente erstellt habe, wodurch ich den Effekt schneller an jeder beliebigen Stelle einbinden kann:

See the Pen FrostedGlass React Component by Lars Gerrit Kliesing LGK (@lgkonline) on CodePen.

Update

Ich habe eine neue CSS-Spezifikation gefunden, mit der es deutlich einfacher wird, den Effekt einzusetzen: backdrop-filter
Mit backdrop-filter: blur(10px); könnte man also Frosted Glass realisieren.
Derzeit wird backgroup-filter leider nur von Safari für macOS und iOS unterstützt, soll aber auch in der nächsten Edge-Version verfügbar sein (Quelle: caniuse.com).

Ich konnte backdrop-filter erfolgreich in diesem Pen testen.

Link zum CodePen: https://codepen.io/lgkonline/pen/GQrLeV

Mac als optimale Plattform für Entwickler – und wie Microsoft mit dazu beiträgt

Ich bin selber Entwickler. Und wenn ich im Internet Fach-Artikel oder auch offizielle Dokumentationen von Bibliotheken lese, stelle ich immer wieder fest, dass viele andere Entwickler offenbar macOS statt Windows oder einer Linux Distribution nutzen.

Selbst Microsoft präsentiert in den Blogs von Visual Studio Code größtenteils Screenshots von macOS.
Und das, obwohl sie doch selbst immer betonen, dass Windows 10 die beste Plattform für Entwickler sei. Tatsächlich scheint aber macOS viel optimaler für Entwickler zu sein. Warum? Dafür brauchen wir nur zu schauen, welche Plattformen heute für Endanwender am relevantesten sind: Smartphones und Web. Die Anwendungen dafür werden größtenteils in JavaScript entwickelt (das geht auch aus der GitHub Octoverse 2017 Statistik hervor). Der größte Vorteil an der JavaScript-Entwicklung ist, dass dies sowohl unter Linux, Windows und macOS möglich ist.
Was aber nicht unter allen Plattformen möglich ist: Das Erstellen einer mobilen App. Zumindest das Kompilieren von iOS-Apps erlaubt Apple nur auf Mac-Geräten. Jetzt kann man als Entwickler sagen: „Fuck it, dann entwickle ich halt nur für Android.“ Aber wer ernsthaft mit seiner App Geld verdienen will, für den ist der iOS-Marktanteil einfach zu wichtig, man kann ihn nicht einfach ignorieren.

Es war also ein kluger Schachzug von Apple iOS-Kompilierung nur von Macs zu erlauben. Denn während der PC-Kauf jährlich abnimmt, scheint dies nicht für Apple-PCs zu zutreffen: Mac-Verkaufszahlen im Q2 2017 widerstehen dem Trend | Mac Life

Warum ich nicht mehr Adobe Portfolio verwende

Für die, die es nicht kennen: Adobe Portfolio ist eine Art Website-Baukasten insbesondere für Kreativschaffende, um einfach und ohne nötigen Programmierkenntnissen eine eigene Portfolio-Website zu erstellen.

Auch wenn ich Kenntnisse in der Web-Entwicklung sehr wohl besitze, hielt ich Adobe Portfolio durchaus für interessant. Vor allem die Synchronisation mit Behance ist durchaus praktisch. Alle meine Projekte von dort wurden automatisch auf der Portfolio-Site angezeigt.
In meinem Creative Cloud-Abo ist die Nutzung von Adobe Portfolio übrigens inklusive. Ich musste also nichts extra bezahlen. Auch das war ein Grund für mich, warum ich das ausnutzen sollte.

Nun aber der ausschlagende Grund, warum ich mich für eine Alternative entschieden habe: Die Nutzung von HTTPS in Kombination mit einer eigenen Domain ist unmöglich.  Es ist seit längerem bekannt, dass Suchmaschinen Webseiten mit HTTP schlechter einstufen, als welche mit HTTPS. Browser, wie Chrome kennzeichnen Webseiten mit Eingabefeldern und ohne HTTPS sogar als „Nicht sicher“.
Dieses Manko wurde bereits vor Jahren an Adobe kommuniziert, getan hat sich aber noch nichts. Wenigstens ist dieses Problem im FAQ aufgeführt: Adobe Portfolio FAQ

Der Umstieg

Ich entschied mich also dazu, meine Portfolio-Site selbst zu gestalten und zu entwickeln. Glücklicherweise bietet Behance eine API an. Damit kann ich also auch weiterhin meine Projekte anzeigen lassen.
Gestalterisch habe ich mich zunächst an das Template aus Adobe Portfolio orientiert. Dass ich jetzt alle Freiheiten habe, habe ich das natürlich auch genutzt und habe beispielsweise als Font HK Grotesk verwendet. Bei Adobe Portfolio ist die Schriftarten-Auswahl relativ begrenzt gewesen.

Cool ist auch, dass ich mein Portfolio jetzt auch in mehreren Sprachen anbieten kann. Das war bei Adobe Portfolio auch nicht möglich.
Das neue Portfolio habe ich übrigens u.a. mit React realisiert.