Entwicklung einer RESTful PHP-API unter Ubuntu 20.04

Was ist eine API und wofür verwendet man sie?

Eine API (Application Programming Interface) fungiert als Vermittler zwischen Softwareanwendungen und ermöglicht die Kommunikation über das REST-Protokoll (Representational State Transfer). Ein typisches Beispiel: Eine mobile App greift über eine PHP-API auf eine MySQL-Datenbank in der Cloud zu.

Ein weiteres Einsatzgebiet ist das Bereitstellen von Schnittstellen für Dritte. Bekannte Unternehmen wie Google, Twitter und Facebook stellen APIs bereit, über die Nutzer direkt Daten anfragen oder übermitteln können – ganz ohne Benutzeroberfläche, sondern direkt über URL-Endpunkte.

Warum eine API für mehr Integration sorgt

Eine eigene API sorgt für mehr Flexibilität bei der Anbindung externer Anwendungen. Geben Sie die API-Antwort im JSON-Format zurück, können mobile Apps, Desktops, Tablets oder eingebettete Systeme sie direkt nutzen – ganz ohne Änderungen am Backend-Code.

In dieser Anleitung lernst du, wie du eine REST-API mit PHP unter Ubuntu 20.04 für einen fiktiven Online-Shop erstellst. Am Ende kannst du Produktdaten direkt aus der Datenbank abrufen, ohne auf ein klassisches Interface angewiesen zu sein.

Voraussetzungen

  • Ein Ubuntu 20.04 System
  • Ein Benutzerkonto mit sudo-Rechten
  • Ein installierter LAMP-Stack (MySQL oder MariaDB)

Beispieldatenbank einrichten

Verbinde dich per SSH mit deinem Server und starte die MySQL-Konsole:

Nachdem du dein Root-Passwort eingegeben hast und die mysql>-Eingabeaufforderung erscheint, erstelle mit folgendem Befehl die Datenbank store_api:

mysql> CREATE DATABASE store_api;

Erstelle jetzt einen eigenen MySQL-Nutzer, den deine API später für den Datenbankzugriff verwendet:

mysql> CREATE USER 'api_user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'EXAMPLE_PASSWORD';
mysql> GRANT ALL PRIVILEGES ON store_api.* TO 'api_user'@'localhost';
mysql> FLUSH PRIVILEGES;

Falls du MariaDB statt MySQL verwendest, nutze diesen Befehl für die Rechtevergabe:

MariaDB> GRANT ALL PRIVILEGES on store_api.* TO 'api_user'@'localhost' identified by 'EXAMPLE_PASSWORD';

Wechsle danach in die neue Datenbank:

Produkte-Tabelle definieren

Erstelle nun eine Tabelle namens products, in der die Artikel deines Onlineshops gespeichert werden. Damit kannst du später die Produktinformationen per API abrufen – ganz ohne grafische Oberfläche.

mysql> CREATE TABLE products (
           product_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
           product_name  VARCHAR(50),
           cost_price  DOUBLE,
           retail_price DOUBLE
           ) ENGINE = InnoDB;

Füge jetzt einige Beispielprodukte in die Tabelle ein, um ein realistisches Inventar zu simulieren:

mysql> INSERT INTO products (product_name, cost_price, retail_price) VALUES ('LEATHER JACKET', '89.23', '99.95');
mysql> INSERT INTO products (product_name, cost_price, retail_price) VALUES ('SILVER COAT', '44.00', '60.00');
mysql> INSERT INTO products (product_name, cost_price, retail_price) VALUES ('REXI BELT', '14.49', '18.85');
mysql> INSERT INTO products (product_name, cost_price, retail_price) VALUES ('SUEDE SHOE', '24.00', '36.00');
mysql> INSERT INTO products (product_name, cost_price, retail_price) VALUES ('WOOLEN SWEATER', '14.45', '18.00');

Führe eine SELECT-Abfrage aus, um zu überprüfen, ob die Einträge korrekt gespeichert wurden:

mysql> SELECT
           product_id,
           product_name,
           cost_price,
           retail_price
           FROM products;

Die Ausgabe sollte wie folgt aussehen:

+------------+----------------+------------+--------------+
| product_id | product_name   | cost_price | retail_price |
+------------+----------------+------------+--------------+
|          1 | LEATHER JACKET |      89.23 |        99.95 |
|          2 | SILVER COAT    |         44 |           60 |
|          3 | REXI BELT      |      14.49 |        18.85 |
|          4 | SUEDE SHOE     |         24 |           36 |
|          5 | WOOLEN SWEATER |      14.45 |           18 |
+------------+----------------+------------+--------------+
5 rows in set (0.00 sec)

Beende die MySQL-Sitzung mit folgendem Befehl:

Apache ModRewrite zur API-Weiterleitung einrichten

Damit deine API-Endpunkte wie gewünscht funktionieren, musst du das URL-Rewriting in Apache aktivieren. Starte mit dem Laden des Rewrite-Moduls:

