Kleines Composer-Paket, das PHP-Regeln für Sie bricht


Ton zu laut? Lassen Sie es uns erklären, um enttäuschende Erwartungen zu vermeiden: Dieses Paket nutzt etwas Magie außerhalb von Hogwarts und wird für Fans strenger Typen in PHP sehr nützlich sein.

1. Einführung

Hallo zusammen! Wir sind WPLake, eine WordPress-Entwicklungsagentur.

Da das neue Jahr vor der Tür steht, scheint es, als würden sich alle einen Moment Zeit nehmen, um über das zu Ende gehende Jahr nachzudenken. Gibt es einen besseren Zeitpunkt, um einige Lösungen für gemeinsame Herausforderungen vorzustellen, mit denen wir dieses Jahr konfrontiert waren und die wir unglaublich nützlich fanden?

In diesem Beitrag geht es um PHP Typed – ein Composer-Paket (wplake/typed), das wir diese Woche veröffentlicht haben. Lass uns eintauchen!

2. Herausforderungen bei der Handhabung loser Typen in PHP

PHP ist eine interpretierte Sprache ohne strikte Typisierung und soll das Leben von Entwicklern vereinfachen und die Entwicklung erleichtern. Ohne Kompilierung und integrierte Speicherbereinigung versprach es ein Paradies für Entwickler zu werden.

Aber wie bei allem hat die Medaille zwei Seiten. Es dauerte nicht lange, bis allen klar wurde, dass die lose Typisierung von PHP zwar die Entwicklung beschleunigte, den Code jedoch auch weniger sicher und schwieriger zu warten machte.

Zum Beispiel:

function getUserData($id) {}

Was ist hier los? Erwarten Sie die Funktion a int als $idoder vielleicht ein Zeichenfolge Zeichen? Was gibt es zurück? Ein Objekt? Ein Siedlung? Ohne einen Blick auf die Implementierung kann man es nicht genau wissen.

Aus diesem Grund wurde PHPDoc erstellt:

/**
 * @var int $id
 * @return array
 */
function getUserData($id) {}

Okay, das ist viel besser. PHPDoc ist zum De-facto-Industriestandard für die Dokumentation von Code in kommerzieller Software geworden. Darüber hinaus hat die Sprache mit PHP 7.4 und höher Fortschritte in Richtung einer besseren Typunterstützung gemacht.

Wir haben jedoch immer noch viele Probleme mit der Eingabe. Schauen wir uns einige Beispiele für gängige, ausführliche Funktionen an, die beim Umgang mit untypisierten Variablen geschrieben wurden:

function getUserAge(array $userData): int
{
    return isset($userData['meta']['age']) &&
           is_numeric($userData['meta']['age'])
           ? (int) $userData['meta']['age']
           : 0;
}

function upgradeUserById($mixedUserId): void
{
    $userId = is_string($mixedUserId) || 
         is_numeric($mixedUserId)
        ? (string)$mixedUserId
        : '';
}

Obwohl diese Funktionen an sich nicht nützlich sind, zeigen sie die Ausführlichkeit und Unklarheit, die häufig bei der Arbeit mit untypisierten Variablen auftreten.

„Warte mal. Warum nicht einfach einen Typ für deklarieren?“ $mixedUserId? Warum nicht PHPDoc-Kommentare hinzufügen, um die Schlüssel von zu beschreiben? $userData?”
Vielleicht denken Sie darüber nach, während Sie diese Beispiele lesen.

In einer idealen Welt, ja, würden wir a Zeichenfolge Tippen Sie auf $gemischte Benutzer-IDUnd $userData wäre entweder ein Klasseninstanz oder zumindest ein Siedlung mit den in PHPDoc beschriebenen Schlüsseln. Die Realität ist jedoch, dass Software nicht von einem einzelnen Entwickler geschrieben wird und nicht nur aus 100 Zeilen Code besteht. Es sind Komponenten von Drittanbietern, externe Bibliotheken, Anbieter und große Legacy-Codebasen zu berücksichtigen.

Wenn es um Siedlungen geht, haben wir das starke Gefühl, dass dieses Problem auch in den kommenden Jahren bestehen bleiben wird. Es ist fast unmöglich, eine feste Struktur für Arrays zu garantieren – denken Sie nur $_SERVERwobei die Werte je nach Umgebung variieren können, ganz zu schweigen von benutzergesteuerten Eingaben wie z $_GET Und $_POST.

Okay, genug von den Problemen – reden wir über Lösungen.

