【发布时间】:2011-02-09 04:13:48
【问题描述】:
我们都知道臭名昭著的“无法重新声明类”错误。有什么方法可以克服这个问题并实际声明一个具有相同名称的新类,或者这在 PHP 5 中是不可能的?
【问题讨论】:
-
您要完成什么,可能会使答案更有用。否则,请查看 Kohana 的班级系统。
-
你到底为什么想要这样做?
我们都知道臭名昭著的“无法重新声明类”错误。有什么方法可以克服这个问题并实际声明一个具有相同名称的新类,或者这在 PHP 5 中是不可能的?
【问题讨论】:
可能有一种方法可以使用一些不起眼的扩展,但据我所知,在基本的标准 PHP 中,没有。
但是,您始终可以扩展现有的类,并且 - 也许,根据您的情况 - 将该扩展类的实例“走私”到您正在处理的应用程序中。
【讨论】:
AFAIK,在 PHP 中无法重新声明现有函数或类。
如果你能说出你想要做什么,也许还有另一种解决方案......
【讨论】:
正如 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 现在引用了您的命名空间类。
【讨论】:
namespace Mynamespace; class MyClass extends ParentClass {...} 然后我收到 MyNamespace\ParentClass 不存在的错误。有什么解决方法吗?
这是不可能的。根据用例,命名空间(如提到的 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 重新实现一个类。
【讨论】:
基本上你不能重新声明一个类。但如果你真的想,你可以。 :) 一切皆有可能。需要一个动态改变其结构的类吗?您可以使用魔术方法 __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 中重新声明类及其方法是可能的(我认为这是语言设计中的错误)。
【讨论】:
基本上我们不能在 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
【讨论】:
也许是 2016 年更现代的答案,看起来现在至少有 3 个选项:
参考:http://php.net/manual/en/function.runkit-class-emancipate.php
bool runkit_class_emancipate ( string $classname )
“将继承的类转换为基类,删除范围为祖先的任何方法”
虽然我自己没有对此进行测试,但如果您可以控制加载的扩展,这似乎是一个可行的选择。这里的缺点是您失去了所有祖传方法。
参考: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 的问题。
参考:http://php.net/manual/en/function.spl-autoload-register.php
bool spl_autoload_register ([ callable $autoload_function [, bool $throw = true [, bool $prepend = false ]]] )
“将给定函数注册为 __autoload() 实现”
这是我个人最喜欢的三个,因为:
这也是我确实有一些经验的。一个示例实现如下:
// 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';
}
}
【讨论】: