【问题标题】:Loading multiple versions of the same class加载同一类的多个版本
【发布时间】:2011-08-13 03:35:35
【问题描述】:

假设我将代码库作为独立的 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();

不幸的是,这不是很好的可扩展性。它适用于 2 实例情况(希望我不必首先使用),但要将其扩展到 3、4、5 或更多实例,您需要定义额外的命名空间并设置,如果你一开始就没有使用这些命名空间,那是一堆不必要的代码。

那么有没有办法动态地创建命名空间,包含一个文件,并在一个唯一命名的变量中实例化该文件中包含的类?


让我再补充一点……

我正在构建一组库,供为几个 CMS 平台构建插件/模块的其他开发人员使用。理想情况下,每个人都将始终使用我的库的最新版本,但我不能保证,也不能保证最终用户在新版本可用时总是升级他们的模块。

我正在尝试使用的用例是最终用户安装两个不同开发人员的两个不同模块:分别称为 AppleOrange。这两个模块都使用我的库的 1.0 版,这很棒。我们可以实例化它一次,两组代码都可以从该功能中受益。

后来,我为这个库发布了一个小补丁。它的版本为 1.1,因为它不会破坏与 1.x 分支的向后兼容性。 Apple 的开发者立即更新他的本地版本并推送他的系统的新版本。 Orange的开发者正在休假,不打扰。

当最终用户更新 Apple 时,她会获得我的库的最新维护版本。因为它是一个维护版本,所以完全替换 1.0 版被认为是安全的。所以代码只实例化了 1.1 并且 Orange 受益于维护补丁,即使开发人员从未费心更新他们的版本。

甚至后来,出于某种原因,我决定更新我的 API 以向 Facebook 添加一些挂钩。新功能和 API 扩展对库有很大的改变,所以我将版本升级到 2.0 以将其标记为在所有情况下都可能不向后兼容。 Apple 再次介入并更新了他的代码。没有任何问题,他只是用最新版本替换了他/lib 文件夹中的我的库。 Orange 决定回到学校成为一名小丑,但已经停止维护他的模块,因此它没有得到任何更新。

当最终用户使用新版本更新 Apple 时,她会自动获得我的库的 2.0 版。但是 Orange 在他的系统中有代码已经添加了 Facebook 钩子,所以如果 2.0 默认滚入他的库中会发生冲突。因此,我没有完全替换它,而是为 Apple 实例化 2.0 一次,然后并排实例化 Orange 附带的 1.0 版本,以便它可以使用正确的代码.

这个项目的全部意义在于允许第三方开发人员基于我的代码构建系统,而不依赖于它们是否可靠,并在他们应该更新他们的代码时。对最终用户而言,什么都不会中断,并且在其他人的系统中使用我的库时更新我的​​库应该是一个简单的文件替换,而不是遍历和更改所有类引用。

【问题讨论】:

  • 你为什么要担心这个?我了解它的用例,但看不到好处。
  • Google 开发人员的演示文稿可能会让您感兴趣youtube.com/watch?v=aAb7hSCtvGw
  • 好处:不止一个开发人员可以使用我的库为特定的 CMS 平台构建模块。如果一个开发人员将他们的系统绑定到 1.0 和一个绑定到 2.0,并且最终用户尝试安装这两个模块,那么平台应该能够同时从两个版本实例化一个对象。
  • 编码人员很可能会为您的第三方代码构建适配器。这样,他们可以在适配器中切换几行,以使应用程序与您的新版本库一起工作。
  • 一个现实生活中这样有用的例子是两个不同的 CMS 模块,一个提供 facebook 登录,一个提供使用 facebook 的注册。两者都使用 facebook PHP SDK,但如果不能命名空间,则不能使用不同的版本。首先加载的任何版本都将优先。

标签: php namespaces dynamic-code


【解决方案1】:

那么有没有办法动态地创建命名空间,包含一个文件,并在一个唯一命名的变量中实例化该文件中包含的类?

是的,这种方法是存在的。您可以使用 eval 和流处理程序做任何您想做的事情。但这是不好的做法和错误的方法-您可以尝试使用工厂方法(代码未经测试-仅显示示例):

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

}

...

【讨论】:

    【解决方案2】:

    让用户选择一个版本,然后根据那个加载你的api文件

    文件名应该是动态确定的,例如:

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

    如果版本 -1.0 明智

    请注意确保用户输入被转换为单个十进制 float 并且没有恶意。

    【讨论】:

    • 问题在于,在/api-1.0 文件夹和/api-2.0 文件夹中,该类都被称为Library。你不能有两个同名的类......这就是我建议命名空间的原因。但我不想对命名空间进行硬编码,我希望那部分是动态的。会有不止一个“用户”,并且可能不会通过沟通来选择版本号......
    【解决方案3】:

    我决定选择一条稍微不同的路线。命名空间方法有效,但您需要为每个版本的类使用不同的命名空间。所以它不是真正可扩展的,因为你必须预先定义可用命名空间的数量。

    相反,我已经确定了类的特定命名架构和版本加载器/实例化器。

    每个类将采用以下格式:

    <?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 类实际上最终会包含一些特定于库的标识符 - 用途、兼容性声明等。这样我可以执行其他逻辑检查以确保 正确 @ 987654323@ 在继续前进并声称 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 的两个不同版本是相同的,但是......所以仍然依赖第三方开发人员知道他们在做什么。

    【讨论】:

      猜你喜欢
      • 2010-12-14
      • 1970-01-01
      • 2011-05-25
      • 1970-01-01
      • 1970-01-01
      • 2012-07-30
      • 1970-01-01
      • 2010-09-18
      • 1970-01-01
      相关资源
      最近更新 更多