Spirale: Hochleistungs-PHP / Go-Framework



Hallo Habr. Mein Name ist Anton Titov, CTO von Spiral Scout. Heute möchte ich Ihnen von unserem PHP-Elefanten erzählen. Oder besser gesagt, über die zweite Version des Open-Source-Full-Stack-PHP / Go-Frameworks - Spiral .

Spiral ist ein Komponenten-Full-Stack-Framework, das von unserem Unternehmen seit mehr als elf Jahren entwickelt wurde und hundert echte Projekte bedient. Das Softwarepaket basiert auf vielen offenen und proprietären Bibliotheken, einschließlich RoadRunner und Cycle ORM .

Das Framework ist mit den meisten PSR-Empfehlungen kompatibel, unterstützt MVC und läuft 5-10 mal schneller als Laravel / Symfony.

Wenn Sie noch nie von Spiral gehört haben und sich fragen, was das PHP / Go-Framework ist und wohin die erste Version gegangen ist, dann sind Sie bei cat willkommen.

Über das Framework


Die Spiralentwicklung begann 2008/09 als tragbarer Kernel für freiberufliche Anwendungen. 2010 haben wir endlich ein Outsourcing-Unternehmen gegründet und sind seitdem damit beschäftigt, unseren Stack zu verbessern.

Infolgedessen wurde das Framework in eine Reihe unabhängiger Komponenten umgewandelt, die durch eine gemeinsame Integrationsschicht verbunden sind.

Die einzige öffentliche Ankündigung der ersten Version erfolgte 2017 auf Reddit und machte uns klar, dass Spiral die Konkurrenz zu diesem Zeitpunkt technisch nicht wirklich geschlagen hat. Wir haben das Feedback, die Erfahrungen mit komplexeren Projekten berücksichtigt und die zweite Version im Mai 2019 fertiggestellt.

Nach einem Jahr Dokumentation und Ausführung realer Projekte sind wir bereit, diese Entwicklung der Öffentlichkeit vorzulegen.

Der Hauptunterschied zwischen Spiral 2.0 von der vorherigen Generation des Frameworks und möglicherweise von allen anderen vorhandenen PHP-Frameworks ist der integrierte Anwendungsserver RoadRunner sowie die Anpassung der Architektur an ein langlebiges Ausführungsmodell (Daemon-Modus).

Hybrid Rantheim


Das Hauptkonzept des Frameworks ist die Symbiose zwischen dem in Golang geschriebenen Anwendungsserver und dem PHP-Kern. Der PHP-Anwendungscode wird nur einmal in den Speicher geladen. Auf diese Weise erhalten Sie erhebliche Ressourceneinsparungen, verlieren jedoch die Fähigkeit, WordPress auszuführen.



Mehr über das Hybridmodell können Sie hier und hier lesen .

Der Server ist für den gesamten Infrastrukturteil verantwortlich: HTTP / FastCGI, Kommunikation mit Warteschlangenbrokern, GRPC, WebSockets, Pub / Sub, Cache, Metriken usw.

Das Schreiben von Code für Spiral unterscheidet sich praktisch nicht von Code für andere Frameworks. Die SOLID-Prinzipien müssen jedoch verantwortungsbewusster sein. Wenn das vorherige Zwischenspeichern von Benutzerdaten in Singleton nur starke Schmerzen bei der Codeüberprüfung verursachte, funktioniert ein solcher Code jetzt einfach nicht mehr richtig.

Golang-Kenntnisse sind nicht erforderlich, um mit der Plattform zu arbeiten. Nachdem Sie jedoch eine zweite Sprache gelernt haben, können Sie nahezu nahtlose Integrationen in Golang-Bibliotheken erstellen. Betten Sie beispielsweise eine Volltextsuchmaschine ein oder schreiben Sie Ihren eigenen Kafka-Treiber.

Das Schießen in das Bein war etwas schwieriger, es wurde beschlossen, das System der Ereignisse und Haken aufzugeben und die Arbeit mit dem Aufrufstapel in expliziter Form zu priorisieren.

Das Framework bietet eine Reihe von Tools wie IoC-Closures, Middleware, Domain-Layer-Spoiler und unveränderliche Services.

