[英]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.