【问题标题】:Is there any way to redeclare a class safely in PHP?有没有办法在 PHP 中安全地重新声明一个类?
【发布时间】:2011-02-09 04:13:48
【问题描述】:

我们都知道臭名昭著的“无法重新声明类”错误。有什么方法可以克服这个问题并实际声明一个具有相同名称的新类,或者这在 PHP 5 中是不可能的?

【问题讨论】:

  • 您要完成什么,可能会使答案更有用。否则,请查看 Kohana 的班级系统。
  • 你到底为什么想要这样做?

标签: php oop class


【解决方案1】:

可能有一种方法可以使用一些不起眼的扩展,但据我所知,在基本的标准 PHP 中,没有。

但是,您始终可以扩展现有的类,并且 - 也许,根据您的情况 - 将该扩展类的实例“走私”到您正在处理的应用程序中。

【讨论】:

    【解决方案2】:

    AFAIK,在 PHP 中无法重新声明现有函数或类。

    如果你能说出你想要做什么,也许还有另一种解决方案......

    【讨论】:

      【解决方案3】:

      正如 Pekka 和 Techpriester 都指出的那样:不,你不能。但是,如果您使用的是 PHP >= 5.3,那么您可以使用 namespaces 和“use”构造来有效地“重新声明”该类。这是一个例子:

      // MyClass.php
      class MyClass {
        const MY_CONST = 1;
      }
      // MyNamespaceMyClass.php namespace Mynamespace; class MyClass { const MY_CONST = 2; }
      // example.php require_once 'MyClass.php'; require_once 'MyNamespaceMyClass.php';
      use Mynamespace\MyClass as MyClass;
      echo MyClass::MY_CONST; // outputs 2

      因此,您得到了您想要的结果,因为 MyClass 现在引用了您的命名空间类。

      【讨论】:

      • +1 很好的解决方法,适用于没有其他方法的情况(第三方软件等)
      • 所以我认为您的解决方案可能对我有用。但是我有一个问题,两个类都继承了同一个父类。 namespace Mynamespace; class MyClass extends ParentClass {...} 然后我收到 MyNamespace\ParentClass 不存在的错误。有什么解决方法吗?
      【解决方案4】:

      这是不可能的。根据用例,命名空间(如提到的 jpfuentes2)可能适合您。

      一个技巧是实现一个自定义的新“运算符”。

      示例:

      
      $GLOBALS['class_map'] = array('A' => 'A');
      function _new($class){
        $realClass = $GLOBALS['class_map'][$class];
        return new $realClass;
      }
      
      

      class A {} $a = _new('A');

      // change the implementation $GLOBALS['class_map']['A'] = 'B'; $a2 = _new('A');

      另一个技巧是使用runkit 重新实现一个类。

      【讨论】:

        【解决方案5】:

        基本上你不能重新声明一个类。但如果你真的想,你可以。 :) 一切皆有可能。需要一个动态改变其结构的类吗?您可以使用魔术方法 __call 和模式状态。

        class Example
        {
          var $state;
        
          public function setImplementation($state)
          {
            $this->state = $state;
          }
        
          public function __call($method, $args)
          {
            if (method_exists($this->state, $method))
               return $this->state->$method($args);
            else
              // error
          }
        
        }
        

        还有一个 PHP 工具包可以动态处理类:http://php.net/manual/en/book.runkit.php

        我知道在 Ruby 中重新声明类及其方法是可能的(我认为这是语言设计中的错误)。

        【讨论】:

          【解决方案6】:

          基本上我们不能在 PHP directly 中重新声明一个类。如果您需要在 php 中重新声明一个类,那么我建议您将该类写在一个单独的文件中并使用require_one 将该文件调用到所需的页面。如下:

          Page1.php

          class abcd
          {
              function max()
              {
                  echo "Hello World!!! count:-".$GLOBALS['x'];
              }
          }
          

          Page2.php

          $i=10; 
          for($x=0;$x<$i;$x++)
          {
                require_once "Page1.php";
                $myclass = new abcd();
                $myclass->max();
          }
          

          Now it will work as you desired。它适用于me

          输出如下:

           Hello World!!! count:- 0
          
           Hello World!!! count:- 1
          
           Hello World!!! count:- 2
          
           Hello World!!! count:- 3
          
           Hello World!!! count:- 4
          
           Hello World!!! count:- 5
          
           Hello World!!! count:- 6
          
           Hello World!!! count:- 7
          
           Hello World!!! count:- 8
          
           Hello World!!! count:- 9
          

          【讨论】:

            【解决方案7】:

            也许是 2016 年更现代的答案,看起来现在至少有 3 个选项:

            选项 1. 使用 runkit

            参考:http://php.net/manual/en/function.runkit-class-emancipate.php

            bool runkit_class_emancipate ( string $classname )

            “将继承的类转换为基类,删除范围为祖先的任何方法”

            虽然我自己没有对此进行测试,但如果您可以控制加载的扩展,这似乎是一个可行的选择。这里的缺点是您失去了所有祖传方法。


            选项 2. 再次使用 runkit

            参考:http://php.net/manual/en/function.runkit-method-redefine.php

            bool runkit_method_redefine ( string $classname , string $methodname , string $args , string $code [, int $flags = RUNKIT_ACC_PUBLIC ] )

            “动态改变给定方法的代码”

            如果您的目标是在基类上调整一两个方法,这解决了选项 1 的问题。


            选项 3. 实现自动加载器

            参考:http://php.net/manual/en/function.spl-autoload-register.php

            bool spl_autoload_register ([ callable $autoload_function [, bool $throw = true [, bool $prepend = false ]]] )

            “将给定函数注册为 __autoload() 实现”

            这是我个人最喜欢的三个,因为:

            • 它在现代 PHP 版本中本机工作
            • 它可以与其他自动装载机配合使用
            • 它允许您围绕覆盖实现条件逻辑

            这也是我确实有一些经验的。一个示例实现如下:

            // Some early point in your application
            // Ideally after other autoloaders have registered
            spl_autoload_register('autoload_override');
            
            function autoload_override($class) {
              if ($class == 'TargetClassName') {
                include '/path/to/overriding/class.php';
              }
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2015-11-05
              • 1970-01-01
              • 2010-12-22
              • 2018-02-23
              • 2020-05-15
              • 2012-06-26
              • 2012-12-13
              • 1970-01-01
              相关资源
              最近更新 更多