PHP:变量变量中的类属性链接

时间:2011-03-21 09:39:08

标签: php oop method-chaining 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'
);

然后通过地图预先填充新对象。我想我可以改为使用映射器......

14 个答案:

答案 0 :(得分:3)

如果我是你,我会创建一个返回->property();

的简单方法$this->contact->phone

答案 1 :(得分:3)

  

是否可以使用变量来调用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;
    }
}

答案 2 :(得分:2)

试试这段代码

$property = $contact->phone;
echo $person->$property;

答案 3 :(得分:2)

我认为这是一件坏事,因为它导致不可读的代码在其他级别上也是明显错误的,但一般来说如果你需要在对象语法中包含变量,你应该将它包装在大括号中以便它得到它首先解析。

例如:

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

如果您需要访问名称中包含不允许的字符的对象,这同样适用于SimpleXML对象。

$xml->{a-disallowed-field}

答案 4 :(得分:2)

如果它是合法的,那并不意味着它也是道德的。这是PHP的主要问题,是的,你可以做任何你能想到的事情,但这并没有使它正确。看一下demeter法则:

Law of Demeter

尝试这个,如果你真的想要: json_decode(json_encode($人),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;

  }

}

答案 5 :(得分:0)

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();

答案 6 :(得分:0)

您可以使用类型转换将对象更改为数组。

$person = (array) $person;

echo $person['contact']['phone'];

答案 7 :(得分:0)

在大多数嵌套内部对象的情况下,可能是重新评估数据结构的好时机。

在上面的示例中, person 联系人 dob 联系人还包含地址。在编写复杂的数据库应用程序时,尝试从最高级别访问数据并不罕见。但是,您可能会发现,最好的解决方案是将数据合并到 person 类中,而不是尝试基本上“挖掘”内部对象。

答案 8 :(得分:0)

尽管我讨厌说,但你可以做一个评估:

foreach ($properties as $property) {
     echo eval("return \$person->$property;");
}

答案 9 :(得分:0)

除了制作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
    }
}

答案 10 :(得分:0)

我已经决定废弃这整个方法,并采用更长篇幅但更清洁,更有效的方式。我一开始并不是太热衷于这个想法,而且大多数人都在这里说过我的想法。谢谢你的回答。

编辑:

如果您有兴趣:

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

答案 11 :(得分:0)

如果您以类似结构的方式使用对象,则可以显式地为所请求的节点建模“路径”。然后,您可以使用相同的检索代码“装饰”对象。

“仅检索”装饰代码的示例:

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上找到)

答案 12 :(得分:0)

如果您知道这两个功能的名称,可以这样做吗? (未经测试)

Get Info

如果它不总是两个功能,你可以更多地破解它以使其工作。重点是,只要使用括号格式,就可以使用变量调用链式函数。

答案 13 :(得分:-1)

我所知道的最简单,最干净的方式。

LoadModule rewrite_module modules/mod_rewrite.so

用法

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