在 php 中在运行时动态生成类?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1203625/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Dynamically generate classes at runtime in php?
提问by Will Shaver
Here's what I want to do:
这是我想要做的:
$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
}
Obviously this isn't what I'm actually doing, but basically I have unknown names for a class and based on the name, I want to generate the class with certain properties etc.
显然这不是我实际在做的事情,但基本上我有一个未知的类名称,并且基于名称,我想生成具有某些属性等的类。
I've tried using eval() but it is giving me fits over private and $this-> references...
我试过使用 eval() 但它让我适合私人和 $this-> 引用...
//edit
//编辑
Ok, obviously my short and sweet "here's what I want to do" caused massive strife and consternation amongst those who may be able to provide answers. In the hope of getting an actual answer I'll be more detailed.
好吧,显然我简短而甜蜜的“这就是我想要做的”在那些可能能够提供答案的人中引起了巨大的冲突和惊愕。希望得到一个实际的答案,我会更详细。
I have a validation framework using code hints on the site I maintain. Each function has two definitions
我在我维护的网站上有一个使用代码提示的验证框架。每个函数有两个定义
function DoSomething($param, $param2){
//code
}
function DoSomething_Validate(vInteger $param, vFloat $param2){
//return what to do if validation fails
}
I'm looking to add a validator for primary keys in my database. I don't want to create a separate class for EVERY table (203). So my plan was to do something like
我希望为我的数据库中的主键添加一个验证器。我不想为每个表 (203) 创建一个单独的类。所以我的计划是做类似的事情
function DoSomething_Validate(vPrimaryKey_Products $id){ }
Where the __autoload would generate a subclass of vPrimaryKey and set the table parameter to Products.
__autoload 将生成 vPrimaryKey 的子类并将表参数设置为 Products。
Happy now?
现在开心?
采纳答案by hobodave
This is almost certainly a bad idea.
这几乎肯定是一个坏主意。
I think your time would be better spent creating a script that would create your class definitions for you, and not trying to do it at runtime.
我认为您最好花时间创建一个脚本来为您创建类定义,而不是尝试在运行时执行此操作。
Something with a command-line signature like:
带有命令行签名的东西,例如:
./generate_classes_from_db <host> <database> [tables] [output dir]
回答by foobaer
its funny, actually this is one of the few things where eval doesnt seem such a bad idea.
这很有趣,实际上这是 eval 似乎不是一个坏主意的少数事情之一。
as long as you can ensure that no user input will ever enter the eval.
只要你能确保没有用户输入会进入评估。
you still have downsides like when your using a bytecode cache that code wont be cached etc etc. but the security issues of eval are pretty much related to having user inputy in the eval, or to ending up in the wrong scope.
你仍然有缺点,比如当你使用字节码缓存时,代码不会被缓存等等。但是 eval 的安全问题与用户在 eval 中输入或结束在错误的范围内有很大关系。
if you know what you are doing, eval will help you with this.
如果您知道自己在做什么, eval 将帮助您解决这个问题。
That said, in my opinion you are much better off when you no rely on type-hinting for your validation, but you have one function
也就是说,在我看来,当您不依赖类型提示进行验证时,情况会好得多,但您只有一个功能
DoSomething_Validate($id)
{
// get_class($id) and other validation foo here
}
回答by akincer
I know this is an old question and there are answers that WILL work, but I wanted to offer a few snippets that would answer the original question and I think offer a more expanded solution should someone end up here like I did when searching for an answer to this problem.
我知道这是一个老问题,有些答案会奏效,但我想提供一些可以回答原始问题的片段,我认为如果有人像我在寻找答案时那样最终来到这里,我认为提供更广泛的解决方案到这个问题。
Create Single Dynamic Class
创建单个动态类
<?php
// Without properties
$myclassname = "anewclassname";
eval("class {$myclassname} { }";
// With a property
$myclassname = "anewclassname";
$myproperty = "newproperty";
eval("class {$myclassname} { protected ${$myproperty}; }";
?>
As long as you properly escape your text, you could also add a function in there.
只要您正确转义文本,您还可以在其中添加一个函数。
But what about if you want to dynamically create classes based on something that could itself be dynamic such as creating a class for each table in your database as the original question mentioned?
但是,如果您想根据本身可能是动态的内容动态创建类,例如为原始问题提到的数据库中的每个表创建一个类,该怎么办?
Create Multiple Dynamic Classes
创建多个动态类
<?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);
}
}
?>
This will dynamically generate a class for each table in a database. For each class, a property that is named after each column will ALSO get created.
这将为数据库中的每个表动态生成一个类。对于每个类,还将创建以每列命名的属性。
Now it's been pointed out you shouldn't do this. As long as you control what's going on in the eval, the risk of security isn't a problem. BUT -- there's most likely a solution that makes more sense if you think deeply enough about it. I thought I had the perfect use case for dynamically creating new classes. Careful examination of the problem proved otherwise.
现在有人指出你不应该这样做。只要您控制评估中发生的事情,安全风险就不是问题。但是——如果你足够深入地思考它,很可能有一个更有意义的解决方案。我以为我有动态创建新类的完美用例。对问题的仔细检查证明并非如此。
One potential solution -- use the stdClass for creating objects that are just data containers and don't need any methods.
一种潜在的解决方案——使用 stdClass 创建只是数据容器并且不需要任何方法的对象。
Also -- as mentioned, you could use a script to manually generate lots of classes. In the case of classes mirroring your database tables, you could use the same logic I have above and instead of doing an eval, write that info to a file.
此外 - 如前所述,您可以使用脚本手动生成大量类。在类镜像数据库表的情况下,您可以使用我上面的相同逻辑,而不是执行 eval,将该信息写入文件。
回答by dallin
As of PHP 7.0, with a little creativity and knowledge of some lesser known PHP features, you can absolutely do this without resorting to eval or creating script files dynamically. You just need to use anonymous classesand class_alias(), like such:
从 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
}
This works because anonymous classes are still assigned a name behind the scenes and put in the global scope, so you're free to grab that class name and alias it. Check out the second comment under the anonymous classes link above for more information.
这是有效的,因为匿名类仍然在幕后分配了一个名称并放在全局范围内,因此您可以自由地获取该类名并为其取别名。查看上面匿名类链接下的第二条评论以获取更多信息。
Having said that, I feel like all the people in this question who are saying "Eval is always a very bad idea. Just don't use it ever!" are just repeating what they've heard from the hive mind and not thinking for themselves. Eval is in the language for a reason, and there are situations where it can be used effectively. If you're on an older version of PHP, eval might be a good solution here.
话虽如此,我觉得这个问题中的所有人都在说“Eval 总是一个非常糟糕的主意。永远不要使用它!” 只是重复他们从蜂巢中听到的东西,而不是自己思考。Eval 出现在语言中是有原因的,并且在某些情况下可以有效地使用它。如果您使用的是旧版本的 PHP,则 eval 可能是一个很好的解决方案。
However, they are correct in that it canopen up very large security holes and you have to be careful how you use it and understand how to eliminate the risks. The important thing is, much like SQL injection, you have to sanitize any input you put in the eval statement.
但是,它们是正确的,因为它会打开非常大的安全漏洞,您必须小心使用它并了解如何消除风险。重要的是,就像 SQL 注入一样,您必须清理放入 eval 语句中的任何输入。
For example, if your autoloader looked like this:
例如,如果您的自动加载器如下所示:
spl_autoload_register(function ($unfoundClassName) {
{
eval("class $unfoundClassName {}");
}
A hacker could do something like this:
黑客可以这样做:
$injectionCode = "bogusClass1{} /*insert malicious code to run on the server here */ bogusClass2";
new $injectionCode();
See how this has the potential to be a security hole? Anythingthe hacker puts between the two bogusClass names will be run on your server by the eval statement.
看看这有可能成为安全漏洞吗?黑客在两个 bogusClass 名称之间放置的任何内容都将通过 eval 语句在您的服务器上运行。
If you adjust your autoloader to check the class name passed in (i.e. doing a preg_match to make sure there's no spaces or special characters, checking it against a list of acceptable names, etc.), you can eliminate these risks and then eval might be totally fine to use in this situation. If you're on PHP 7 or higher though, I recommend the anonymous class alias method above.
如果您调整自动加载器以检查传入的类名(即执行 preg_match 以确保没有空格或特殊字符,根据可接受的名称列表进行检查等),则可以消除这些风险,然后 eval 可能是在这种情况下完全可以使用。如果您使用的是 PHP 7 或更高版本,我建议您使用上面的匿名类别名方法。
回答by Waseem Abu Senjer
I think using eval()it's not a reliable solution, especially if your script or software will be distributed to different clients. Shared hosting providers always disable eval()function.
我认为使用eval()它不是一个可靠的解决方案,特别是如果您的脚本或软件将分发给不同的客户端。共享主机提供商总是禁用eval()功能。
I'm thinking of a better aproach like this :
我正在考虑这样的更好的方法:
<?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();
After finishing executing the code, you can delete the file if you want, I tested it and it works perfectly.
执行完代码后,您可以根据需要删除文件,我对其进行了测试,效果很好。
回答by Matthias
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();
This might help to create a class at runtime. It also creates a methor run and a catchall method for unknown methods But better create Objects at runtime, not classes.
这可能有助于在运行时创建一个类。它还为未知方法创建了一个methor run和一个catchall方法,但最好在运行时创建对象,而不是类。
回答by Henrik P. Hessel
Using eval() is really a bad idea. It opens a large security hole. Just don't use it!
使用 eval() 真的是一个坏主意。它打开了一个大的安全漏洞。只是不要使用它!
回答by MitMaro
Please read everyone else answers on how this is truly a very very bad idea.
请阅读其他人的答案,了解这真的是一个非常糟糕的主意。
Once you understand that, here is a small demo on how you could, but should not, do this.
一旦你理解了这一点,这里有一个关于你可以但不应该这样做的小演示。
<?php
$clname = "TestClass";
eval("class $clname{}; $cls = new $clname();");
var_dump($cls);
回答by Pawan Kumar
We can create class instance dynamically by following way
我们可以通过以下方式动态创建类实例
I also face this issue in Laravel 5.8 version and now it is working fine for me.
我在 Laravel 5.8 版本中也遇到了这个问题,现在它对我来说工作正常。
Give full path instead of the class Name
提供完整路径而不是类名称
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;
}
}
Output
输出
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] => [email protected]
)
[original:protected] => Array
(
[id] => 1
[email] => [email protected]
)
[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
)
)
回答by Adil El Kanabi
I have created a package that dynamically creates classes/interfaces/traits... and stores them into a file you can then just include the created file to be able to use your class
我创建了一个动态创建类/接口/特征的包......并将它们存储到一个文件中,然后您只需包含创建的文件即可使用您的类

