Strategie zum Entwickeln von Namensraum- und Nicht-Namensraum-Versionen desselben PHP-Codes

Lesezeit: 10 Minuten

Benutzer-Avatar
Kornel

Ich verwalte eine für PHP 5.2 geschriebene Bibliothek und möchte eine PHP 5.3-Namespace-Version davon erstellen. Ich würde jedoch auch die Version ohne Namespace auf dem neuesten Stand halten, bis PHP 5.3 so alt ist, dass sogar Debian Stable es ausliefert 😉

Ich habe ziemlich sauberen Code, ungefähr 80 Klassen folgen Project_Directory_Filename Namensschema (ich würde sie ändern in \Project\Directory\Filename natürlich) und nur wenige Funktionen und Konstanten (auch mit vorangestelltem Projektnamen).

Die Frage ist: Was ist der beste Weg, um Namespace- und Nicht-Namespace-Versionen parallel zu entwickeln?

  • Soll ich einfach einen Fork im Repository erstellen und Änderungen zwischen Branches zusammenführen? Gibt es Fälle, in denen mit Backslash gesprenkelter Code schwer zusammenzuführen ist?

  • Soll ich ein Skript schreiben, das die Version 5.2 in 5.3 konvertiert oder umgekehrt? Sollte ich den PHP-Tokenizer verwenden? sed? C-Präprozessor?

  • Gibt es eine bessere Möglichkeit, Namespaces zu verwenden, wo verfügbar, und die Abwärtskompatibilität mit älterem PHP aufrechtzuerhalten?


Aktualisieren: Habe mich doch gegen die Verwendung von Namespaces entschieden.

  • +1 Tolle Frage – darauf bin ich auch neugierig.

    – Peter Bailey

    2. Dezember 2009 um 22:46 Uhr

  • Warum willst du die doppelte Arbeit machen? Führen Sie dies in einem anderen Projekt durch, in dem Sie 5.3 als Mindestanforderung erzwingen können.

    – Byron Whitlock

    2. Dezember 2009 um 23:09 Uhr

  • @Byron Whitlock: Aber wie werden Sie Ihre Codebibliothek, die Sie für PHP 5.2 geschrieben haben, dann in diesem 5.3-Projekt verwenden? Umschreiben, umgestalten oder so lassen wie es ist? Wenn Ihre Antwort eine der ersten beiden ist, sehen Sie sich die ursprüngliche Frage an.

    – Merkator

    2. Dezember 2009 um 23:45 Uhr

  • @Byron Whitlock: Ich möchte keine 2×-Arbeit machen, weshalb ich frage! Ich möchte mir einen Wettbewerbsvorteil verschaffen, indem ich früh auf 5.3 springe, aber ich möchte bestehende Benutzer nicht im Stich lassen.

    – Körnel

    2. Dezember 2009 um 23:52 Uhr

  • Ich liebe Ihren Blogbeitrag zu Namespaces. Vielen Dank für die Verlinkung

    Benutzer2923827

    27. November 2013 um 14:59 Uhr

Benutzer-Avatar
Evert

Ich denke nicht, dass die Vorverarbeitung des 5.3-Codes eine großartige Idee ist. Wenn Ihr Code in PHP 5.2 und 5.3 funktional identisch ist, mit Ausnahme der Verwendung von Namespaces anstelle von durch Unterstriche getrennten Präfixen, warum sollten Sie dann überhaupt Namespaces verwenden? In diesem Fall klingt es für mich so, als ob Sie Namespaces verwenden möchten, um Namespaces zu verwenden.

Ich denke, Sie werden feststellen, dass Sie bei der Migration zu Namespaces anfangen werden, etwas anders über die Organisation Ihres Codes nachzudenken.

Aus diesem Grund stimme ich Ihrer ersten Lösung nachdrücklich zu. Erstellen Sie einen Fork und führen Sie Backports von Funktionen und Bugfixes durch.

Viel Glück!

Benutzer-Avatar
NikiC

Dies ist eine Fortsetzung meiner vorherigen Antwort:

Der Namespace-Simulationscode wurde ziemlich stabil. Ich kann Symfony2 bereits zum Laufen bringen (einige Probleme immer noch, aber im Grunde). Allerdings fehlen noch einige Dinge wie die Auflösung von Variablennamensräumen für alle Fälle, abgesehen von new $class.

