提问人:Matt Humphrey 提问时间:3/21/2011 最后编辑:Matt Humphrey 更新时间:8/17/2016 访问量:4201
PHP:变量中的类属性链接
PHP: Class property chaining in variable variables
问:
因此,我有一个结构类似于下面的对象,所有这些对象都作为对象返回给我stdClass
$person->contact->phone;
$person->contact->email;
$person->contact->address->line_1;
$person->contact->address->line_2;
$person->dob->day;
$person->dob->month;
$person->dob->year;
$album->name;
$album->image->height;
$album->image->width;
$album->artist->name;
$album->artist->id;
等。。。(请注意,这些示例未链接在一起)。
是否可以使用变量变量作为 的直接属性调用?contact->phone
$person
例如:
$property = 'contact->phone';
echo $person->$property;
这不会按原样工作并抛出一个,所以我正在尝试找出一种替代方法来实现这一目标。E_NOTICE
有什么想法吗?
针对与代理方法相关的答案:
除了这个对象来自一个库,并且正在使用它来填充一个新对象与数组映射,如下所示:
array(
'contactPhone' => 'contact->phone',
'contactEmail' => 'contact->email'
);
然后通过地图填充新对象。我想我可以用测绘器来代替......
答:
如果我是你,我会创建一个简单的方法,返回->property();
$this->contact->phone
评论
OOP 主要是将物体的内部与外界隔离开来。您在这里尝试做的是提供一种通过接口公开内部的方法。这可不好。phone
person
如果你想要一种方便的方式来获取“所有”属性,你可能想要为此编写一组显式的方便函数,如果你愿意,可以包装在另一个类中。这样,您就可以发展支持的实用程序,而无需触及(并可能破坏)核心数据结构:
class conv {
static function phone( $person ) {
return $person->contact->phone;
}
}
// imagine getting a Person from db
$person = getpersonfromDB();
print conv::phone( $p );
如果您需要更专业的功能,请将其添加到实用程序中。恕我直言,这是不错的解决方案:将便利性与核心分开,以降低复杂性,并提高可维护性/可理解性。
另一种方法是围绕核心类的内部构建,为班级提供便利性:Person
class ConvPerson extends Person {
function __construct( $person ) {
Person::__construct( $person->contact, $person->name, ... );
}
function phone() { return $this->contact->phone; }
}
// imagine getting a Person from db
$person = getpersonfromDB();
$p=new ConvPerson( $person );
print $p->phone();
评论
您可以使用类型转换将对象更改为数组。
$person = (array) $person;
echo $person['contact']['phone'];
试试这段代码
$property = $contact->phone;
echo $person->$property;
评论
$contact = $person->contact; echo $contact->phone
$person
$contact->phone
我认为这是一件坏事,因为它会导致代码不可读,在其他级别上也是错误的,但一般来说,如果您需要在对象语法中包含变量,则应将其包装在大括号中,以便首先对其进行解析。
例如:
$property = 'contact->phone';
echo $person->{$property};
如果需要访问名称中包含已取消字符的对象,这同样适用,这通常发生在 SimpleXML 对象中。
$xml->{a-disallowed-field}
评论
是否可以使用变量变量将 contact->phone 作为 $person 的直接属性调用?
不能将表达式用作变量变量名称。
但你总是可以作弊:
class xyz {
function __get($name) {
if (strpos($name, "->")) {
foreach (explode("->", $name) as $name) {
$var = isset($var) ? $var->$name : $this->$name;
}
return $var;
}
else return $this->$name;
}
}
评论
在大多数情况下,如果嵌套了内部对象,则可能是重新评估数据结构的好时机。
在上面的例子中,人有联系人和出生日期。联系人还包含地址。在编写复杂的数据库应用程序时,尝试从最高级别访问数据的情况并不少见。但是,您可能会发现,最好的解决方案是将数据合并到 person 类中,而不是尝试实质上“挖掘”到内部对象中。
如果它是合法的,并不意味着它也是道德的。这是PHP的主要问题,是的,你几乎可以做任何你能想到的事情,但这并不能使它正确。看看得墨忒耳定律:
如果您真的非常想要,请尝试以下操作: json_decode(json_encode($person),true);
您将能够将其解析为数组而不是对象,但它可以为您的工作,而不是为设置。
编辑:
class Adapter {
public static function adapt($data,$type) {
$vars = get_class_vars($type);
if(class_exists($type)) {
$adaptedData = new $type();
} else {
print_R($data);
throw new Exception("Class ".$type." does not exist for data ".$data);
}
$vars = array_keys($vars);
foreach($vars as $v) {
if($v) {
if(is_object($data->$v)) {
// I store the $type inside the object
$adaptedData->$v = Adapter::adapt($data->$v,$data->$v->type);
} else {
$adaptedData->$v = $data->$v;
}
}
}
return $adaptedData;
}
}
评论
尽管我讨厌这样说,但你可以做一个评估:
foreach ($properties as $property) {
echo eval("return \$person->$property;");
}
评论
除了制作之外,您还可以制作一个神奇的方法,该方法可以查看请求字段的内部对象。请记住,魔法方法有点慢。function getPhone(){return $this->contact->phone;}
class Person {
private $fields = array();
//...
public function __get($name) {
if (empty($this->fields)) {
$this->fields = get_class_vars(__CLASS__);
}
//Cycle through properties and see if one of them contains requested field:
foreach ($this->fields as $propName => $default) {
if (is_object($this->$propName) && isset($this->$propName->$name)) {
return $this->$propName->$name;
}
}
return NULL;
//Or any other error handling
}
}
我决定放弃这整个方法,转而采用更啰嗦但更干净、很可能更有效率的方法。我一开始并不太热衷于这个想法,大多数人都在这里发言,为我下定决心。感谢您的回答。
编辑:
如果您有兴趣:
public function __construct($data)
{
$this->_raw = $data;
}
public function getContactPhone()
{
return $this->contact->phone;
}
public function __get($name)
{
if (isset($this->$name)) {
return $this->$name;
}
if (isset($this->_raw->$name)) {
return $this->_raw->$name;
}
return null;
}
如果您以类似结构的方式使用对象,则可以显式地对所请求节点的“路径”进行建模。然后,您可以使用相同的检索代码“装饰”您的对象。
“仅检索”装饰代码示例:
function retrieve( $obj, $path ) {
$element=$obj;
foreach( $path as $step ) {
$element=$element[$step];
}
return $element;
}
function decorate( $decos, &$object ) {
foreach( $decos as $name=>$path ) {
$object[$name]=retrieve($object,$path);
}
}
$o=array(
"id"=>array("name"=>"Ben","surname"=>"Taylor"),
"contact"=>array( "phone"=>"0101010" )
);
$decorations=array(
"phone"=>array("contact","phone"),
"name"=>array("id","name")
);
// this is where the action is
decorate( $decorations, &$o);
print $o->name;
print $o->phone;
(在 CodePad 上找到它)
我所知道的最简单,最干净的方式。
function getValueByPath($obj,$path) {
return eval('return $obj->'.$path.';');
}
用法
echo getValueByPath($person,'contact->email');
// Returns the value of that object path
如果你知道这两个函数的名称,你能这样做吗?(未测试)
$a = [
'contactPhone' => 'contact->phone',
'contactEmail' => 'contact->email'
];
foreach ($a as $name => $chain) {
$std = new stdClass();
list($f1, $f2) = explode('->', $chain);
echo $std->{$f1}()->{$f2}(); // This works
}
如果它并不总是两个功能,你可以更多地破解它以使其工作。关键是,只要使用括号格式,就可以使用变量变量调用链式函数。
上一个:从变量变量调用定义的常量
下一个:解析字符串中的变量
评论