$container->runScope(
    [UserContext::class => $user],
    function () use ($container) {
        dump($container->get(UserContext::class);
    }
);

Obwohl ein solcher Ansatz einen zusätzlichen Overhead mit sich bringt, deckt die Möglichkeit, den Kern des Programms, die Verbindungen zur Datenbank und die Sockets aus dem Speicher nicht zu entladen, diese Einschränkungen mehr als ab.

Performance


In der Full-Stack-Klasse von PHP-Frameworks konkurriert Spiral hauptsächlich mit Swoole-basierten Assemblys und mehreren Micro-Frameworks.



Vollständige Benchmarks finden Sie hier und hier .

Leistung ist für uns ein Nebeneffekt der gewählten Architektur. Wir sind sicher, dass bei richtiger Konfiguration und Ersetzen des PSR-7 durch eine leichtere Abstraktion die Produktivität um 50-80% gesteigert werden kann (dies wird am Beispiel des Ubiquity-Roadrunner bewiesen ).

Swoole. Swoole RoadRunner, PHP ++. , Go. , .

RoadRunner ( ), , Windows.

Spiral Swoole, !

PSR-*


Die meisten Spiral-Komponenten sind für Ihren Build optional. Der Unterschied zwischen Micro- und Full-Build besteht nur im Inhalt composer.json. Bei Bedarf können Sie die Schnittstellen verwenden, um Standardbibliotheken durch alternative Implementierungen zu ersetzen.

Die HTTP-Schicht des Frameworks wird unter Berücksichtigung der PSR-7/15/17-Standards geschrieben. Sie können den Router , die Implementierung von Nachrichten usw. sicher ändern .

Die meisten Framework-Bibliotheken können außerhalb des Frameworks verwendet werden. RoadRunner funktioniert beispielsweise hervorragend mit Symfony und Laravel , und Cycle ORM wird in Yii3 verfügbar sein.

Serverkomponenten


Zusätzlich zu den PHP-Komponenten enthält die RoadRunner-Assembly mehrere in Golang geschriebene Bibliotheken. Die meisten Serverdienste können in PHP verwaltet werden.

Insbesondere gibt es eine Warteschlangenkomponente, die die Arbeit mit den Brokern AMQP, Amazon SQS und Beanstalk unterstützt. Die Bibliothek kann eine beliebige Anzahl eingehender Warteschlangen korrekt stoppen, erneut verbinden und an mehrere Mitarbeiter verteilen.

Standardmäßig werden Prometheus- und Health-Check-Punkte überwacht, ein Hot-Neustart und ein Speicherlimit durchgeführt. Für verteilte Projekte gibt es einen GRPC-Server und -Client. Es gibt Web-Sockets, die von einer PHP-Anwendung autorisiert und mit dem Pub-Sub-Bus (im Speicher oder auf Redis) verbunden werden können. Key-Value-Treiber werden derzeit getestet.

INFO[0154] 10.42.5.55:51990 Ok {2.28ms} /images.Service/GetFiles
INFO[0155] 10.42.3.95:50926 Ok {11.3ms} /images.Service/GetFiles
INFO[0156] 10.42.5.55:52068 Ok {3.60ms} /images.Service/GetFiles
INFO[0158] 10.42.5.55:52612 Ok {2.30ms} /images.Service/GetFiles
INFO[0166] 10.42.5.55:52892 Ok {2.23ms} /images.Service/GetFiles
INFO[0167] 10.42.3.95:49938 Ok {2.37ms} /images.Service/GetFiles
INFO[0169] 10.42.5.55:52988 Ok {2.22ms} /images.Service/GetFiles




Portabilität


Das Framework benötigt kein PHP-FPM und NGINX. Alle Golang-Komponenten verfügen über Treiber für das Arbeiten ohne externe Abhängigkeiten. So können Sie Warteschlangen, Websockets und Metriken verwenden, ohne externe Broker oder Programme zu installieren.

./spiral serve -v -d

Spiral spielt keine Rolle, ob Sie eine große verteilte Anwendung oder eine kleine Site schreiben, die im Hintergrund "Schreiben an uns" sendet. In jedem Fall können Sie dieselben Tools verwenden, um das Verhalten lokaler und Produktionsumgebungen zu vereinheitlichen.

Da die HTTP-Schicht optional ist, können Sie Konsolenanwendungen schreiben, die Daten im Hintergrund mithilfe eines Warteschlangenpakets verarbeiten. Wir verwenden solche Programme für die Datenmigration.

Zyklus ORM


Als ORM wird Cycle ORM ausgeliefert . Diese Data Mapper-Engine ist in ihrer Funktion Doctrine sehr ähnlich, aber architektonisch sehr unterschiedlich.

Wie Doctrine kann Cycle mit reinen Domänenmodellen arbeiten, unabhängig Migrationen generieren und Fremdschlüssel anordnen. Das Zuordnungsschema kann durch Code beschrieben oder aus Anmerkungen gesammelt werden. Anstelle von DQL werden jedoch klassische Query Builder verwendet.

//    
//        
$users = $orm->getRepository(User::class)
    ->select()
    ->where('active', true)
    ->load('orders', [
        'method' => Select::SINGLE_QUERY, // force LEFT JOIN
        'load'   => function($query) {
            $query->where('paid', true)->orderBy('timeCreated', 'DESC');
        }
    ])
    ->fetchAll();

$transaction = new Transaction($orm);

foreach($users as $user) {
    $transaction->persist($user);
}

$transaction->run();

Der Zyklus läuft bei Auswahl schneller als Doctrine, bei Persistenz jedoch langsamer. Die Engine unterstützt komplexe Abfragen mit verschiedenen Ladestrategien, Proxy-Klassen und Einbettungen und bietet tragbare Transaktionen anstelle des globalen EntityManager.

Weitere Details finden Sie auf der PHP Russia 2020 .

Das Hauptmerkmal von ORM ist die Möglichkeit, die Zuordnung von Daten und Links zur Laufzeit zu ändern. Mit einfachen Worten, Sie können Benutzern erlauben, das Datenschema unabhängig zu definieren (DBAL unterstützt die Introspektion und Deklaration von Datenbankschemata).

Weitere Informationen zum Vergleichen von Zyklus, Eloquent und Lehre 2 finden Sie hier .

Rapid-Prototyping


Spiral enthält mehrere Tools, um die Entwicklung zu beschleunigen und zu vereinfachen. Die wichtigsten sind die automatische Abhängigkeitsinjektion, die automatische Konfiguration und die automatische Modellsuche mithilfe der statischen Analyse. Mit Konsolenbefehlen können Sie die meisten erforderlichen Klassen generieren und sind leicht anpassbar.

Für die Integration in die IDE gibt es ein Rapid Prototyping System. Mit dem magischen PrototypeTrait erhalten Sie schnellen Zugriff auf Tipps in der IDE.



Das Merkmal findet automatisch ORM-Repositorys, Standardkomponenten und kann Ihre Services durch Anmerkungen indizieren. Unter der Haube wird die magische __get- Methode verwendet , die Ihnen eine schnelle Codeüberprüfung garantiert.

Führen Sie einfach den Befehl `php app.php prototype: injizieren -r` aus, und das Prototyping-System entfernt automatisch alle Magie:

namespace App\Controller;

use App\Database\Repository\UserRepository;
use Spiral\Views\ViewsInterface;

class HomeController
{
    /** @var ViewsInterface */
    private $views;

    /** @var UserRepository */
    private $users;

    /**
     * @param ViewsInterface $views
     * @param UserRepository $users
     */
    public function __construct(ViewsInterface $views, UserRepository $users)
    {
        $this->users = $users;
        $this->views = $views;
    }

    public function index()
    {
        return $this->views->render('profile', [
            'user' => $this->users->findByName('Antony')
        ]);
    }
}

Unter der Haube wird PHP-Parser verwendet .

Sicherheit


Da die meisten unserer Anwendungen für das B2B-Segment entwickelt wurden, müssen Sicherheitsaspekte ernst genommen werden.

Komponenten zur Validierung komplexer Anforderungen (Anforderungsfilter), CSRF und Verschlüsselung (basierend auf Defuse / PHP-Verschlüsselung ) stehen Ihnen zur Verfügung . Die Arbeit mit Cookies und Sitzungen unterstützt die Verhinderung von Manipulationen und das Signieren von Daten auf der Serverseite.

Das Framework bietet eine auslaufende tokenbasierte Authentifizierungskomponente . Als Treiber können Sie Sitzungen, eine Datenbank oder sogar reine JWT verwenden.

Oder alle Arten von Token gleichzeitig, wenn die Anforderung „SAML / SSO / 2FA dringend verbinden!“ Plötzlich zu Ihnen fliegt :(

Die Zugriffsautorisierung erfolgt über die RBAC-Komponente mit einigen Verbesserungen, die den Betrieb im DAC- und ABAC-Modus ermöglichen. Es gibt Unterstützung für viele Rollen, Anmerkungen zum Schutz von Controller-Methoden und ein Regelsystem.

Die Arbeit mit dem Domänenschicht erfolgt durch eine Zwischen Interceptor Schicht . So können Sie spezielle Einschränkungen für eine Gruppe von Controllern erstellen, Daten vorab validieren und Fehler umbrechen.

Template Engine


Wenn Sie nur Twig mögen, können Sie durch diesen Abschnitt scrollen, einfach die Erweiterung installieren und vertraute Tools verwenden. :) :)