Jetzt habe ich ein Skript geschrieben, das rekursiv durch ein Verzeichnis iteriert und alle Dateien verarbeitet: http://github.com/nikic/prephp/blob/master/prephp/namespacePortR.php


Gebrauchsanweisung

Anforderungen, damit Ihr Code funktioniert

Ihre Klassennamen dürfen nicht enthalten _ Charakter. Wenn dies der Fall ist, können Klassennamen beim Konvertieren mehrdeutig werden.

Ihr Code darf keine globalen Funktionen oder Konstanten innerhalb eines Namespaces neu deklarieren. Dadurch wird sichergestellt, dass Ihr gesamter Code zur Kompilierzeit aufgelöst werden kann.

Grundsätzlich sind dies die einzigen Einschränkungen für Ihren Code. Ich sollte jedoch beachten, dass der NamespacePortR in einer Standardkonfiguration Dinge wie nicht auflöst $className="Some\\NS\\Class"; new $className, da hierfür zusätzlicher Code eingefügt werden müsste. Es ist besser, dass dies später gepatcht wird (entweder manuell oder mithilfe eines automatisierten Patch-Systems).

Aufbau

Da wir davon ausgegangen sind, dass keine globale Funktion oder Konstante in einem Namensraum neu deklariert wird, müssen Sie die assumeGlobal Klassenkonstante in der Namespace-Listener. Legen Sie in derselben Datei die SEPARATOR konstant zu _.

Ändern Sie im NamespacePortR den Konfigurationsblock entsprechend Ihren Anforderungen.


PS: Das Skript kann bereitgestellt werden a ?skip=int Möglichkeit. Dies sagt ihm, den ersten zu überspringen int Dateien. Sie sollten es nicht benötigen, wenn Sie den Override-Modus auf intelligent eingestellt haben.

Benutzer-Avatar
Kornel

Hier ist, was ich gefunden habe:

Dies mit regulären Ausdrücken zu tun, ist ein Albtraum. Sie können das meiste mit nur ein paar einfachen Ausdrücken erledigen, aber dann sind Grenzfälle ein Killer. Am Ende habe ich ein schreckliches, zerbrechliches Durcheinander, das kaum mit einer Codebasis funktioniert.

Es ist mit einem eingebauten Tokenizer und einem einfachen rekursiven Abstiegsparser machbar, der nur eine vereinfachte Teilmenge der Sprache verarbeitet.

Am Ende hatte ich ein ziemlich hässliches Design (Parser und Transformer in einem – meistens nur das Ändern oder erneute Ausgeben von Token), weil es zu viel Arbeit schien, einen nützlichen Syntaxbaum mit beibehaltenen Leerzeichen zu erstellen (ich wollte, dass der resultierende Code für Menschen lesbar ist). ).

Ich wollte es versuchen phc dafür, konnte ihn aber nicht überzeugen configure dass ich die erforderliche Version der Boost-Bibliothek erstellt habe.

Ich habe ANTLR dafür noch nicht ausprobiert, aber es ist wahrscheinlich das beste Tool für diese Art von Aufgaben.

  • Abgesehen von sehr begrenzten Spezialfällen (z. B. Ihrer “vereinfachten Teilmenge”) sind reguläre Ausdrücke immer eine Katastrophe, wenn sie zur Ausführung massiver, zuverlässig Quellcodeänderungen, gerade weil sie die Sprachstruktur nicht erfassen können.

    – Ira Baxter

    7. September 2010 um 8:02 Uhr

  • Deshalb ist meine Antwort am besten.

    – Theodore R. Smith

    7. September 2010 um 20:52 Uhr

Benutzer-Avatar
NikiC

Ich arbeite an einem Projekt, das PHP 5.3 auf PHP 5.2 emuliert: prephp. Es enthält Namespace-Unterstützung (allerdings noch nicht vollständig).

Nun, aus der Erfahrung heraus, dies zu schreiben, gibt es ein Mehrdeutigkeitsproblem Namensraumauflösung: Unqualifizierte Funktionsaufrufe und konstante Lookups haben einen Fallback auf den globalen Namensraum. Sie könnten Ihren Code also nur dann automatisch konvertieren, wenn Sie alle Ihre Funktionsaufrufe/Konstantensuchen entweder vollständig qualifiziert oder qualifiziert haben oder wenn Sie keine Funktion oder Konstante in einem Namespace mit demselben Namen wie eine in PHP eingebaute Funktion neu definiert haben.

