在PHP中使用动态变量实例化对象的含义

时间:2020-03-05 18:48:51  来源:igfitidea点击:

使用以下形式在PHP中声明新的类实例的性能,安全性或者"其他"含义是什么?

<?php
  $class_name = 'SomeClassName';
  $object = new $class_name;
?>

这是一个人为的示例,但是我已经在工厂(OOP)中看到了这种形式,以避免使用较大的if / switch语句。

立即想到的问题是

  • 我们失去了将参数传递给构造函数的能力(LIES。谢谢Jeremy)
  • 闻起来像eval()一样,它带有表带来的所有安全问题(但不一定是性能问题?)

还有什么其他含义,或者有人可以使用" Rank PHP Hackery"以外的搜索引擎术语来进行研究?

解决方案

回答

看起来我们仍然可以将参数传递给构造函数,这是我的测试代码:

<?php

class Test {
    function __construct($x) {
        echo $x;
    }
}

$class = 'Test';
$object = new $class('test'); // echoes "test"

?>

那是你的意思,对不对?

因此,我们提到的并且我能想到的唯一另一个问题是它的安全性,但是使其变得安全起来并不难,而且显然比使用eval()更安全。

回答

实际上,在查找类定义之前必须先解析变量的名称,可能确实会对性能产生影响。但是,如果不动态声明类,则无法进行"动态"或者"元"编程。我们将无法编写代码生成程序或者诸如域特定语言构造之类的任何东西。

我们在内部框架的某些核心类中各处都使用了此约定,以使URL到控制器的映射起作用。我还在许多商业开源应用程序中看到了它(我将尝试挖掘一个示例并将其发布)。无论如何,我的回答的重点是,如果编写更灵活,动态的代码,性能可能会略有下降,这似乎是值得的。

我要提到的另一个折衷方案是,除了性能之外,它的确会使代码不太明显且不易读,除非我们对变量名非常小心。大多数代码只编写一次,然后多次重读和修改,因此可读性很重要。

回答

在运行时进行解析的问题之一是,使操作码缓存(如APC)变得非常困难。不过,就目前而言,如果我们在实例化内容时需要一定数量的间接访问,则执行类似我们在问题中描述的操作是一种有效的方法。

只要你不做类似的事情

$classname = 'SomeClassName';
for ($x = 0; $x < 100000; $x++){
  $object = new $classname;
}

你可能很好:-)

(我的意思是:在这里动态查找课程,然后再继续学习也不会受到伤害。如果我们经常这样做,将会如此)。

另外,请确保永远不要从外部设置$ classname,而希望对要实例化的确切类进行一些控制。

回答

我在自定义框架中使用动态实例化。我的应用程序控制器需要根据请求实例化一个子控制器,而使用一个巨大的,不断变化的switch语句来管理那些控制器的加载将是非常荒谬的。结果,我可以在控制器后的控制器中添加控制器,而不必修改应用程序控制器来调用它们。只要我的URI遵守框架的约定,应用程序控制器就可以使用它们,而无需在运行时知道任何内容。

我现在正在生产购物车应用程序中使用此框架,性能也相当不错。话虽如此,我只在整个应用程序的一两个位置使用动态类选择。我想知道在什么情况下我们需要经常使用它,以及那些情况是否是由于程序员想要过度抽象应用程序而遭受的(我以前一直对此感到内gui)。

回答

艾伦,动态类初始化没有错。这种技术也存在于Java语言中,在Java语言中,可以使用Class.forClass('classname')`方法将字符串转换为类。将算法复杂度推迟到几个类而不是包含if条件列表也非常方便。动态类名称特别适合希望代码保持开放状态以进行扩展而无需修改的情况。

我本人经常将不同的类与数据库表结合使用。在第一列中,我保留了将用于处理记录的类名。这给了我强大的力量,可以添加新类型的记录并以独特的方式处理它们,而无需更改现有代码中的单个字节。

我们不必担心性能。它几乎没有开销,并且对象本身在PHP中非常快。如果我们需要产生数千个相同的对象,请使用Flyweight设计模式来减少内存占用。特别是,我们不应牺牲开发人员的时间来节省服务器上的毫秒数。操作码优化器也可以与此技术无缝协作。使用Zend Optimizer编译的脚本不会出现错误行为。

回答

我要补充一点,我们也可以使用动态数量的参数来实例化它:

<?php

$class = "Test";
$args = array('a', 'b');
$ref = new ReflectionClass($class);
$instance = $ref->newInstanceArgs($args);

?>

但是,当然,这样做会增加一些开销。

关于安全性问题,我认为它并不重要,至少与eval()相比没有任何意义。在最坏的情况下,错误的类会被实例化,这当然是潜在的安全漏洞,但要利用它要难得多,如果确实需要用户输入来定义类名,则很容易使用允许的类数组进行过滤。

回答

一个问题是,例如,我们不能解决静态成员这样的问题

<?php
$className = 'ClassName';

$className::someStaticMethod(); //doesn't work
?>

回答

@coldFlame:IIRC,我们可以使用call_user_func(array($ className,'someStaticMethod')'和call_user_func_array()`传递参数

回答

class Test {
    function testExt() {
    print 'hello from testExt :P';
    }
    function test2Ext()
    {
    print 'hi from test2Ext :)';
    }
}

$class = 'Test';
$method_1 = "testExt";
$method_2 = "test2Ext";
$object = new $class(); // echoes "test"
$object->{$method_2}(); // will print 'hi from test2Ext :)'
$object->{$method_1}(); // will print 'hello from testExt :P';

此技巧在php4和php5中均有效:D享受。