Загрузка нескольких версий одного и того же класса - PullRequest
6 голосов
/ 26 апреля 2011

Допустим, я выпустил библиотеку кода как отдельный класс PHP. Затем кто-то использует версию 1.0 этой библиотеки в своем приложении. Позже я выпускаю версию 2.0 библиотеки, и тот же самый человек, по любой причине, должен использовать в своих приложениях одновременно 1.0 и 2.0, потому что он или я нарушили обратную совместимость с новым выпуском.

Если имена классов отличаются, достаточно легко включить и создать оба экземпляра, потому что нет конфликта имен. Но если имена классов остаются неизменными, мы сталкиваемся с проблемами:

include /lib/api-1.0/library.php;
$oldlibary = new Library();

include /lib/api-2.0/library.php;
$newlibrary = new Library();

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

namespace old {
    include /lib/api-1.0/library.php;
}
namespace new {
    include /lib/api-2.0/library.php;
}

$oldlibary = new old\Library();
$newlibrary = new new\Library();

К сожалению, это не очень масштабируемо. Это будет работать с ситуацией с двумя экземплярами (которую, я надеюсь, мне не пришлось бы использовать в первую очередь), но чтобы масштабировать ее до 3, 4, 5 или более экземпляров, вам необходимо определить дополнительные пространства имен и настройте, если вы не используете эти пространства имен, это куча ненужного кода.

Так есть ли способ динамически создать пространство имен, включить файл и создать экземпляр класса, содержащегося в этом файле, в переменной с уникальным именем?


Позвольте мне добавить еще несколько уточнений ...

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

Вариант использования, с которым я пытаюсь работать, - это случай, когда конечный пользователь устанавливает два разных модуля двумя разными разработчиками: назовите их Apple и Orange . Оба модуля используют версию 1.0 моей библиотеки, и это здорово. Мы можем создать его экземпляр один раз , и оба набора кода могут воспользоваться его функциональностью.

Позже я выпускаю небольшой патч для этой библиотеки. Это версия 1.1, потому что она не нарушает обратную совместимость с веткой 1.x. Разработчик Apple немедленно обновляет свою локальную версию и выпускает новую версию своей системы. Разработчик Orange в отпуске и не беспокоится.

Когда конечный пользователь обновляет Apple , она получает последнюю версию технической поддержки моей библиотеки. Поскольку это вспомогательный выпуск, предполагается, что он полностью заменяет версию 1.0. Таким образом, код создает только экземпляры 1.1, а Orange получает выгоду от исправления, даже если разработчик не удосужился обновить их выпуск.

Даже позже я решил обновить свой API, чтобы по какой-то причине добавить некоторые хуки в Facebook. Новые функции и расширения API сильно меняются в библиотеке, поэтому я поднял версию до 2.0, чтобы пометить ее как потенциально несовместимую во всех ситуациях. Еще раз, Apple входит и обновляет свой код. Ничего не сломалось, он просто заменил мою библиотеку в папке /lib на последнюю версию. Оранжевый решил вернуться в школу, чтобы стать клоуном, и, тем не менее, прекратил поддерживать свой модуль, поэтому он не получает никаких обновлений.

Когда конечный пользователь обновляет Apple с новым выпуском, она автоматически получает версию 2.0 моей библиотеки. Но Orange имел в своей системе код, который уже добавил хуки для Facebook, поэтому возникнет конфликт, если 2.0 по умолчанию будет добавлен в его библиотеку. Поэтому вместо полной замены я создаю экземпляр 2.0 один раз для Apple и, параллельно, создаю экземпляр версии 1.0, поставляемой с Orange , чтобы он мог использовать правильный код.

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

Ответы [ 3 ]

3 голосов
/ 27 апреля 2011

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

Вместо этого я остановился на конкретной схеме именования для классов и загрузчика / экземпляра версии.

Каждый класс будет иметь следующий формат:

<?php
if( ! class_exists( 'My_Library' ) ) { class My_Library { } }

if( ! class_exists( 'My_Library_1_0' ) ) :
class My_Library_1_0 extends My_Library {
    ... class stuff ...
}
endif;

Родительский класс My_Library на самом деле будет содержать несколько идентификаторов, специфичных для библиотеки - назначение цели, операторы совместимости и т. Д. Таким образом, я могу выполнитьдругие логические проверки, чтобы убедиться, что right My_Library существует, прежде чем двигаться вперед и заявить, что My_Library_1_0 действительно версия 1.0 библиотеки, которую я хочу.

Далее, у меня есть класс загрузчикакоторый я буду использовать в своем основном проекте:

<?php
class Loader {
    static function load( $file, $class, $version ) {
        include( $file );
        $versionparts = explode('.', $version);
        foreach($versionparts as $part) $class .= '_' . $part;
        return new $class();
    }
}

Как только это будет сделано, вы можете использовать Loader для загрузки обоих экземпляров класса или простых ссылок, если вы хотите использовать статические методы:

$reference = Loader::load( 'library.php', 'My_Library', '1.0' );

$loader = new Loader();
$instance = $loader->load( 'library.php', 'My_Library', '1.0' );

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

0 голосов
/ 26 апреля 2011

Позвольте пользователю выбрать версию, затем в соответствии с этим загрузите ваш файл API

Имя файла должно быть динамически определяемым, например:

include('/lib/api-'.$versionId.'/library.php'); 

, если версия -1.0 в качестве мудрой

Будьте внимательны, чтобы убедиться, что пользовательский ввод преобразуется в одно десятичное число float и ничего плохого.

0 голосов
/ 26 апреля 2011

Так есть ли способ динамически создать пространство имен, включить файл и создать экземпляр класса, содержащегося в этом файле, в переменной с уникальным именем?

Да, такой метод существует. Вы можете делать все что угодно с обработчиками eval и stream. Но это плохая практика и неправильный подход - вы можете попробовать использовать фабричный метод (код не проверен - он показывает только пример):

<?php

if (!class_exists('Library')) {

    class Library
    {
        public static function create($version)
        {
            if (class_exists($c = 'Library' . $version))
                return new $c();
            return null;
        }
    }

}

class Library1
{

}

class Library2
{

}

...
...