Wenn Sie sich strikt an diese Vorgehensweise halten (welche auch immer Sie wählen), wäre es ziemlich einfach, Ihren Code zu konvertieren. Es wäre eine Teilmenge des Codes zum Emulieren von Namespaces in Prephp. Falls ihr Hilfe bei der Umsetzung braucht, könnt ihr mich gerne fragen, ich wäre interessiert 😉

PS: Der Namespace-Emulationscode von prephp ist noch nicht vollständig und kann fehlerhaft sein. Aber es kann Ihnen einige Einblicke geben.

Hier ist die beste Antwort, die Sie meiner Meinung nach finden werden:

Schritt 1: Erstellen Sie ein Verzeichnis namens 5.3 für jedes Verzeichnis mit php5.3-Code darin und kleben Sie den gesamten 5.3-spezifischen Code hinein.

Schritt 2: Nehmen Sie eine Klasse, die Sie in einen Namensraum einfügen möchten, und tun Sie dies 5.3/WebPage/Consolidator.inc.php:

namespace WebPage;
require_once 'WebPageConsolidator.inc.php';

class Consolidator extends \WebpageConsolidator
{
    public function __constructor()
    {
        echo "PHP 5.3 constructor.\n";

        parent::__constructor();
    }
}

Schritt 3: Verwenden Sie eine Strategiefunktion, um den neuen PHP 5.3-Code zu verwenden. In nicht-PHP5.3 findclass.inc.php platzieren:

// Copyright 2010-08-10 Theodore R. Smith <phpexperts.pro>
// License: BSD License
function findProperClass($className)
{
    $namespaces = array('WebPage');

    $namespaceChar="";
    if (PHP_VERSION_ID >= 50300)
    {
        // Search with Namespaces
        foreach ($namespaces as $namespace)
        {
            $className = "$namespace\\$className";
            if (class_exists($className))
            {
                return $className;
            }
        }

        $namespaceChar = "\\";
    }

    // It wasn't found in the namespaces (or we're using 5.2), let's search global namespace:
    foreach ($namespaces as $namespace)
    {
        $className = "$namespaceChar$namespace$className";
        if (class_exists($className))
        {
            return $className;
        }
    }

    throw new RuntimeException("Could not load find a suitable class named $className.");
}

Schritt 4: Schreiben Sie Ihren Code so um, dass er so aussieht:

<?php
require 'findclass.inc.php';

$includePrefix = '';
if (PHP_VERSION_ID >= 50300)
{
        $includePrefix = '5.3/';
}

require_once $includePrefix . 'WebPageConsolidator.inc.php';

$className = findProperClass('Consolidator');
$consolidator = new $className;

// PHP 5.2 output: PHP 5.2 constructor.
// PHP 5.3 output: PHP 5.3 constructor. PHP 5.2 constructor.

Dass Wille Arbeite für dich. Es ist ein leistungsmäßiger Schlag, aber nur ein kleiner, und wird beseitigt, wenn Sie sich entscheiden, die Unterstützung von 5.3 einzustellen.

  • Meine Lösung funktioniert, ohne Tausende für ein Nicht-Commodity-Produkt ausgeben zu müssen, das niemand effektiv vorführen kann und das sich viele von uns nicht leisten können.

    – Theodore R. Smith

    9. September 2010 um 14:17 Uhr

  • Obwohl meine Lösung ungefähr so ​​gut funktioniert wie das “Nicht-Commodity-Produkt”, das dasselbe tut. Hören Sie also bitte auf, den Leuten zu sagen, dass Ihre Antwort die beste ist. Es ist eine gute Möglichkeit, aber es nervt mich, dass du herumrennst und allen sagst, wie gut es ist. Vielen Dank.

    – Nikic

    9. September 2010 um 14:52 Uhr

  • Es ist nicht relevant für mein Problem. Es zeigt nur, wie man PHP 5.3/5.2-Code lädt, nicht, wie man ihn erstellt/verwaltet. Ich verstehe auch nicht, warum ich dies explizit aufrufen sollte, anstatt den Autoloader einzurichten.

    – Körnel

    10. September 2010 um 10:56 Uhr


Benutzer-Avatar
jdd