Bearbeite nun die zentrale Apache-Konfigurationsdatei:

$ sudo nano /etc/apache2/apache2.conf

Suche in der Datei nach folgendem Abschnitt und passe die Einstellung AllowOverride wie folgt an:

...
<Directory /var/www/>
        Options Indexes FollowSymLinks
        AllowOverride None
        Require all granted
</Directory>
...

Ändere AllowOverride None zu AllowOverride All, damit Apache deine .htaccess-Regeln berücksichtigt:

...
<Directory /var/www/>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
</Directory>
...

Speichere die Änderung und starte Apache neu:

$ sudo systemctl restart apache2

.htaccess-Datei für URL-Umschreibungen erstellen

Erstelle nun einen Basisordner für deine API. In diesem Beispiel beginnt die Versionierung mit „v1“:

$ sudo mkdir -p /var/www/html/api/v1

Lege in diesem Verzeichnis eine neue .htaccess-Datei an:

$ sudo nano /var/www/html/api/v1/.htaccess

Füge diesen Inhalt in die Datei ein:

RewriteEngine On
RewriteBase /api/v1

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule (.*)$ index.php?request=$1 [QSA,NC,L]

Was bedeuten die .htaccess-Anweisungen?

  • RewriteEngine On: Aktiviert die Umschreibefunktion von Apache.
  • RewriteBase /api/v1: Legt den Basispfad für die Rewrite-Regeln fest.
  • RewriteCond %{REQUEST_FILENAME} !-f: Regel greift nur, wenn die angeforderte Datei nicht existiert.
  • RewriteCond %{REQUEST_FILENAME} !-d: Regel greift nur, wenn kein Ordner mit dem Namen vorhanden ist.
  • RewriteRule …: Leitet alle Anfragen intern an index.php weiter und übergibt den Pfad als Parameter.

Beispiel: Ein Aufruf wie http://localhost/api/v1/products/1 wird automatisch zu http://localhost/api/v1/index.php?request=products/1 umgeschrieben.

Die zentrale index.php-Routingdatei erstellen

Als Nächstes legst du die Datei index.php an, die alle eingehenden API-Anfragen verarbeitet. Sie übernimmt die Routingfunktion und leitet basierend auf URL und HTTP-Methode zur passenden Verarbeitung weiter.

Erstelle und öffne die Datei mit folgendem Befehl:

$ sudo nano /var/www/html/api/v1/index.php

Füge folgenden PHP-Code ein:

<?php

header("Content-type:application/json");

function load_class($class) {
    require_once $class . '.php';
}

spl_autoload_register('load_class');

$http_verb = $_SERVER['REQUEST_METHOD'];

if ($_SERVER['REQUEST_METHOD'] == 'GET') {
    foreach($_GET as $key => $value) {
        $params[$key] = $value;
    }
}

$request  = explode('/', $_REQUEST['request']);
$resource = $request[0];

if (isset($request[1])) {
    $resource_id = $request[1];
} else {
    $resource_id = '';
}

if ($resource == 'products') {
    $request = new Products;
}

if ($http_verb == 'GET') {

    if (!empty($resource_id)) {
        $response = $request->read($resource_id);
    } else {
        $response = $request->read($resource_id, $params);
    }

    echo $response;
}

Funktionsweise der index.php-Datei

  • header(“Content-type:application/json”): Setzt den Content-Type-Header, damit Clients die Antwort als JSON interpretieren.
  • load_class-Funktion: Diese Funktion lädt benötigte PHP-Klassen automatisch, was manuelles require vermeidet.
  • spl_autoload_register: Registriert die Autoload-Funktion für dynamisches Nachladen von Klassendateien.
  • GET-Verarbeitung: Bei GET-Anfragen werden die übergebenen Parameter in einem Array $params gespeichert.
  • Request-Auflösung: Die URL wird in Ressourcen und IDs zerlegt. products wird beispielsweise als Ressourcentyp erkannt.
  • Ressourcenzuweisung: Wird „products“ erkannt, wird eine Instanz der Klasse Products erstellt.
  • Ausführung von GET-Requests: Falls eine ID vorhanden ist, wird nur ein Produkt abgerufen. Ohne ID erfolgt eine gefilterte Abfrage über GET-Parameter.

Beispiel: http://localhost/api/v1/products/1 ruft das Produkt mit ID 1 ab. Ein Aufruf wie http://localhost/api/v1/products?type=shoes ermöglicht eine Filterung über Parameter.

Die Products-Klasse zur Datenverarbeitung erstellen

Für Anfragen an /products oder /products/1 wird eine Klasse Products benötigt. Diese übernimmt das Lesen der Datenbankeinträge.

Lege die Datei mit folgendem Befehl an:

$ sudo nano /var/www/html/api/v1/Products.php

Füge nun den folgenden PHP-Code für die Klasse ein:

<?php

