Стратегия разработки версий одного и того же PHP-кода с пространством имен и без пространства имен


Я поддерживаю библиотеку, написанную для PHP 5.2, и я хотел бы создать ее версию PHP 5.3 с пространством имен. Тем не менее, я бы также обновлял версию без пространства имен до тех пор, пока PHP 5.3 не станет настолько старым, что даже стабильный Debian отправит его;)

У меня довольно чистый код, около 80 классов, следующих схеме именования Project_Directory_Filename (я бы, конечно, изменил их на \Project\Directory\Filename), и только несколько функций и констант (также с префиксом имени проекта).

Вопрос в следующем: каков наилучший способ разработки пространства имен и параллельные версии без пространства имен?

  • Должен ли я просто создать разветвление в репозитории и продолжать объединять изменения между ветвями? Бывают ли случаи, когда код с обратной косой чертой становится трудно объединить?

  • Должен ли я написать скрипт, который преобразует версию 5.2 в 5.3 или наоборот? Должен ли я использовать PHP-токенизатор? sed? Препроцессор C?

  • Есть ли лучший способ использовать пространства имен там, где это возможно, и сохранить обратную совместимость со старыми PHP?


Обновление: Все-таки решил не использовать пространства имен.

Author: hakre, 2009-12-03

9 answers

Я не думаю, что предварительная обработка кода 5.3 - это отличная идея. Если ваш код функционально идентичен как в PHP 5.2, так и в 5.3, за исключением использования пространств имен вместо префиксов, разделенных подчеркиванием, зачем вообще использовать пространства имен? В этом случае мне кажется, что вы хотите использовать пространства имен ради использования пространств имен..

Я действительно думаю, что вы обнаружите, что по мере перехода к пространствам имен вы начнете "думать немного по-другому" об организации своего кода.

По этой причине я полностью согласен с вашим первым решением. Создайте вилку и сделайте бэкпорты функций и исправлений ошибок.

Удачи!

 9
Author: Evert, 2009-12-28 11:31:08

Это продолжение моего предыдущего ответа:

Код моделирования пространства имен стал довольно стабильным. Я уже могу заставить symfony2 работать (некоторые проблемы все еще есть, но в основном). Хотя все еще не хватает некоторых вещей, таких как разрешение пространства имен переменных для всех случаев, кроме new $class.

Теперь я написал скрипт, который будет рекурсивно перебирать каталог и обрабатывать все файлы: http://github.com/nikic/prephp/blob/master/prephp/namespacePortR.php


Инструкции по использованию

Требования к работе вашего кода

Имена ваших классов не должны содержать символ _. Если они это сделают, имена классов могут стать неоднозначными при преобразовании.

Ваш код не должен повторно объявлять какие-либо глобальные функции или константы в пространстве имен. Таким образом, гарантируется, что весь ваш код может быть разрешен во время компиляции.

В основном это только ограничения для вашего кода. Хотя я должен отметить, что в конфигурации по умолчанию namespacePortR не будет разрешать такие вещи, как $className = 'Some\\NS\\Class'; new $className, потому что для этого потребуется вставить дополнительный код. Лучше, чтобы это было исправлено позже (вручную или с помощью автоматизированной системы исправлений.)

Конфигурация

Поскольку мы предположили, что в пространстве имен не объявляется глобальная функция или константа, вы должны установить константу класса assumeGlobal в пространстве имен слушатель. В том же файле установите константу SEPARATOR в _.

В окне пространства имен измените блок конфигурации, чтобы удовлетворить ваши потребности.


PS: Сценарию может быть предоставлена опция ?skip=int. Это говорит ему пропустить первые файлы int. Вам это не понадобится, если вы установили режим переопределения на интеллектуальный.

 7
Author: NikiC, 2017-05-23 12:25:02

Вот что я нашел:

Делать это с регулярными выражениями - кошмар. Вы можете сделать большую часть этого всего несколькими простыми выражениями, но тогда крайние случаи - это убийца. Я закончил с ужасным, хрупким беспорядком, который едва работает с одной кодовой базой.

Это выполнимо с помощью встроенного маркера и простого анализатора рекурсивного спуска, который обрабатывает только упрощенное подмножество языка.

В итоге у меня получился довольно уродливый дизайн (парсер и трансформатор в одном – в основном просто меняя или повторно испуская токены), потому что казалось слишком большой работой построить полезное синтаксическое дерево с сохранением пробелов (я хотел, чтобы полученный код был удобочитаемым для человека).

Я хотел попробовать phc для этого, но не смог убедить его configure в том, что я создал требуемую версию библиотеки Boost.

Я еще не пробовал ANTLR для этого, но это, вероятно, лучший инструмент для такого рода задач.

 1
Author: Kornel, 2010-02-28 22:16:10

Я работаю над проектом, который эмулирует PHP 5.3 на PHP 5.2: prephp. Он включает поддержку пространства имен (хотя еще не завершен.)

Теперь, исходя из опыта написания этого, существует одна проблема неоднозначности в разрешении пространства имен : Неквалифицированные вызовы функций и постоянные запросы имеют запасной вариант для глобального пространства имен. Таким образом, вы можете автоматически конвертировать свой код только в том случае, если вы либо полностью квалифицировали, либо квалифицировали все свои вызовы функций/постоянные запросы, либо если вы не переопределяли какую-либо функцию или константу в пространстве имен с тем же именем, что и встроенная функция PHP.

Если бы вы строго придерживались этой практики (какую бы из них вы ни выбрали), было бы довольно легко преобразовать ваш код. Это будет подмножество кода для эмуляции пространств имен в prephp. Если вам нужна помощь с реализацией, не стесняйтесь обращаться ко мне, мне было бы интересно;)