Was ich getan habe, mit einer großen Codebasis, die (unter anderem) die Unterstrich-Namenskonvention verwendete, und require_once eine ganze Menge anstelle eines Autoloaders, war es, einen Autoloader zu definieren und hinzuzufügen class_alias Zeilen in den Dateien, die Aliase für den alten Namen einer Klasse definieren, nachdem ihre Namen geändert wurden, um mit Namespaces gut zu sein.

Ich habe dann angefangen zu entfernen require_once Anweisungen, bei denen die Ausführung nicht von der Einschlussreihenfolge abhängig war, da der Autoloader Sachen und Namespace-Sachen aufsammelte, während ich Fehler behob und so weiter.

Es hat bisher ganz gut funktioniert.

  • Meine Lösung funktioniert, ohne Tausende für ein Nicht-Commodity-Produkt ausgeben zu müssen, das niemand effektiv vorführen kann und das sich viele von uns nicht leisten können.

    – Theodore R. Smith

    9. September 2010 um 14:17 Uhr

  • Obwohl meine Lösung ungefähr so ​​gut funktioniert wie das “Nicht-Commodity-Produkt”, das dasselbe tut. Hören Sie also bitte auf, den Leuten zu sagen, dass Ihre Antwort die beste ist. Es ist eine gute Möglichkeit, aber es nervt mich, dass du herumrennst und allen sagst, wie gut es ist. Vielen Dank.

    – Nikic

    9. September 2010 um 14:52 Uhr

  • Es ist nicht relevant für mein Problem. Es zeigt nur, wie man PHP 5.3/5.2-Code lädt, nicht, wie man ihn erstellt/verwaltet. Ich verstehe auch nicht, warum ich dies explizit aufrufen sollte, anstatt den Autoloader einzurichten.

    – Körnel

    10. September 2010 um 10:56 Uhr


Benutzer-Avatar
Kitson

Nun, ich weiß nicht, ob es der “beste” Weg ist, aber theoretisch könnten Sie ein Skript verwenden, um Ihren 5.3-Migrationscode zu nehmen und ihn in 5.2 zurückzuportieren (möglicherweise sogar mit PHP).

An Ihren Namespace-Dateien möchten Sie etwas konvertieren:

namespace \Project\Directory\Filename;

class MyClass {
  public $attribute;

  public function typedFunction(MyClass $child) {
    if ($child instanceof MyClass) {
      print 'Is MyClass';
    }
  }
}

Zu etwas wie:

class Project_Directory_Filename_MyClass {
  public $attribute;

  public function typedFunction(Project_Directory_Filename_MyClass $child) {
    if ($child instanceof Project_Directory_Filename_MyClass) {
      print 'Is MyClass';
    }
  }
}

Und in Ihrem Namespace-Code müssten Sie konvertieren von:

$myobject = new Project\Directory\Filename\MyClass();

Zu:

$myobject = new Project_Directory_Filename_MyClass();

Während alle Ihre includes und requires würde gleich bleiben, ich denke, Sie müssten fast eine Art Cache aller Ihrer Klassen und Namensräume aufbewahren, um die komplexe Konvertierung um die ‘instanceof’- und typisierten Parameter durchzuführen, wenn Sie sie verwenden. Das ist das Schwierigste, was ich sehen kann.

  • Ja, Sie haben den kniffligsten Teil Ihrer Antwort ausgelassen 🙂 Es muss auch mit eingebauten Klassen (\Exception) umgehen. Ich habe mich weiter damit befasst und bin jetzt überzeugt, dass ein Tokenizer-basiertes Skript wahrscheinlich der beste Weg ist, aber es erfordert einen ziemlich großen Teil der PHP-Syntax (AST), um analysiert zu werden :/

    – Körnel

    28. Dezember 2009 um 14:56 Uhr


  • Ja, ich gebe zu, ich hatte Glück, dass ich meine Sachen auf 5.3 verschieben konnte und die Abwärtskompatibilität für 5.2 nicht aufrechterhielt. Es ist eine ausgezeichnete Frage. Offensichtlich keine, die wirklich breit geachtet wurde.

    – Kitson

    29. Dezember 2009 um 9:41 Uhr

1308240cookie-checkStrategie zum Entwickeln von Namensraum- und Nicht-Namensraum-Versionen desselben PHP-Codes

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy