这就是我想要做的事情:
$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的子类并将table参数设置为Products。
现在开心吗?
答案 0 :(得分:12)
它很有趣,实际上这是eval看起来不太好的几件事之一。
只要您可以确保没有用户输入将进入eval。
你仍然有缺点,例如当你使用字节码缓存时代码不会被缓存等等但是eval的安全问题几乎与在eval中输入用户输入或者在错误的范围内有关。 / p>
如果你知道自己在做什么,eval会帮助你解决这个问题。
那就是说,在我看来,当你不依赖于类型提示进行验证时,你会好得多,但你有一个功能
DoSomething_Validate($id)
{
// get_class($id) and other validation foo here
}
答案 1 :(得分:10)
我知道这是一个老问题,并且有些答案可以使用,但我想提供几个可以回答原始问题的片段,如果有人像我在搜索时那样结束,我认为提供更广泛的解决方案为了解决这个问题。
创建单个动态类
<?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中正在发生的事情,安全风险就不是问题。但是 - 如果你对它有足够深入的思考,那么很可能是一个更有意义的解决方案。我认为我有动态创建新类的完美用例。仔细研究这个问题就证明了这一点。
一个可能的解决方案 - 使用stdClass创建只是数据容器的对象,而不需要任何方法。
另外 - 如上所述,您可以使用脚本手动生成许多类。在镜像数据库表的类的情况下,您可以使用我上面使用的相同逻辑,而不是执行eval,将该信息写入文件。
答案 2 :(得分:4)
我认为使用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();
完成代码后,你可以删除文件,我测试了它,它完美无缺。
答案 3 :(得分:3)
这几乎肯定是一个坏主意。
我认为您花时间创建一个可以为您创建类定义的脚本,而不是尝试在运行时执行此操作。
使用命令行签名的东西,如:
./generate_classes_from_db <host> <database> [tables] [output dir]
答案 4 :(得分:3)
使用eval()确实是一个坏主意。它打开了一个大的安全漏洞。只是不要使用它!
答案 5 :(得分: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();
这可能有助于在运行时创建一个类。 它还为未知方法创建了一个方法和一个catchall方法 但是最好在运行时创建对象,而不是类。
答案 6 :(得分:3)
从PHP 7.0开始,您只要具有一点点创造力并了解一些鲜为人知的PHP功能,就可以完全做到这一点,而不必求助于eval或动态创建脚本文件。您只需要使用anonymous classes和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是有原因的,在某些情况下可以有效使用它。如果您使用的是旧版本的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();
看看这有可能成为安全漏洞吗?黑客在两个bogusClass名称之间放置的任何内容将通过eval语句在您的服务器上运行。
如果您调整自动加载器以检查传入的类名(即执行preg_match以确保没有空格或特殊字符,并根据可接受的名称列表进行检查等),则可以消除这些风险,然后在这种情况下,使用eval可能会很好。如果您使用的是PHP 7或更高版本,我建议您使用上面的匿名类别名方法。
答案 7 :(得分:2)
请阅读其他所有关于这是一个非常非常糟糕的想法的答案。
一旦你理解了这一点,这里有一个关于你如何做的小演示,但不应该这样做。
<?php
$clname = "TestClass";
eval("class $clname{}; \$cls = new $clname();");
var_dump($cls);
答案 8 :(得分:2)
我们可以通过以下方式动态创建类实例
我在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
)
)
答案 9 :(得分:0)
我创建了一个包,该包可以动态创建类/接口/特征...并将它们存储到文件中 然后,您只需添加创建的文件即可使用您的课程