36

这是我想要做的:

$clsName = substr(md5(rand()),0,10); //generate a random name
$cls = new $clsName(); //create a new instance

function __autoload($class_name)
{
  //define that instance dynamically
}

显然这不是我实际在做的事情,但基本上我有一个类的未知名称,并且基于名称,我想生成具有某些属性等的类。

我尝试过使用 eval() ,但它让我很适合私人和 $this-> 引用......

//编辑

好吧,显然我简短而甜蜜的“这就是我想做的”在那些可能能够提供答案的人中引起了巨大的冲突和惊愕。希望得到一个实际的答案,我会更详细。

我在我维护的网站上有一个使用代码提示的验证框架。每个函数有两个定义

function DoSomething($param, $param2){
   //code
}
function DoSomething_Validate(vInteger $param, vFloat $param2){
   //return what to do if validation fails
}

我正在寻找为我的数据库中的主键添加一个验证器。我不想为每个表(203)创建一个单独的类。所以我的计划是做类似的事情

function DoSomething_Validate(vPrimaryKey_Products $id){ }

__autoload 将生成 vPrimaryKey 的子类并将表参数设置为 Products。

现在开心?

4

10 回答 10

15

从 PHP 7.0 开始,只要有一点创造力和对一些鲜为人知的 PHP 特性的了解,您完全可以做到这一点,而无需求助于 eval 或动态创建脚本文件。你只需要使用匿名类class_alias (),像这样:

