PHP: цепочка свойств класса в переменных переменных - PullRequest
3 голосов
/ 21 марта 2011

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

и затем прохождение по карте для заполнения нового объекта. Полагаю, я мог бы вместо этого использовать Картограф ...

Ответы [ 14 ]

3 голосов
/ 21 марта 2011

Можно ли использовать переменные переменные для вызова 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;
    }
}
3 голосов
/ 21 марта 2011

Если бы я был тобой, я бы создал простой метод ->property();, который возвращает $this->contact->phone

2 голосов
/ 21 марта 2011

Если это законно, это не значит, что это также морально. И это главная проблема с PHP, да, вы можете делать почти все, что только можете придумать, но это не делает это правильно. Посмотрите на закон Деметры:

Закон Деметры

попробуйте это, если вы действительно хотите: json_decode (json_encode ($ человек), правда);

вы сможете проанализировать его как массив, а не как объект, но он сделает вашу работу, чтобы получить не настройку.

EDIT:

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;

  }

}

2 голосов
/ 21 марта 2011

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

Например:

$property = 'contact->phone';
echo $person->{$property};

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

$xml->{a-disallowed-field}
2 голосов
/ 21 марта 2011

попробуйте этот код

$property = $contact->phone;
echo $person->$property;
0 голосов
/ 16 августа 2016

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

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

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

0 голосов
/ 24 февраля 2016

Самый простой и понятный мне способ.

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

Использование

echo getValueByPath($person,'contact->email');
// Returns the value of that object path
0 голосов
/ 21 марта 2011

Если вы используете свой объект в структурном стиле, вы можете явно смоделировать «путь» к запрошенному узлу.Затем вы можете «украсить» ваши объекты одним и тем же поисковым кодом.

Пример кода «только для поиска»:

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 )

0 голосов
/ 21 марта 2011

Я решил отказаться от всего этого подхода и перейти к более многословному, но более чистому и, скорее всего, более эффективному.Во-первых, я не слишком увлекался этой идеей, и большинство высказалось здесь, чтобы принять решение.Спасибо за ваши ответы.

Редактировать:

Если вы заинтересованы:

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;
}
0 голосов
/ 21 марта 2011

Помимо создания 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
    }
}
...