3. Standardansätze für den Schriftguss

Wie können wir das Problem der Zeckenaussaat angehen? Es gibt drei gängige Out-of-the-Box-Ansätze:

3.1) Verwenden Sie „isset“ und Typprüfung

Wie wir oben gesehen haben:

$age = isset($userData['meta']['age']) &&
             is_numeric($userData['meta']['age'])
           ? (int) $userData['meta']['age']
           : 0;

Es ist selbst für eine so einfache Aufgabe ausführlich und unklar.

3.2) Verwenden Sie den Zero-Merging-Operator und die Typprüfung

$number = $data['meta']['age'] ?? 10;
$number = is_numeric($number) ? (int) $number : 10;

Was passiert hier? Am Ende haben wir zwei Codezeilen, da der Null-Merge-Operator die Typprüfung nicht berücksichtigt. Wenn Sie außerdem einen benutzerdefinierten Standardwert mit dem Nullverkettungsoperator verwenden, wiederholen Sie sich am Ende.

3.3) Verwendung des Zero-Coalescing-Operators und des Straight-Type-Castings

$number = (int) ($data['meta']['age'] ?? 10);

Das ist es! Kurz und bündig! Einige von Ihnen denken vielleicht: „Dieser Beitrag verschwendet nur meine Zeit.“

Dann unsere Antwort: Warten Sie ein wenig. Schauen wir uns genauer an, was hier passiert. Diese Zeile besteht aus zwei Teilen:

Teil 1: $Daten[‘meta’][‘age’] ?? 10

Wir prüfen sicher, ob die Variable vorhanden ist, und stellen andernfalls einen Standardwert bereit. Cool, lass uns weitermachen.

Teil 2: (int) {resultFromNullCoalescing}

Hier weisen wir PHP an, das Array-Element (oder unseren Standardwert) in eine Ganzzahl umzuwandeln. Es scheint einfach, aber denken wir über die Art der Variablen nach, die wir an einen int übergeben.

Ups, aber wir kennen den Typ eigentlich nicht.

Was? Ja, während Sie diesen Code schreiben, werden Sie wahrscheinlich „erwarten„dass die ‘[meta][age]’ item ist entweder eine Zeichenfolge oder zumindest eine numerische Zeichenfolge. Aber können Sie das garantieren? In der realen Welt liegt die Antwort NEIN. Große Anwendungen verfügen über unzählige logische Zweige, und einer davon kann diesen Wert beeinflussen.

Und selbst wenn es jetzt eine Ganzzahl ist, wer weiß, was es morgen sein wird? Die App erhält ein Update und jetzt wird das „Alter‘ Feld ist ein Objekt mit verschiedenen Eigenschaften. Boom! Diese Zeile erzeugt einen schwerwiegenden Fehler, da PHP ein Objekt nicht in einen String konvertieren kann, es sei denn, es wird explizit implementiert __toString.

Ein anderer Fall: Array-zu-String-Konvertierung

$string = (string) ($array['meta']['location'] ?? 'Default one');

Wenn die ‘Standort‘ wurde ein Siedlungstatt des erwarteten ZeichenfolgeDas Skript wird:

  1. Produzieren Sie ein beachten
  2. Setzen Sie die Ausführung mit der Zeichenfolge fort, die „Array“-Literale enthält

Obwohl eindeutig falsche Daten verwendet werden, würde die richtige Logik in diesem Fall den Fallback „Standard eins“ verwenden.

„Es ist nicht meine Schuld. Lass es einen fatalen Fehler sein, lass es eine fehlerhafte Logik sein.“
(Ein unvorsichtiger Entwickler könnte sagen)

Aber sollte das tatsächlich ein fataler Fehler sein? Wenn es sich nur um einen kleinen Zweig handelt, der unbedeutende Tags auf dem Bildschirm anzeigt, sollte dies dann wirklich dazu führen, dass die gesamte Anwendung in der Produktion fehlschlägt?

Wenn Sie mit externen Datenanbietern zusammengearbeitet haben, wissen Sie, wie unvorhersehbar deren Aktualisierungen sein können.

Ein guter Programmierer ist nicht gleichgültig. Zumindest nicht in den Augen ihres Chefs und in der besseren Welt der Entwicklungspraktiken. Seien wir nicht die Bösen.

Obwohl die ersten beiden Ansätze sicher sind, sind sie umständlich und ausführlich. Lassen Sie uns nun endlich untersuchen, was das Typed-Paket zu bieten hat, um bei dieser Aufgabe zu helfen.