Im Lieferumfang ist die Stemper-Vorlagen-Engine bzw. eine Bibliothek zum Erstellen eines eigenen DSL-Markups enthalten. Insbesondere gibt es einen vollwertigen Lexer, mehrere Grammatiken, einen Parser und Zugriff auf AST (ähnlich wie bei Nikitas PHP-Parser).

Es ist möglich, mehrere verschachtelte Grammatiken zu analysieren. So können Sie beispielsweise Laravel Blade-Direktiven und Ihr eigenes DSL-Markup (in Form von HTML-Tags) in derselben Vorlage verwenden. Es stellt sich so etwas wie Webkomponenten auf der Serverseite heraus.

Wir verwenden diese Komponente, um komplexe Schnittstellen mit einfachen Grundelementen und Regeln zu beschreiben.

<extends:admin.layout.tabs title="User Information"/>
<use:bundle path="admin/bundle"/>

<ui:tab id="info" title="Information">
  User, {{ $user->name }}
</ui:tab>

<ui:tab id="data" title="User Settings">
  <grid:table for={{ $user->settings }}>
    <grid:cell title="Key">{{ $key }}</grid:cell>
    <grid:cell title="Value">{{ $value }}</grid:cell>
  </grid:table>
</ui:tab>

Es unterstützt die automatische Escape-Funktion mit Kontextunterstützung (z. B. konvertiert die Ausgabe von PHP im JS-Block automatisch Daten in JSON), Quellzuordnungen für die Arbeit mit Fehlern. Vorlagen werden in optimierten PHP-Code kompiliert und dann direkt aus dem Anwendungsspeicher bereitgestellt.
Stemper kann vollständig mit dem DOM des Dokuments arbeiten (wenn auch langsamer, wenn Sie spezielle Tools verwenden).