PS: Код эмуляции пространства имен prephp еще не завершен и может содержать ошибки. Но это может дать вам некоторые идеи.

 1
Author: NikiC, 2010-09-03 16:08:20

Вот лучший ответ, который, я думаю, вы сможете найти:

Шаг 1. Создайте каталог под названием 5.3 для каждого каталога с кодом/php5.3 в нем и вставьте в него весь специфичный для 5.3 код.

Шаг 2: Возьмите класс, который вы хотите поместить в пространство имен, и сделайте это в 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();
    }
}

Шаг 3: Используйте функцию стратегии для использования нового кода PHP 5.3. Место в не-PHP5.3 findclass.inc.php:

// 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.");
}

Шаг 4: Перепишите свой код так, чтобы он выглядел следующим образом:

<?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.

Это будет работать на вас. Это небольшая проблема с точки зрения производительности, но совсем немного, и с ней будет покончено, когда вы решите прекратить поддержку 5.3.

 1
Author: Theodore R. Smith, 2010-09-04 10:24:45

То, что я сделал с большой кодовой базой, в которой использовалось соглашение об именовании подчеркивания (среди прочего), и require_once много вместо автозапуска, состояло в том, чтобы определить автозагрузчик и добавить class_alias строки в файлах, определяющие псевдонимы для старого имени классов после изменения их имен, чтобы они соответствовали пространствам имен.

Затем я начал удалять операторы require_once, выполнение которых не зависело от порядка включения, так как автозагрузчик будет собирать данные и данные пространства имен, как я пошел исправлять ошибки и так далее.

До сих пор это работало довольно хорошо.

 1
Author: jeremiahd, 2012-01-05 12:23:42

Ну, я не знаю, является ли это "лучшим" способом, но теоретически вы могли бы использовать скрипт для переноса кода 5.3 и переноса его в 5.2 (потенциально даже с использованием PHP).

В файлах вашего пространства имен вы хотели бы что-то преобразовать:

namespace \Project\Directory\Filename;

class MyClass {
  public $attribute;

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

К чему-то вроде:

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';
    }
  }
}

И в вашем коде пространства имен вам нужно будет преобразовать из:

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

Кому:

$myobject = new Project_Directory_Filename_MyClass();

В то время как все ваши includes и requires останутся прежними, я думаю, вам почти нужно будет сохранить своего рода кэш всех ваших классов и пространства имен для выполнения сложного преобразования вокруг "instanceof" и типизированных параметров, если вы их используете. Это самая сложная вещь, которую я могу видеть.

 0
Author: Kitson, 2009-12-28 11:50:45

Я не тестировал это самостоятельно, но вы можете взглянуть на этот скрипт преобразования php 5.2 ->php 5.3.

Это не то же самое, что 5.3->5.2, но, возможно, вы найдете там что-нибудь полезное.

 -1
Author: takeshin, 2010-06-06 09:08:21

Наш Инструментарий для реинжиниринга программного обеспечения DMS, вероятно, может довольно хорошо реализовать ваше решение. Он предназначен для выполнения надежных преобразований исходного кода с использованием преобразований AST в AST, закодированных в терминах поверхностного синтаксиса.

У него есть Интерфейс PHP, который является полным, точным анализатором PHP, построителем AST и регенератором AST для PHP-кода. DMS обеспечивает точную печать или точную печать ("по возможности сохраняйте номера столбцов").

Эта комбинация был использован для реализации различных надежных инструментов обработки исходного кода PHP для PHP 4 и 5.

РЕДАКТИРОВАТЬ (в ответ на несколько недоверчивый комментарий):

Для решения OP большую часть работы должно выполнять следующее правило преобразования DMS:

rule replace_underscored_identifier_with_namespace_path(namespace_path:N)
   :namespace_path->namespace_path
"\N" -> "\complex_namespace_path\(\N\)" 
if N=="NCLASS_OR_NAMESPACE_IDENTIFIER" && has_underscores(N);

Это правило находит все "простые" идентификаторы, которые используются там, где разрешены пути к пространству имен, и заменяет эти простые идентификаторы соответствующим путем к пространству имен, построенным путем разрыва строки для идентификатор разделяется на составные элементы, разделенные символами подчеркивания. Нужно закодировать некоторую процедурную помощь в языке реализации DMS, PARLANSE, чтобы проверить, что идентификатор содержит подчеркивания ("has_underscores"), и реализовать логику разрыва путем построения соответствующего поддерева пути к пространству имен ("complex_namespace_path").

Правило работает путем абстрактного определения деревьев, соответствующих нетерминалам языка (в данном случае "namespace_path" и замены простых единицы более сложными деревьями, которые представляют полный путь пространства имен. Правило написано в виде текста, но само правило анализируется DMS для построения деревьев, необходимых для соответствия деревьям PHP.

Логика применения правил DMS может тривиально применять это правило повсюду в AST, создаваемом анализатором PHP.

Этот ответ может показаться чрезмерно простым перед лицом всего сложного, что составляет язык PHP, но вся эта другая сложность скрыта в языке PHP определение, используемое DMS; это определение содержит около 10 000 строк лексических и грамматических определений, но уже протестировано и работает. Все оборудование DMS и эти 10 тыс. строк указывают на то, почему простые регулярные выражения не могут надежно выполнять свою работу. (Удивительно, сколько оборудования требуется, чтобы сделать это правильно; я работаю над DMS с 1995 года).

Если вы хотите увидеть все механизмы, которые определяют, как DMS определяет/управляет языком, вы можете увидеть приятный простой пример.

 -1
Author: Ira Baxter, 2010-09-08 00:16:02