Подтвердить что ты не робот

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

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

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

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

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

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

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


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

4b9b3361

Ответ 1

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

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

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

Удачи!

Ответ 2

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

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

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


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

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

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

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

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

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

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

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


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

Ответ 3

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

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

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

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

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

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

Ответ 4

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

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

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

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

Ответ 5

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

Шаг 1. Создайте каталог 5.3 для каждого кода каталога w/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 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.

Ответ 6

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

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

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

Ответ 7

Ну, я не знаю, является ли это "лучшим" способом, но теоретически вы можете использовать script, чтобы перенести код переноса 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();

To:

$myobject = new Project_Directory_Filename_MyClass();

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

Ответ 8

Я не тестировал это самостоятельно, но вы можете взглянуть на это php 5.2 → php 5.3 conversion script.

Это не то же самое, что 5.3 → 5.2, но, возможно, вы найдете там что-то полезное.

Ответ 9

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

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

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

EDIT (в ответ на несколько недоверчивый комментарий):

Для решения 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);

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

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

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

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

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