在php中运行时动态生成类?

在php中运行时动态生成类?,php,code-generation,proxy-classes,dynamic-compilation,Php,Code Generation,Proxy Classes,Dynamic Compilation,以下是我想做的: $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 } 显然这不是我实际要做的,但基本上我有一个类的未知名称,并且基于这个名称,我想生成具有某些属性的类等等 我尝试过使

以下是我想做的:

$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(),但它比private和$this->references更适合我

//编辑

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

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

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){ }
其中_自动加载将生成vPrimaryKey的子类,并将表参数设置为Products


现在开心吗?

这几乎肯定是个坏主意

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

具有命令行签名的内容,如:

./generate_classes_from_db <host> <database> [tables] [output dir]
/generate\u classes\u from\u db[tables][output dir]

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

请阅读其他人关于这是一个多么糟糕的想法的答案

一旦您了解了这一点,这里有一个小演示,说明您如何可以但不应该这样做



很有趣,事实上,这是为数不多的eval看起来不是个坏主意的事情之一

只要您能确保没有用户输入将进入评估

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

如果你知道你在做什么,eval会帮你的

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

DoSomething_Validate($id)
{ 
   // get_class($id) and other validation foo here
}
函数自动加载($class){
$code=“class$class{`
公共功能运行(){
回显“$class
”; } ".' 公共函数调用($name,$args){ $args=内爆(“,”,$args); echo“$name($args)
”; } }'; eval($代码); } $app=new Klasse(); $app->run(); $app->HelloWorld();
这可能有助于在运行时创建类。 它还为未知方法创建methor run和catchall方法
但是最好是在运行时创建对象,而不是类。

我知道这是一个老问题,有一些答案可以解决,但我想提供一些片段来回答最初的问题,我想如果有人像我在搜索这个问题的答案时那样出现在这里,我会提供一个更扩展的解决方案

创建单个动态类

<?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);
    }
}

?>
我认为使用
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();

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


软件包:

从PHP7.0开始,只要对一些鲜为人知的PHP特性有一点创造性和知识,就完全可以做到这一点,而无需求助于eval或动态创建脚本文件。您只需要使用and(),如下所示:

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使用这种语言是有原因的,在某些情况下它可以被有效地使用。如果您使用的是旧版本的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();
黑客可以这样做:

spl_autoload_register(function ($unfoundClassName) {
{
    eval("class $unfoundClassName {}");
}
$injectionCode = "bogusClass1{} /*insert malicious code to run on the server here */ bogusClass2";

new $injectionCode();
看看这怎么可能成为一个安全漏洞?黑客在两个BoguClass名称之间放置的任何内容都将通过eval语句在您的服务器上运行


如果您调整自动加载器以检查传入的类名(即,进行预匹配以确保没有空格或特殊字符,对照可接受名称列表进行检查等),您可以消除这些风险,然后在这种情况下使用eval可能完全可以。如果您使用的是PHP7或更高版本,我建议您使用上面的匿名类别名方法。

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

我在Laravel5.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
                )
)

我建议你告诉我们你到底想做什么,并问我们如何做得更好。你所尝试的不是正确的方法。我对这一次投了更高的票,所以没有投反对票。这是一个有效的问题,即使这是一个坏主意。谢谢你的澄清。这有更好的答案