[英]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
是否可以使用变量来调用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;
}
}
试试这段代码
$property = $contact->phone;
echo $person->$property;
我认为这是一件坏事,因为它导致无法读取的代码在其他级别上也是完全错误的,但是通常如果你需要在对象语法中包含变量,你应该将它包装在大括号中以便首先解析它。
例如:
$property = 'contact->phone';
echo $person->{$property};
如果您需要访问名称中包含不允许的字符的对象,这种情况同样适用于SimpleXML对象会定期发生的情况。
$xml->{a-disallowed-field}
如果它是合法的,那并不意味着它也是道德的。 这是PHP的主要问题,是的,你可以做任何你能想到的事情,但这并没有使它正确。 看一下demeter法则:
如果你真的想要尝试这个: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;
}
}
如果您知道这两个函数的名称,可以这样做吗? (未测试)
$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
}
如果它不总是两个功能,你可以更多地破解它以使其工作。 要点是,只要使用括号格式,就可以使用变量变量调用链式函数。
OOP非常关注物体内部与外界的隔离。 你在这里尝试做的是提供一种通过person
界面宣传phone
内部的方法。 那不好。
如果你想要一个方便的方法来获取“all”属性,你可能想要为它编写一组明确的便利函数,如果你愿意,也可以包装在另一个类中。 这样,您可以发展支持的实用程序,而无需触摸(并可能破坏)核心数据结构:
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'];
在大多数嵌套内部对象的情况下,可能是重新评估数据结构的好时机。
在上面的例子中, 人有联系和dob 。 该联系人还包含地址 。 在编写复杂的数据库应用程序时,尝试从最高级别访问数据并不罕见。 但是,您可能会发现最好的解决方案是将数据合并到person类中,而不是尝试将其“挖掘”到内部对象中。
尽管我讨厌说,你可以做一个评估:
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;
(在键盘上找到它)
我所知道的最简单,最干净的方式。
function getValueByPath($obj,$path) {
return eval('return $obj->'.$path.';');
}
用法
echo getValueByPath($person,'contact->email');
// Returns the value of that object path
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.