Hallo Habr! Ich mache Sie auf eine Übersetzung des Berichts von Alexander Kuzmenko von der jüngsten Open Source-Konferenz 2019 in Hongkong (14.-15. Juni) aufmerksam.

Bevor Alexander als Entwickler des Haxe-Compilers zur Haxe Foundation kam, war er ungefähr 10 Jahre lang professionell in der PHP-Programmierung tätig, damit er das Thema des Berichts kennt.

Eine kleine Einführung in Haxe ist eine plattformübergreifende Reihe von Tools zum Erstellen von Software, die Folgendes umfassen:
- Ein Compiler, der je nach Zielplattform entweder den Haxe-Quellcode in anderen Programmiersprachen (C ++, PHP, Java, C #, JavaScript, Python, Lua) in Quellcode übersetzt oder direkt in den Bytecode der virtuellen Maschine (JVM, neko) kompiliert , HashLink, Flash)
- Standardbibliothek für alle unterstützten Plattformen implementiert
- Haxe bietet auch Tools für die Interaktion mit Code, der in der Sprache der Zielplattform geschrieben wurde.
- Standardbibliotheksmanager - Haxelib

Die PHP-Unterstützung in Haxe erschien vor langer Zeit - bereits 2008. In Haxe-Versionen bis einschließlich 3.4.7 wurde PHP 5.4 und höher unterstützt, und ab der vierten Version unterstützt Haxe PHP-Version 7.0 und höher.

Sie fragen sich vielleicht: Warum sollte ein PHP-Entwickler Haxe verwenden?
Der Hauptgrund dafür ist die Möglichkeit, sowohl auf dem Server als auch auf dem Client dieselbe Logik zu verwenden.
Betrachten Sie dieses Beispiel: Sie haben einen Server, der mit dem Laravel-Framework in PHP geschrieben wurde, und mehrere Clients, die in JavaScript, Java, C # geschrieben sind. In diesem Fall müssen Sie 4 Implementierungen für jede der verwendeten Sprachen schreiben, um die Netzwerkinteraktion zwischen dem Server und dem Client zu verarbeiten (deren Logik im Grunde dieselbe ist). Mit Haxe können Sie den Netzwerkprotokollcode jedoch einmal schreiben und dann auf verschiedenen Plattformen kompilieren / übersetzen, was viel Zeit spart.

Hier ist ein weiteres Beispiel - eine Spieleanwendung - ein Zusammenprall von Clash of Clans. Alexander war an der Entwicklung eines solchen Spiels beteiligt: Sein Server wurde in PHP geschrieben, der mobile Client in C # (Xamarin) und der Browser-Client in JavaScript unter Verwendung des Phaser-Frameworks. Auf dem Client und dem Server wurde eine Logik verarbeitet - die sogenannte "Schlacht", die das Verhalten der Spielereinheiten am Standort berechnete. Anfangs wurde der Kampfcode für jede der Plattformen separat geschrieben. Im Laufe der Zeit (und das Projekt entwickelt sich seit ungefähr 5 Jahren) haben sich jedoch Unterschiede im Verhalten auf dem Server und auf den Clients angesammelt. Dies lag an der Tatsache, dass es von verschiedenen Personen geschrieben wurde, von denen jeder es auf seine eigene Weise realisierte. Aus diesem Grund gab es keine zuverlässige Möglichkeit, Betrüger zu erkennen, da sich die Logik auf dem Server überhaupt nicht wie auf dem Client verhielt, was dazu führte, dass auch ehrliche Spieler darunter litten. Der Server konnte sie als Betrüger zählen und nicht die Ergebnisse eines ehrlichen Kampfes.
Am Ende wurde beschlossen, die Schlacht auf Haxe zu übertragen, was es ermöglichte, das Problem mit Betrügern vollständig zu lösen, weil Jetzt verhielt sich die Logik des Kampfes auf allen Plattformen gleich. Darüber hinaus konnten durch diese Entscheidung die Kosten für die Weiterentwicklung des Kampfsystems gesenkt werden Jetzt war es genug für einen Programmierer, der mit Haxe vertraut war, anstatt für drei, von denen jeder für seine Plattform verantwortlich sein würde.

Wie unterscheiden sich Haxe und PHP voneinander? Im Allgemeinen scheint die Haxe-Syntax dem PHP-Programmierer nichts Fremdes zu sein. Ja, es ist anders, aber in den meisten Fällen wird es PHP sehr ähnlich sein.
Die Folie zeigt einen Vergleich des Codes zum Anzeigen einer Zeichenfolge in der Konsole. In Haxe sieht der Code fast gleich aus, außer dass Sie sie importieren müssen, um mit PHP-Funktionen zu arbeiten (siehe erste Zeile).

Und so sieht der generierte PHP-Code im Vergleich zum manuell geschriebenen aus.
Der Haxe-Compiler fügt dem Code automatisch einen Kommentarblock hinzu, der die Typen von Funktionsargumenten und Rückgabetypen beschreibt, sodass der resultierende Code mit einem PHP-Projekt verbunden werden kann und die automatische Vervollständigung problemlos funktioniert.
Schauen wir uns einige signifikante Unterschiede in der Syntax von Haxe und PHP an.

Beginnen wir mit den Unterschieden in der Syntax anonymer Funktionen (am Beispiel zum Sortieren eines Arrays).
Die Folie zeigt eine anonyme Funktion, die den Wert der lokalen Variablen desc
erfasst und ändert. In PHP müssen Sie explizit angeben, welche Variablen im Hauptteil einer anonymen Funktion verfügbar sind. Um den Wert einer Variablen ändern zu können, müssen Sie außerdem &
vor ihrem Namen hinzufügen.
In Haxe ist dies nicht mehr erforderlich. Der Compiler selbst bestimmt, auf welche Variablen Sie zugreifen. Darüber hinaus wurden in Haxe 4 Pfeilfunktionen (eine Kurzform zur Beschreibung anonymer Funktionen) angezeigt, mit denen wir unser Beispiel verkürzen können, indem wir ein Array auf nur eine Zeile sortieren.

Ein weiterer Unterschied in der Syntax sind Unterschiede in der Beschreibung des switch
Control-Konstrukts. In PHP funktioniert der switch
genauso wie in C. In Haxe funktioniert es anders:
- Erstens verwendet der Switch nicht das Schlüsselwort
break
- Zweitens können Sie mit
|
mehrere Bedingungen kombinieren (anstatt das case
Schlüsselwort zu duplizieren) - Drittens verwendet Haxe Pattern Matching für den
switch
. So können Sie beispielsweise den switch
auf das Array anwenden und unter den Bedingungen Aktionen definieren, die vom Inhalt des Arrays abhängen (das Zeichen _
bedeutet, dass dieser Wert uns nicht stört und alles sein kann). Die Bedingung [1, _, 3]
ist erfüllt, wenn das Array aus drei Elementen besteht, während das erste Element 1, das dritte 3 und der Wert des zweiten Elements beliebig ist.

In Haxe ist alles ein Ausdruck, und so können Sie kompakteren Code schreiben. Sie können einen Wert von try
/ catch
, if
oder switch
ohne das Schlüsselwort return
in diesen Konstrukten zu verwenden. Im obigen Beispiel mit try
/ catch
„weiß“ catch
Compiler, dass Sie einen Wert zurückgeben möchten, und kann ihn von diesem Konstrukt übergeben.

PHP bewegt sich allmählich in Richtung einer stärkeren Typisierung, aber Haxe hat bereits eine starke statische Typisierung!
Im obigen Beispiel weisen wir der Variablen s
Wert zu, der von der functionReturnsString()
, die eine Zeichenfolge zurückgibt. Somit ist der Typ der Variablen s
eine Zeichenfolge. Wenn Sie versuchen, es an die Funktion giveMeInteger()
, die eine Ganzzahl als Argument giveMeInteger()
, gibt der Haxe-Compiler einen Fehler bezüglich der Typinkongruenz aus.
Dieses Beispiel zeigt auch ein weiteres wichtiges Merkmal von Haxe - die Typinferenz - die Fähigkeit des Compilers, die Variablentypen unabhängig davon zu bestimmen, welchem Wert sie zugewiesen sind.

Für den Programmierer bedeutet dies, dass in den meisten Fällen die explizite Angabe der Variablentypen optional ist. In der obigen Funktion isSmall()
bestimmt der Compiler, dass der Argumenttyp a
eine Ganzzahl ist, weil In der ersten Zeile des Funktionskörpers vergleichen wir den Wert von a
mit einer ganzen Zahl. Ferner bestimmt der Compiler basierend auf der Tatsache, dass wir in der zweiten Zeile des Funktionskörpers true
, dass der Rückgabetyp ein boolescher Wert ist. Und weil Da der Compiler den Typ des Rückgabewerts bereits ermittelt hat, wird bei weiteren Versuchen, einen anderen Typ von der Funktion zurückzugeben, ein Typfehlanpassungsfehler ausgegeben.

In Haxe gibt es im Gegensatz zu PHP keine automatische Typkonvertierung. In PHP ist es beispielsweise möglich, eine Zeichenfolge von einer Funktion zurückzugeben, für die angegeben wird, dass sie eine Ganzzahl zurückgibt. In diesem Fall wird die Zeichenfolge bei Ausführung des Skripts in eine Zahl konvertiert (nicht immer erfolgreich). Und in Haxe wird ähnlicher Code einfach nicht kompiliert - der Compiler gibt einen Typ-Mismatch-Fehler aus.

Ein weiteres Merkmal von Haxe ist das fortschrittliche Typsystem, das Folgendes umfasst:
- Funktionstypen, bestehend aus Funktionsargumenttypen und Rückgabetyp
- verallgemeinerte (parametrisierte) Typen (dazu gehören beispielsweise Arrays, für die der Typ der gespeicherten Werte als Parameter verwendet wird)
- aufgezählte Typen
- verallgemeinerte algebraische Datentypen
- Mit Arten anonymer Strukturen können Sie Typen für Objekte deklarieren, ohne Klassen zu deklarieren
- abstrakte Datentypen (Abstraktionen über vorhandene Typen, jedoch ohne Leistungsverlust zur Laufzeit)
Alle diese Typen werden während der Kompilierung in PHP-Klassen konvertiert.

Eines der Hauptunterscheidungsmerkmale von Haxe ist die Metaprogrammierung (in Haxe heißt sie Makros), dh die Fähigkeit, den Quellcode eines Programms automatisch zu generieren.
Makros werden zum Zeitpunkt der Kompilierung des Programms ausgeführt und auf regulärem Haxe geschrieben.
Makros haben vollen Zugriff auf den abstrakten Syntaxbaum, dh sie können ihn lesen (die erforderlichen Ausdrücke darin durchsuchen) und ihn ändern.
Makros können Ausdrücke generieren, vorhandene Typen ändern und neue erstellen.

Im Kontext von PHP können Makros beispielsweise zum Routing verwendet werden. Angenommen, Sie haben eine einfache Router-Klasse, die eine Methode zum Anzeigen einer Seite anhand ihrer Kennung sowie eine Methode zum Abmelden implementiert. Mithilfe eines Makros können Sie Code für die route()
-Methode generieren, der ihn basierend auf der http-Anforderung an die entsprechende Methode der Router
Klasse umleitet. Es ist daher nicht erforderlich, ifs für Aufrufe an jede der Methoden dieser Klasse manuell zu schreiben (das Makro erledigt dies automatisch, wenn das Projekt in PHP kompiliert wird). Beachten Sie, dass der resultierende Code keine Reflektion verwendet, keine speziellen Konfigurationsdateien oder andere zusätzliche Tricks erfordert und daher sehr schnell funktioniert.

Ein weiteres Beispiel für die Verwendung von Makros ist die Codegenerierung für das Parsen und die JSON-Validierung. Beispielsweise haben Sie eine Datenklasse, deren Objekte basierend auf von JSON erhaltenen Daten erstellt werden sollen. Dies kann mithilfe eines Makros erfolgen. Da das Makro jedoch zusätzlich zum Parsen Zugriff auf die Struktur der Datenklasse hat, können Sie Code für die JSON-Validierung generieren, indem Sie einen Ausnahmeauswurf hinzufügen, wenn keine Felder vorhanden sind oder der Datentyp nicht übereinstimmt. So können Sie sicher sein, dass Ihre Anwendung keine falschen Daten übersieht, die von Benutzern oder von einem Server eines Drittanbieters empfangen wurden.
Erwähnenswert sind auch die wichtigen Merkmale der Implementierung einiger Datentypen für die PHP-Plattform, z Wenn Sie sie nicht berücksichtigen, können Sie unangenehme Folgen haben.

In PHP sind Zeichenfolgen binärsicher. Wenn Sie in PHP keine Unicode-Unterstützung benötigen, funktionieren die Methoden zum Arbeiten mit Zeichenfolgen sehr schnell.
In Haxe unterstützen Zeichenfolgen ab der vierten Version Unicode. Beim Kompilieren in PHP werden Methoden aus dem Modul verwendet, um mit mbstring-Multibyte-Zeichenfolgen zu arbeiten. Dies bedeutet langsamen Zugriff auf beliebige Zeichen in der Zeichenfolge und langsame Berechnung der Zeichenfolgenlänge.
Wenn Sie keine Unicode-Unterstützung benötigen, können Sie daher die php.NativeString
verwenden, um mit Zeichenfolgen zu arbeiten, die "native" Zeichenfolgen aus PHP verwenden.

Die Folie links zeigt PHP-Code, der beide Methoden verwendet, die Unicode unterstützen, und nicht.
Rechts ist der entsprechende Haxe-Code. Wie Sie sehen können, müssen Sie, wenn Sie Unicode-Unterstützung benötigen, die Methoden der String
Klasse verwenden. Wenn nicht, dann die Methoden der php.NativeString
Klasse.

Ein weiterer wichtiger Punkt ist die Arbeit mit Arrays.
In PHP werden Arrays als Wert übergeben, und Arrays unterstützen sowohl numerische als auch Zeichenfolgenschlüssel.
In Haxe werden Arrays als Referenz übergeben und unterstützen nur numerische Schlüssel (wenn Zeichenfolgenschlüssel benötigt werden, sollte in Haxe die Map
Klasse dafür verwendet werden). Außerdem sind "Löcher" in den Indizes in Haxe-Arrays nicht zulässig (Indizes müssen kontinuierlich sein).
Es ist auch erwähnenswert, dass das Schreiben in ein Array nach Index in Haxe ziemlich langsam ist.

Hier ist der Code zum Zuordnen des Arrays mithilfe der Pfeilfunktion. Wie Sie sehen können, optimiert der Haxe-Compiler den am Ausgang empfangenen PHP-Code ziemlich aktiv: Der resultierende Code enthält keine anonyme Funktion, stattdessen wird sein Code in einer Schleife auf jedes Element des Arrays angewendet. Neben map()
diese Optimierung auch für die filter()
-Methode.
Bei Bedarf können Sie auch die Klasse php.NativeArray
und die entsprechenden PHP-Methoden für die Arbeit mit Arrays in Haxe verwenden.

Anonyme Objekte werden mithilfe der HxAnon
Klasse implementiert, die von der StdClass
Klasse von PHP erbt.

In PHP ist StdClass
eine Klasse in Instanzen, von denen alles, was wir versuchen, in ein Objekt zu konvertieren, konvertiert wird. Und es wäre ideal für die Implementierung anonymer Objekte, wenn nicht für ein Merkmal ihrer Spezifikation: In Haxe sollte der Zugriff auf ein nicht vorhandenes Feld eines anonymen Objekts null
, und in PHP wird eine Warnung ausgegeben. Aus diesem Grund musste ich von der Standardklasse von PHP erben und eine magische Methode hinzufügen, die beim Zugriff auf nicht vorhandene Eigenschaften null
zurückgibt.

Haxe kann mit in PHP geschriebenem Code interagieren. Zu diesem Zweck gibt es die folgenden Funktionen (ähnlich den Möglichkeiten der Interaktion mit JavaScript-Code):
- externs
- PHP-Code direkt in Haxe-Code einbetten
- spezielle Klassen aus
php.*
Paket php.*
php.Syntax
- für spezielle PHP-Konstrukte, die nicht in Haxe enthalten sindphp.Global
- für native globale PHP-Funktionenphp.Const
- für native globale PHP-Konstantenphp.SuperGlobal
- für superglobale PHP-Variablen, auf die von überall aus $_POST
werden kann ( $_POST
, $_GET
, $_SERVER
usw.)

Weil PHP verwendet das klassische OOP-Modell, dann ist das Schreiben von externen Elementen ein ziemlich einfacher Prozess. Tatsächlich sind Externe für PHP-Klassen in Haxe fast wörtliche "Übersetzungen", mit Ausnahme einiger Schlüsselwörter.
Der externe Code für eine PHP-Klasse aus der obigen Folie sieht beispielsweise folgendermaßen aus:

Konstanten aus der PHP-Klasse "verwandeln" sich in statische Variablen im Haxe-Code (jedoch mit zusätzlichen Meta-Tags).
Die statische Variable $useBuiltinEncoderDecoder
wird zur statischen Variablen useBuiltinEncoderDecoder
.
Dies zeigt, dass Externe für PHP-Klassen automatisch erstellt werden können (Alexander plant, in diesem Jahr einen externen Generator zu implementieren).

Zum Einfügen von PHP-Code wird ein spezielles php.Syntax
Modul php.Syntax
. Auf diese Weise hinzugefügter Code unterliegt keiner Konvertierung oder Optimierung durch den Haxe-Compiler.
Zusätzlich zu php.Syntax
kann Haxe weiterhin untypisierten Code verwenden .

Erwähnenswert sind auch solche Funktionen von Haxe, die nicht in PHP enthalten sind:
- reale Eigenschaften mit Lese- und Schreibmethoden
- schreibgeschützte Felder und Variablen
- statische Erweiterungen
- Meta-Tags, mit denen Felder mit Anmerkungen versehen werden können und die in Makros lesbar sind. Daher werden Meta-Tags hauptsächlich für die Metaprogrammierung verwendet.
- Nullsicherheit (experimentelle Funktion)
- Bedingte Kompilierung, mit der Sie Teile des Codes aktivieren / deaktivieren können, die beispielsweise in der Debug-Version der Anwendung verfügbar sind
- Der Haxe-Compiler generiert hochoptimierten PHP-Code, der um ein Vielfaches schneller ausgeführt werden kann als manuell geschriebener PHP-Code.
Weitere Informationen zu Haxe und seinen Funktionen finden Sie in seinem offiziellen Handbuch .