根据字符串动态创建PHP对象

51

我想在PHP中创建一个对象,该对象的类型是由MySQL数据库中的字符串定义的。 数据库表格具有以下列和示例数据:

 id | type | propertyVal
----+------+-------------
  1 | foo  | lorum
  2 | bar  | ipsum

...使用PHP数据类型

class ParentClass {...}
class Foo extends ParentClass {private $id, $propertyVal; ...}
class Bar extends ParentClass {private $id, $propertyVal; ...} 
//...(more classes)...

我希望只使用一条查询语句,按照id选择一行,并创建一个对象,该对象类型由表的类型列定义,将SELECTed行中的其他列分配给新创建的对象。

我正在考虑使用:

  1. mysql_fetch_object()
  2. 读取类型属性
  3. 使用类型属性定义创建一个对象

但是我不知道如何根据字符串动态创建类型。如何做到这一点?

5个回答

117
但是我不知道有什么方法可以基于字符串动态创建类型。如何实现呢?
您可以很轻松自然地完成这个任务:
$type = 'myclass';

$instance = new $type;

如果您的查询返回一个关联数组,您可以使用类似的语法来分配属性:
// build object
$type = $row['type'];
$instance = new $type;

// remove 'type' so we don't set $instance->type = 'foo' or 'bar'
unset($row['type']);  

// assign properties
foreach ($row as $property => $value) {
   $instance->$property = $value;
}

33
您可以将参数传递给构造函数: $instance = new $type(5, 'hi'); - goat
注意,className必须是一个完整路径、有效的类地址,例如: $type = "app\models\MyClass"; - Em.MF

10

有一个非常简洁的语法,我几个月前学到的,它不依赖于临时变量。这里是一个示例,我使用一个POST变量来加载特定的类:

$eb = new ${!${''} = $_POST['entity'] . 'Binding'}();

在你的具体情况中,你可以通过使用PDO来解决它。它有一个提取模式,可以将第一列的值作为实例化行的类。

$sth->fetch(PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE);

我不明白,显然你会得到 $eb= new ${FALSE}();,或者我漏掉了什么? - FrancescoMM
这个 hack 能够生效的原因是 ${false} 会被解析为 ${''}(相当于写成 (string)false),而拥有名称 [empty string] 的变量就是我们刚刚在内部括号子句中创建的那个变量。非常巧妙。 - silkfire
1
这种语法叫什么名字?我在哪里可以阅读更多关于它的信息?因为我还没有理解它。 - algo

8
$instance = new $classname; // i.e. $type in your case

非常好用...


但是如何添加构造函数的参数呢?这样写是正确的吗:$instance = new $classname($parameterA, $parameterB); - algo

2
以下是我在查看此主题时所寻找的内容。使用{"objectName"}(括号)以字符串形式声明或引用对象名称。
$gameData = new stdClass();
$gameData->location = new stdClass();
$basementstring = "basement";

class tLocation {
    public $description;
}

$gameData->location->{'darkHouse'} = new tLocation;
$gameData->location->{"darkHouse"}->description = "You walkinto a dusty old house";


$gameData->location->{$basementstring} = new tLocation;
$gameData->location->{"basement"}->description = "its really damp down here.";

//var_dump($gameData); 
echo $gameData->location->basement->description;

这种引用对象的方式似乎是可以互换的。我找不到答案,所以只能试着摸索,直到找到一种方法。


0

如silkfire所说,这可以通过使用PDO特定模式来实现,下面是一个例子。使用相同的数据库值和已定义的对象:

 id | type | propertyVal
----+------+-------------
  1 | foo  | lorum
  2 | bar  | ipsum
class ParentClass {...} class Foo extends ParentClass {private $id, $propertyVal; ...} class Bar extends ParentClass {private $id, $propertyVal; ...} //...(更多类)...

使用单一查询(必须先命名包含类名称的字段):

$stmt = $db->prepare('SELECT type,id,propertyVal FROM table WHERE id=1');
$stmt->execute();
$foo = $stmt->fetch(PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE);
var_dump($foo); // $foo is a newly created object of class foo, with properties named like and containing the value of subsequent fields

这很酷,但随着时间的推移会变得更酷

$stmt = $db->prepare('SELECT type,id,propertyVal FROM table');
$stmt->execute();
while ($object = $stmt->fetch(PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE))
 {var_dump($object);} // here all desired objects, dynamically constructed accordingly to the first column returned by the query

你可以定义一个构造函数(在从数据库中获取的值被分配给属性后将被调用),以便处理那些动态分配的属性,例如通过将字符串替换为其大写值。

class foo
 {function __construct ()
   {$this->uper = strtoupper($this->propertyVal);}}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接