4. PHP Typed: Dienstprogramm zur nahtlosen Typumwandlung

Wie können wir es also verbessern? Basierend auf unserer Recherche gibt es kein bekanntes Paket, das genau diese Aufgabe löst. Es gibt jedoch mehrere Array-Wrapper, die Array-bezogene Aufgaben vereinfachen, beispielsweise Laravel Collections.

Das Einpacken von Elementen in Arrays hört sich gut an und funktioniert gut innerhalb des Framework-Ökosystems, aber darüber hinaus können wir nicht erwarten, dass jeder dieses Paket verwendet. Die Verwendung eines gesamten Wrappers zum Extrahieren einer einzelnen Variablen ist offensichtlich übertrieben.

Wir brauchen also eine reine Lösung. Die gute Nachricht ist, dass die Forschung zu einer wichtigen Entdeckung geführt hat: einem eleganten Ansatz für den Umgang mit inneren Schlüsseln.

Da wir hauptsächlich mit Arrays arbeiten und die Typumwandlung verbessern, wäre es großartig, Konstruktionen wie diese zu haben $Daten[‘meta’][‘location’][‘city’] mit etwas Saubererem, wie meta.location.cityÄhnlich wie Laravel’s Arr::get funktioniert.

Lassen Sie uns nun die Logik für die Hilfsfunktion formulieren:

„Geben Sie mir den Wert des angeforderten Typs von der angegebenen Ressource über den angegebenen Pfad oder geben Sie den Standardwert zurück.“

Sortiert. Lassen Sie uns nun darüber sprechen, wie es aussehen soll. Es sollte einfach, intuitiv und klar sein – auch für diejenigen, die mit unserem Dienstprogramm nicht vertraut sind.

Die ursprüngliche Konstruktion ist zwar unsicher, aber recht ausdrucksstark:

$number = (int)($data['meta']['age'] ?? 10);

Können wir uns etwas Ähnliches einfallen lassen? Nach langem Nachdenken, vielen Prüfungen und der Erstellung des Pakets – ja! Hier ist das Ergebnis:

$number = int($data, 'meta.age', 10);

Sieht fast gleich aus, oder? Aber warte – was ist das?“int” darum herum gewickelt? Ist das nicht ein Schlüsselwort eines reservierten Typs in PHP? Hier ist die Entdeckung, das „Regelverbrechen“, das im Titel des Beitrags erwähnt wird:

PHP erlaubt Funktionen, dieselben Namen wie Variablentypen zu verwenden.

Waren Sie sicher, dass es verboten war? Nicht ganz! Während bestimmte Namen für Klassen, Schnittstellen und Eigenschaften eingeschränkt sind, gilt dies für Funktionsnamen nicht:

„Diese Namen können nicht verwendet werden Klasse, Schnittstelle oder Eigenschaft” – PHP-Handbuch: Reservierte andere reservierte Wörter

Das bedeutet, dass wir Dinge haben können wie string($array, ‘key’)was sieht aus (string)$array[‘key’] ist aber sicherer und intelligenter – es verarbeitet sogar verschachtelte Schlüssel und Standardwerte.

Das Paket unterstützt sowohl PHP 7.4+ als auch 8.0+ und wird über Composer verteilt, sodass der Installationsprozess wie gewohnt abläuft:

composer require wplake/typed

// then in your app:
require __DIR__ . '/vendor/autoload.php';

Übrigens behindert der Import dieser Funktionen die native Typumwandlung in PHP nicht. Auch wenn es praktisch nutzlos ist, funktioniert Folgendes dennoch:

echo (string)string('hello');

Der Getippt Das Paket stellt eine Reihe von Hilfsfunktionen bereit WPLake/typisiert Namespace, sodass Sie sich keine Sorgen über mögliche globale Konflikte machen müssen. Der Code mit Eingaben sieht folgendermaßen aus:

use function WPLake\Typed\string;

$string = string($array, 'first.second', 'default value');

Natürlich verwendet Ihre IDE automatisch das verwenden Linie für Sie Für diejenigen, die Funktionen nicht ausstehen können, bietet das Paket auch statische Methoden:

use WPLake\Typed\Typed;

Typed::int($data, 'key');

Wie üblich gibt es einen Wermutstropfen: Im Gegensatz zu anderen Arten ist die Siedlung Das Schlüsselwort fällt in eine andere Kategorie, was seine Verwendung für Funktionsnamen verbietet. Deshalb haben wir in diesem speziellen Fall den Namen verwendet arr stattdessen.

