HTML: Links über rel-Attribut absichern

Wenn du auf eine externe Webseite verlinkst und über target="_blank" festlegst, dass die Seite in einem neuen Tab geöffnet werden soll, wird empfohlen zusätzlich das Attribut rel="noopener noreferrer" zu setzen:

<a href="https://lgk.io" target="_blank" rel="noopener noreferrer">Klick</a>

Snippet für Visual Studio Code

Snippets in Visual Studio Code sind Shortcuts um vorgefertigte Code-Schnipsel einzufügen.

Hier die Snippet-Konfig für target="_blank" rel="noopener noreferrer":

{
	...
	"Secure target blank": {
		"prefix": "target=\"_blank\" rel=\"noopener noreferrer\"",
		"body": "target=\"_blank\" rel=\"noopener noreferrer\""
	}
}

Mehr Infos zu Snippets in Visual Studio Code.

Guide: Use Git for deployment

Prepare server

  1. Use SSH to get to webspace: ssh user@webspace.host
  2. Navigate to correct directory: cd myapp/production
  3. Create Git folder: mkdir .git
  4. Navigate to the folder: cd .git
  5. Init bare repository: git init --bare
  6. Create file called „post-receive“ in .git/hooks with this content:
#!/bin/sh

# The production directory
TARGET="/kunden/homepages/xx/xxx/htdocs/myapp/production/app"

# The Git repo
REPO="/kunden/homepages/xx/xxx/htdocs/myapp/production/.git"
# Deploy the content to the directory
git --work-tree=$TARGET --git-dir=$REPO checkout -f

Update: It’s important that your „post-receive“ file has the permission to execute. In my case, I gave the file permission 705.

Prepare local development setup

  1. Project folder has to be a Git repository. If it isn’t already, run: git init
  2. Add remote: git remote add deploy ssh://user@webspace.host/kunden/homepages/xx/xxx/htdocs/myapp/production/.git

Upload changes

  1. Commit local changes.
  2. Run this to upload them: git push deploy HEAD

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

In eigener Sache: Neues Design und wie es weitergeht

In den letzten Tagen experimentierte ich am Aussehen von Neue St herum und das Redesign ist jetzt soweit abgeschlossen. Ab sofort wird eine abgeänderte Variante des WordPress Themes „Twenty Nineteen“ verwendet. Mit dem neuen Design soll der Fokus noch stärker auf die Inhalte gesetzt werden, Elemente die davon ablenken könnten, wurden soweit es ging reduziert.

Beitragsbild: rawpixel.com, Quelle: Pexel

„In eigener Sache: Neues Design und wie es weitergeht“ weiterlesen

Tipps für ein frustfrei(er)es Entwickeln von Gutenberg Custom Blocks

Zur Zeit arbeite ich viel mit dem neuen WordPress Gutenberg Editor. In diesem Artikel möchte ich einige Erfahrungen, die ich gesammelt habe, in Form von Tipps teilen. Mit der Zeit werden also wird die Liste also wahrscheinlich noch erweitert.

  • Stylesheet immer in eine CSS-Datei auslagern, um Konflikte beim Ändern zu vermeiden
  • Mit InspectorControls können Elemente für die Seitenleiste gesetzt werden (Inspector in der Dokumentation)
  • Eigenschaft parent ist sinnvoll für verschachtelte Blöcke (in der Dokumentation).

Beitragsbild: Negative Space, Quelle: Pexels