spl_autoload_register(function ($unfoundClassName) {
{
    $newClass = new class{}; //create an anonymous class
    $newClassName = get_class($newClass); //get the name PHP assigns the anonymous class
    class_alias($newClassName, $unfoundClassName); //alias the anonymous class with your class name
}

这是可行的,因为匿名类仍然在幕后分配了一个名称并放在全局范围内,因此您可以自由地获取该类名称并为其命名。查看上面匿名类链接下的第二条评论以获取更多信息。

话虽如此,我觉得这个问题中的所有人都在说“Eval 总是一个非常糟糕的主意。永远不要使用它!” 只是重复他们从蜂巢头脑中听到的内容,而不是自己思考。Eval 在语言中是有原因的,并且在某些情况下可以有效地使用它。如果您使用的是旧版本的 PHP,那么 eval 可能是一个很好的解决方案。

但是,它们是正确的,因为它可以打开非常大的安全漏洞,您必须小心使用它并了解如何消除风险。重要的是,就像 SQL 注入一样,您必须清理您在 eval 语句中输入的任何输入。

例如,如果您的自动加载器看起来像这样:

spl_autoload_register(function ($unfoundClassName) {
{
    eval("class $unfoundClassName {}");
}

黑客可以这样做:

$injectionCode = "bogusClass1{} /*insert malicious code to run on the server here */ bogusClass2";

new $injectionCode();

看看这有可能成为安全漏洞吗?黑客在两个虚假类名称之间放置的任何内容都将通过 eval 语句在您的服务器上运行。

如果您调整自动加载器以检查传入的类名(即进行 preg_match 以确保没有空格或特殊字符,将其与可接受名称列表进行检查等),您可以消除这些风险,然后 eval 可能是在这种情况下使用完全没问题。但是,如果您使用的是 PHP 7 或更高版本,我推荐上面的匿名类别名方法。

于 2018-10-17T18:19:55.600 回答
13

有趣的是,实际上这是 eval 似乎不是一个坏主意的少数事情之一。

只要您可以确保没有用户输入将进入评估。

你仍然有一些缺点,比如当你使用字节码缓存时,代码不会被缓存等等。但是 eval 的安全问题与在 eval 中有用户输入或最终进入错误的范围有关。

如果你知道自己在做什么,eval 会帮助你。

也就是说,在我看来,当您不依赖类型提示进行验证时,您的情况会好得多,但您只有一个功能

DoSomething_Validate($id)
{ 
   // get_class($id) and other validation foo here
}
于 2010-06-03T13:48:57.740 回答
13

我知道这是一个老问题,并且有一些可以解决的问题,但是我想提供一些可以回答原始问题的片段,并且我认为如果有人像我在搜索答案时那样最终来到这里,我想提供一个更扩展的解决方案到这个问题。

创建单个动态类

<?php
// Without properties
$myclassname = "anewclassname";
eval("class {$myclassname} { }";
// With a property
$myclassname = "anewclassname";
$myproperty = "newproperty";
eval("class {$myclassname} { protected \${$myproperty}; }";
?>

只要你正确地转义你的文本,你也可以在那里添加一个函数。

但是,如果您想基于本身可能是动态的东西来动态创建类,例如为数据库中的每个表创建一个类,就像提到的原始问题一样?

创建多个动态类

<?php

// Assumes $dbh is a pdo connection handle to your MySQL database
$stmt=$dbh->prepare("show tables");
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
$handle = null;
$classcode = '';
foreach ($result as $key => $value) {
    foreach ($value as $key => $value) {
        $classcode = "class {$value} { ";
        $stmt2=$dbh->prepare("DESC $value");
        $stmt2->execute();
        $result2 = $stmt2->fetchAll(PDO::FETCH_ASSOC);
        foreach ($result2 as $key => $value) {
            $classcode .= "public \${$value['Field']}; ";
        }
        $classcode .=  "}";
        eval($classcode);
    }
}

?>

这将为数据库中的每个表动态生成一个类。对于每个类,也会创建一个以每列命名的属性。

现在有人指出你不应该这样做。只要您控制评估中发生的事情,安全风险就不是问题。但是 - 如果您对它进行足够深入的思考,很可能会有一个更有意义的解决方案。我认为我拥有动态创建新类的完美用例。仔细检查问题证明并非如此。

一种可能的解决方案——使用 stdClass 创建只是数据容器且不需要任何方法的对象。

另外——如前所述,您可以使用脚本手动生成大量类。对于镜像数据库表的类,您可以使用我上面的相同逻辑,而不是执行 eval,而是将该信息写入文件。

于 2013-03-22T20:39:01.040 回答
5

我认为使用eval()它不是一个可靠的解决方案,特别是如果您的脚本或软件将分发给不同的客户端。共享主机提供商总是禁用eval()功能。

我正在考虑这样的更好方法:

<?php 

 function __autoload( $class ) {
      require 'classes/'.$class.'.php';

}

$class = 'App';
$code = "<?php class $class {
    public function run() {
        echo '$class<br>';  
    }
    ".'
    public function __call($name,$args) {
        $args=implode(",",$args);
        echo "$name ($args)<br>";
    }
}';

file_put_contents('classes/App.php' ,$code);

$a = new $class();
$a->run();

执行完代码后,你可以根据需要删除文件,我测试了它,它工作得很好。

于 2014-08-30T11:36:45.153 回答
4

使用 eval() 确实是个坏主意。它打开了一个很大的安全漏洞。只是不要使用它!

于 2009-07-29T23:29:34.573 回答
3
function __autoload($class)  {
    $code = "class $class {`
        public function run() {
            echo '$class<br>';  
        }
        ".'
        public function __call($name,$args) {
            $args=implode(",",$args);
            echo "$name ($args)<br>";
        }
    }';

    eval($code);
}

$app=new Klasse();
$app->run();
$app->HelloWorld();

这可能有助于在运行时创建一个类。它还为未知方法创建了一个方法运行和一个包罗万象的方法,但最好在运行时创建对象,而不是类。

于 2012-03-17T21:18:03.253 回答
2

这几乎可以肯定是个坏主意。

我认为您最好将时间花在创建一个为您创建类定义的脚本上,而不是在运行时尝试这样做。

带有命令行签名的东西,例如:

./generate_classes_from_db <host> <database> [tables] [output dir]
于 2009-07-29T23:27:16.110 回答
1

请阅读其他所有人的答案,了解这确实是一个非常非常糟糕的主意。

一旦你理解了这一点,这里有一个小演示,说明你可以但不应该这样做。


<?php
$clname = "TestClass";

eval("class $clname{}; \$cls = new $clname();");

var_dump($cls);
于 2009-07-29T23:36:22.203 回答
1

我创建了一个动态创建类/接口/特征的包......并将它们存储到一个文件中,然后您可以只包含创建的文件以便能够使用您的类

包:https ://packagist.org/packages/kanel/enuma

于 2018-06-22T08:31:39.230 回答
0

我们可以通过以下方式动态创建类实例

我在 Laravel 5.8 版本中也遇到了这个问题,现在它对我来说工作正常。

给出完整路径而不是类名

class TestController extends Controller
{
    protected $className;

    public function __construct()
    {
        $this->className = 'User';
    }

    public function makeDynamicInstance()
    {
        $classNameWithPath = 'App\\' . $this->className; 
        $classInstance = new $classNameWithPath;
        $data = $classInstance::select('id','email')->get();
        return $data;
    }
}

输出

Illuminate\Database\Eloquent\Collection Object
(
    [items:protected] => Array
        (
            [0] => App\User Object
                (
                    [fillable:protected] => Array
                        (
                            [0] => name
                            [1] => email
                            [2] => password
                            [3] => user_group_id
                            [4] => username
                            [5] => facebook_page_id
                            [6] => first_name
                            [7] => last_name
                            [8] => email_verified
                            [9] => active
                            [10] => mobile
                            [11] => user_type
                            [12] => alternate_password
                            [13] => salt
                            [14] => email_verification_token
                            [15] => parent_id
                        )

                    [hidden:protected] => Array
                        (
                            [0] => password
                            [1] => remember_token
                        )

                    [casts:protected] => Array
                        (
                            [email_verified_at] => datetime
                        )

                    [connection:protected] => mysql
                    [table:protected] => users
                    [primaryKey:protected] => id
                    [keyType:protected] => int
                    [incrementing] => 1
                    [with:protected] => Array
                        (
                        )

                    [withCount:protected] => Array
                        (
                        )

                    [perPage:protected] => 15
                    [exists] => 1
                    [wasRecentlyCreated] => 
                    [attributes:protected] => Array
                        (
                            [id] => 1
                            [email] => admin@admin.com
                        )

                    [original:protected] => Array
                        (
                            [id] => 1
                            [email] => admin@admin.com
                        )

                    [changes:protected] => Array
                        (
                        )

                    [dates:protected] => Array
                        (
                        )

                    [dateFormat:protected] => 
                    [appends:protected] => Array
                        (
                        )

                    [dispatchesEvents:protected] => Array
                        (
                        )

                    [observables:protected] => Array
                        (
                        )

                    [relations:protected] => Array
                        (
                        )

                    [touches:protected] => Array
                        (
                        )

                    [timestamps] => 1
                    [visible:protected] => Array
                        (
                        )

                    [guarded:protected] => Array
                        (
                            [0] => *
                        )

                    [rememberTokenName:protected] => remember_token
                )
)
于 2020-01-10T07:09:51.330 回答