5. PHP typisiert: Anwendungsbeispiele

Nehmen wir eine einzelne Funktion aus dem Typed-Paket und sehen uns die Anwendungsbeispiele dafür an. Lass es so sein Zeichenfolge. Hier ist die Funktionsdeklaration für string():

namespace WPLake\Typed;

/**
 * @param mixed $source
 * @param int|string|array<int,int|string>|null $keys
 */
function string($source, $keys = null, string $default = ''): string;

Verwenden Sie Szenarien

5.1) Extrahiert eine Zeichenfolge aus einer gemischten Variablen

Gibt standardmäßig eine leere Zeichenfolge zurück, wenn die Variable nicht in eine Zeichenfolge konvertiert werden kann:

$userName = string($unknownVar);
// you can customize the fallback:
$userName = string($unknownVar, null, 'custom fallback value');

5.2) Rufen Sie eine Zeichenfolge aus einem Array ab

Einschließlich verschachtelter Strukturen (mit Punktnotation oder als Array):

$userName = string($array, 'user.name');
// Alternatively:
$userName = string($array, ['user', 'name']);
// custom fallback:
$userName = string($array, 'user.name', 'Guest');

5.3) Zugriff auf eine Zeichenfolge von einem Objekt aus

Einschließlich verschachtelter Eigenschaften:

$userName = string($companyObject, 'user.name');
// Alternatively:
$userName = string($companyObject, ['user', 'name']);
// custom fallback:
$userName = string($companyObject, 'user.name', 'Guest');

5.4) Arbeiten Sie mit gemischten Strukturen

(z.B. Objekt->ArrayProperty[‘key’]-> eine andere Immobilie oder [‘key’ => $object]

$userName = string($companyObject, 'users.john.name');
// Alternatively:
$userName = string($companyObject, ['users', 'john', 'name']);
// custom fallback:
$userName = string($companyObject, 'users.john.name', 'Guest');

In allen Fällen ist der Fallback-Wert der „leere“ Wert für den spezifischen Typ (z. B 0, FALSCH, „“usw.), aber Sie können einen benutzerdefinierten Standardwert als drittes Argument übergeben:

$userName = string($companyObject, 'users.john.name', 'Guest');

Das Paket enthält Funktionen für folgende Typen:

  • Zeichenfolge
  • int
  • schweben
  • bool
  • Objekt
  • Datum Uhrzeit
  • arr (steht für Siedlungweil es ein Schlüsselwort ist)
  • beliebig (ermöglicht die Verwendung kurzer Punkttasten für Unbekannte)

Zusätzlich:

  • boolExtended (true,1,”1″, “on” als wahr behandelt, false,0,”0″, “off” wenn falsch)
  • stringExtended (Unterstützen Sie Objekte mit __toString)

Für optionale Fälle, in denen Sie die Logik nur anwenden müssen, wenn das Element vorhanden ist, verfügt jede Funktion über eine OderNull Variation (z stringOfNull, intOrNullund so weiter), was zurückkommt null wenn der Schlüssel nicht existiert.

6. Fazit

Wir haben das PHP-typisierte Paket bereits in einer unserer realen Anwendungen angewendet und festgestellt, dass es die Klarheit, Eleganz und Intuitivität unseres Codes erheblich verbessert hat. Wir hoffen, dass es für andere in der PHP-Community genauso nützlich sein wird.

Hinweis: Wenn Sie dieses Paket nützlich fanden, denken Sie bitte darüber nach, seinem GitHub-Repository einen Stern zu geben und es mit Ihren Kollegen zu teilen, um ihnen ebenfalls bei der Verbesserung ihres Codes zu helfen.

Vielen Dank, dass Sie sich die Zeit genommen haben, diesen Beitrag durchzulesen! Wir wünschen Ihnen ein wundervolles neues Jahr voller Erfolg und Wachstum bei all Ihren Programmierbemühungen.

PS: Wir haben noch weitere Neuigkeiten: Diese Woche haben wir ein weiteres Paket veröffentlicht, das für PHP-Entwickler wertvoll sein könnte. Wir werden nächste Woche einen Beitrag darüber veröffentlichen, aber wenn Sie es jetzt ausprobieren möchten: Das Paket ist bereits verfügbar und hier ist der Link, um es zu erkunden!



Cloud-Server

Leave a Reply

Your email address will not be published. Required fields are marked *