Rahmenentwicklung


Natürlich haben wir in mehr als einem Jahrzehnt der Entwicklung dieses Tools viele Fehler gemacht. Einige von ihnen wurden für die Veröffentlichung der zweiten Version korrigiert, während der andere Teil eine Überarbeitung des Konzepts einiger Komponenten erfordert.

Wir werden auch nicht leugnen, dass einige Dinge eine Datei erfordern. Und einige Fragen mögen für uns zum ersten Mal klingen. Obwohl die Dokumentation versucht, das Maximum an Komponenten abzudecken, kann sie an einigen Stellen immer noch durchhängen.

Wir sind offen für Kritik und Vorschläge, da wir dieses Tool selbst aktiv nutzen. Der enorme Rückstand an Verbesserungen wird langsam von unserem Team und der Community behoben.

Es ist viel Arbeit erforderlich, um den Zyklus zu verbessern und zu beschleunigen sowie Anforderungsfilter gemäß den neuesten RFCs neu zu schreiben. In der Entwicklung befindet sich eine Komponente zum Arbeiten und Emulieren von Schlüsselwertdatenbanken.

Wir haben es einfach nicht geschafft, viele Dinge aus der ersten Version zu übersetzen. Es ist geplant, ODM-Pakete, das Admin-Panel wiederherzustellen, einen guten Profiler zu schreiben usw.

Community und Links


Sie können sich bei unserer kleinen Discord- Community anmelden . Telegrammkanal .


Der gesamte Code wird unter der MIT-Lizenz vertrieben und unterliegt keinen Einschränkungen für die kommerzielle Nutzung.

Vielen Dank für Ihre Aufmerksamkeit. Ich hoffe, unsere Tools sind in Ihren Projekten hilfreich!

All Articles