class Products
{
    public function read($resource_id, $params = '')
    {
        try {

            $db_name     = 'store_api';
            $db_user     = 'api_user';
            $db_password = 'EXAMPLE_PASSWORD';
            $db_host     = 'localhost';

            $pdo = new PDO('mysql:host=' . $db_host . '; dbname=' . $db_name, $db_user, $db_password);
            $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

            $data = [];

            $sql  = "select * from products";

            if (!empty($resource_id)) {
                $sql .= " where product_id = :product_id";
                $data['product_id'] = $resource_id;
            } else {
                $filter = '';

                if (isset($params['product_name'])) {
                    $filter .= " and product_name = :product_name";
                    $data['product_name'] = $params['product_name'];
                }

                $sql .= " where product_id > 0 $filter";
            }

            $stmt = $pdo->prepare($sql);
            $stmt->execute($data);

            $products = [];

            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
                $products[] = $row;
            }

            $response = [];
            $response['data'] = $products;

            if (!empty($resource_id)) {
               $response['data'] = array_shift($response['data']);
            }

           return json_encode($response, JSON_PRETTY_PRINT);

        } catch (PDOException $ex) {
            $error = [];
            $error['message'] = $ex->getMessage();

            return $error;
        }
    }
}

Aufbau und Funktion der Products-Klasse

  • Klassenstruktur: Die Products-Klasse wird über die index.php aufgerufen, sobald der Endpunkt /products angesprochen wird.
  • read()-Methode: Die zentrale Methode read übernimmt das Verarbeiten eingehender GET-Anfragen. Sie akzeptiert optional eine ID und zusätzliche Parameter.
  • Datenbankverbindung: Der Zugriff erfolgt sicher über PDO mit den zuvor eingerichteten Zugangsdaten.
  • Abfragelogik: Gibst du eine bestimmte Produkt-ID an, fragt die API nur dieses eine Produkt ab. Andernfalls kann mit Parametern wie product_name gefiltert werden.
  • Sichere Ausführung: Benannte Parameter im SQL verhindern SQL-Injection. Die Abfrage wird vorbereitet und mit execute() ausgeführt.
  • JSON-Ausgabe: Die Ergebnisse werden als formatierter JSON-String ausgegeben. Bei Einzeltreffern wird statt eines Arrays direkt das Objekt zurückgegeben.
  • Fehlerbehandlung: Tritt ein Fehler auf, wird die Fehlermeldung im JSON-Format zurückgeliefert.

Die PHP-API mit curl testen

Wenn alle Dateien korrekt eingerichtet sind, kannst du die API mit curl testen.

Alle Produkte abrufen

$ curl localhost/api/v1/products

Beispielausgabe:

{
    "data": [
        {
            "product_id": 1,
            "product_name": "LEATHER JACKET",
            "cost_price": 89.23,
            "retail_price": 99.95
        },
        {
            "product_id": 2,
            "product_name": "SILVER COAT",
            "cost_price": 44,
            "retail_price": 60
        },
        {
            "product_id": 3,
            "product_name": "REXI BELT",
            "cost_price": 14.49,
            "retail_price": 18.85
        },
        {
            "product_id": 4,
            "product_name": "SUEDE SHOE",
            "cost_price": 24,
            "retail_price": 36
        },
        {
            "product_id": 5,
            "product_name": "WOOLEN SWEATER",
            "cost_price": 14.45,
            "retail_price": 18
        }
    ]
}

Einzelnes Produkt über ID abrufen

Produkt mit ID 1:

$ curl localhost/api/v1/products/1

{
    "data": {
        "product_id": 1,
        "product_name": "LEATHER JACKET",
        "cost_price": 89.23,
        "retail_price": 99.95
    }
}

Produkt mit ID 2:

$ curl localhost/api/v1/products/2

{
    "data": {
        "product_id": 2,
        "product_name": "SILVER COAT",
        "cost_price": 44,
        "retail_price": 60
    }
}

Nach Produktname filtern

Beispiel: Nur den Artikel „SUEDE SHOE“ anzeigen lassen:

$ curl localhost/api/v1/products?product_name=SUEDE%20SHOE

{
    "data": [
        {
            "product_id": 4,
            "product_name": "SUEDE SHOE",
            "cost_price": 24,
            "retail_price": 36
        }
    ]
}

Fazit

Du hast nun erfolgreich eine REST-API mit PHP und MySQL unter Ubuntu 20.04 umgesetzt. Die API liefert Daten im JSON-Format zurück und unterstützt aktuell GET-Anfragen. Bei Bedarf lässt sich die Funktionalität auf weitere HTTP-Methoden wie POST, PUT oder DELETE erweitern, um vollständige CRUD-Funktionalität zu ermöglichen.

Quelle: vultr.com

Jetzt 200€ Guthaben sichern

Registrieren Sie sich jetzt in unserer ccloud³ und erhalten Sie 200€ Startguthaben für Ihr Projekt.

Das könnte Sie auch interessieren: