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

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

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

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

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

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

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


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


person Kornel    schedule 02.12.2009    source источник
comment
+1 Отличный вопрос - мне тоже это интересно.   -  person Peter Bailey    schedule 03.12.2009
comment
Почему вы хотите работать в 2 раза больше? Сделайте это в другом проекте, где вы можете установить 5.3 в качестве минимального требования.   -  person Byron Whitlock    schedule 03.12.2009
comment
@Byron Whitlock: Но как вы тогда будете использовать свою библиотеку кода, которую вы написали для PHP 5.2, в этом проекте 5.3? Переписать, отрефакторить или оставить как есть? Если ваш ответ один из первых двух, см. исходный вопрос.   -  person mercator    schedule 03.12.2009
comment
@Byron Whitlock: Я не хочу делать двойную работу, поэтому и спрашиваю! Я хотел бы получить конкурентное преимущество, перейдя на 5.3 раньше, но я не хочу отказываться от существующих пользователей.   -  person Kornel    schedule 03.12.2009
comment
Это не в 2 раза больше работы, но до тех пор, пока вы остаетесь совместимым с подходом без пространства имен, вам все равно придется избегать конфликтов имен, как если бы у вас не было пространств имен. Вы не сможете воспользоваться преимуществами этой технологии, пока не откажетесь от версии без пространства имен.   -  person Nate C-K    schedule 03.12.2009
comment
Стоит ли переписывать свой код для использования пространств имен?   -  person Kevin    schedule 04.12.2009
comment
@Кевин: Для меня да. Классы со сверхдлинными префиксами раздражают в работе, и я планирую поддерживать этот код в течение длительного времени.   -  person Kornel    schedule 04.12.2009
comment
Эта тема обсуждалась до смерти на конференции, которую я посетил, и прошло некоторое время, может быть, люди уже нашли некоторые ответы. Назначить награду, чтобы, возможно, привлечь новых людей/ответы   -  person edorian    schedule 02.09.2010
comment
Я по-прежнему считаю, что мой ответ ниже, безусловно, лучшее бесплатное решение.   -  person Theodore R. Smith    schedule 08.09.2010
comment
Мне нравится ваш пост в блоге о пространствах имен. Спасибо за ссылку   -  person    schedule 27.11.2013


Ответы (9)


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

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

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

Удачи!

person Evert    schedule 28.12.2009

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

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

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


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

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

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

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

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

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

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

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


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

person NikiC    schedule 07.09.2010

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

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

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

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

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

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

person Kornel    schedule 22.02.2010
comment
За исключением очень ограниченных особых случаев (например, вашего упрощенного подмножества), регулярные выражения всегда являются катастрофой, когда они применяются для внесения массовых, надежных изменений в исходный код, именно потому, что они не могут зафиксировать структуру языка. - person Ira Baxter; 07.09.2010
comment
Поэтому мой ответ лучший. - person Theodore R. Smith; 08.09.2010

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

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

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

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

person NikiC    schedule 03.09.2010
comment
Вы можете свободно использовать мой ответ ниже в своем коде, если вы сохраняете тег авторского права ;-) - person Theodore R. Smith; 04.09.2010

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

Шаг 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.

person Theodore R. Smith    schedule 04.09.2010
comment
Мое решение работает без необходимости тратить тысячи на нетоварный продукт, который никто не может эффективно продемонстрировать, а многие из нас не могут себе позволить. - person Theodore R. Smith; 09.09.2010
comment
Хотя мое решение работает примерно так же хорошо, как нетоварный продукт, делающий то же самое. Поэтому, пожалуйста, перестаньте говорить людям, что ваш ответ лучший. Это хорошая возможность, но меня раздражает, что ты бегаешь и рассказываешь всем, как это хорошо. Спасибо. - person NikiC; 09.09.2010
comment
Это не относится к моей проблеме. Он показывает только, как загружать код PHP 5.3/5.2, а не как его создавать/обслуживать. Я также не понимаю, почему я должен вызывать это явно вместо настройки автозагрузчика. - person Kornel; 10.09.2010

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

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

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

person jdd    schedule 05.01.2012

Что ж, я не знаю, является ли это «лучшим» способом, но теоретически вы можете использовать скрипт, чтобы взять код миграции 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 останутся прежними, я думаю, вам почти нужно будет сохранить какой-то кеш для всех ваших классов и пространства имен, чтобы выполнить сложное преобразование вокруг «экземпляра» и типизированных параметров, если вы их используете. Это самое сложное, что я вижу.

person Kitson    schedule 28.12.2009
comment
Да, вы упустили самую сложную часть своего ответа :) Он также должен иметь дело со встроенными классами (\Exception). Я изучил это подробнее и теперь я убежден, что сценарий на основе токенизатора, вероятно, лучший способ, но для его анализа требуется довольно большой фрагмент синтаксиса PHP (AST):/ - person Kornel; 28.12.2009
comment
Да, я признаю, что мне повезло, что я смог перенести свои вещи на 5.3 и не поддерживать обратную совместимость с 5.2. Это отличный вопрос. Очевидно, не тот, который действительно широко рассматривался. - person Kitson; 29.12.2009

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

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

person takeshin    schedule 05.06.2010
comment
Спасибо, но это похоже на инструмент для удаления ворсинок, а не на конвертер. - person Kornel; 25.06.2010

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

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

Эта комбинация использовалась для реализации различных надежных инструментов обработки исходного кода 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").

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

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

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

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

person Ira Baxter    schedule 07.09.2010
comment
Не могли бы вы объяснить, почему это было выбрано в качестве ответа? Я имею в виду, что набор инструментов для реинжиниринга DMS стоит несколько сотен долларов, и нет вообще примера того, что он может делать то, о чем просили, в то время как другие ответы здесь фактически дают вам рабочие прототипы ... бесплатно. - person Theodore R. Smith; 08.09.2010
comment
Демо тоже нет, купить негде. Черт возьми, с того места, где я сижу, это выглядит как абсолютная пустышка. - person Theodore R. Smith; 08.09.2010
comment
Vaporware? Нет примеров? Вы проверили инструменты PHP на веб-сайте? Вы можете в это не поверить, но все они реализованы с использованием DMS, внося массовые изменения в исходный код для достижения нужного эффекта. Вы можете проверить semanticdesigns.com/Products/Services/NorthropGrummanB2.html для примеры применения в тяжелых условиях. Вы правы, это не бесплатно. На самом деле, это значительно дороже, чем число, которое вы сообщаете. Вы и ОП можете отдельно определить, оправдана ли его стоимость для того, что он делает WRT для ваших нужд. Вы покупаете его в нашей компании. - person Ira Baxter; 08.09.2010
comment
... РЕДАКТИРОВАТЬ: я изменил ответ, чтобы предоставить более подробную информацию о том, как DMS может достичь желаемого эффекта. Первоначально я не включал это, потому что ОП, похоже, знал (на основе одного из предоставленных им ответов) о значении абстрактных синтаксических деревьев для целей модификации кода. - person Ira Baxter; 08.09.2010
comment
Спасибо за обновления. Не могли бы вы показать мне, где можно купить или продемонстрировать набор инструментов для реинжиниринга? - person Theodore R. Smith; 08.09.2010
comment
Свяжитесь с нами по адресу [email protected]. Он не продается как товарный продукт. - person Ira Baxter; 08.09.2010