PHP: цепочка свойств класса в переменных переменных

Итак, у меня есть объект со структурой, подобной ниже, все из которых возвращаются мне как объекты 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; по $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}; 

То же самое происходит, если вам нужно получить доступ к объекту, у которого disavowed символы в имени, которое может случиться с объектами SimpleXML регулярно.

 $xml->{a-disallowed-field} 

Если это законно, это не значит, что это тоже морально. И это основная проблема с 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; } 

}

ООП многое касается защиты внутренних объектов объекта от внешнего мира. То, что вы пытаетесь сделать здесь, это предоставить возможность публиковать внутренности phone через интерфейс пользователя. Это не приятно.

Если вам нужен удобный способ получить «все» свойства, вы можете написать явный набор удобных функций для этого, возможно, завернутый в другой класс, если хотите. Таким образом, вы можете развивать поддерживаемые утилиты, не затрагивая (и, возможно, нарушая) основные структуры данных:

 class conv { static function phone( $person ) { return $person->contact->phone; } } // imagine getting a Person from db $person = getpersonfromDB(); print conv::phone( $p ); 

Если вам нужна более специализированная функция, вы добавляете ее в утилиты. Это решение imho the nices: отделить удобство от ядра, чтобы уменьшить сложность, и повысить удобство обслуживания / понятность.

Другой способ – «расширить» класс 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; 

(найти его на кодовом коде )

Если вы знаете имена двух функций, не могли бы вы сделать это? (не испытано)

 $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 } 

Если это не всегда две функции, вы можете взломать ее больше, чтобы она работала. Точка, вы можете вызывать цепные функции с использованием переменных переменных, если вы используете формат скобок.

Самый простой и чистый способ, о котором я знаю.

 function getValueByPath($obj,$path) { return eval('return $obj->'.$path.';'); } 

Применение

 echo getValueByPath($person,'contact->email'); // Returns the value of that object path