Bezdepbonusu7Программирование: статьи и ответы

Вопрос

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

Автор вопроса: KornelОтветов: 9

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

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

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

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

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

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


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

  • +1 Отличный вопрос - мне тоже это интересно. Peter Bailey 03.12.2009
  • Почему вы хотите работать в 2 раза больше? Сделайте это в другом проекте, где вы можете установить 5.3 в качестве минимального требования. Byron Whitlock 03.12.2009
  • @Byron Whitlock: Но как вы тогда будете использовать свою библиотеку кода, которую вы написали для PHP 5.2, в этом проекте 5.3? Переписать, отрефакторить или оставить как есть? Если ваш ответ один из первых двух, см. исходный вопрос. mercator 03.12.2009
  • @Byron Whitlock: Я не хочу делать двойную работу, поэтому и спрашиваю! Я хотел бы получить конкурентное преимущество, перейдя на 5.3 раньше, но я не хочу отказываться от существующих пользователей. Kornel 03.12.2009
  • Это не в 2 раза больше работы, но до тех пор, пока вы остаетесь совместимым с подходом без пространства имен, вам все равно придется избегать конфликтов имен, как если бы у вас не было пространств имен. Вы не сможете воспользоваться преимуществами этой технологии, пока не откажетесь от версии без пространства имен. Nate C-K 03.12.2009
  • Стоит ли переписывать свой код для использования пространств имен? Kevin 04.12.2009

phpmigrationnamespacescompatibility

Ответы

Ответ 1 · рейтинг 9 · Evert

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

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

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

Удачи!

Ответ 2 · рейтинг 7 · NikiC

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

Код моделирования пространства имен стал достаточно стабильным. Я уже могу заставить 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 файлов. Вам это не нужно, если вы установили интеллектуальный режим переопределения.

Ответ 3 · рейтинг 1 · Kornel

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

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

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

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

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

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

Ответ 4 · рейтинг 1 · NikiC

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

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

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

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

Ответ 5 · рейтинг 1 · Theodore R. Smith

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

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

Ответ 6 · рейтинг 1 · jdd

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

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

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

Ответ 7 · Kitson

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

Ответ 8 · рейтинг -1 · takeshin

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

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

Ответ 9 · рейтинг -1 · Ira Baxter

Наш набор инструментов для реинжиниринга программного обеспечения 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 определяет язык или управляет им, вы можете см. хороший простой пример.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы