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

这是我想做的:

$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。

现在开心吗?


答案 1

从 PHP 7.0 开始,只要对一些鲜为人知的 PHP 功能有一点创造力和知识,您绝对可以做到这一点,而无需诉诸于评估或动态创建脚本文件。你只需要使用匿名类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或更高版本,我建议使用上面的匿名类别名方法。


答案 2

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

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

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

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

也就是说,在我看来,当你不依赖类型提示进行验证时,你会好得多,但是你有一个函数。

DoSomething_Validate($id)
{ 
   // get_class($id) and other validation foo here
}

推荐