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

发布于 2024-10-24 11:30:28 字数 973 浏览 2 评论 0原文

因此,我有一个结构类似于下面的对象,所有这些对象都作为 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'
);

然后遍历映射以填充新对象。我想我可以用映射器代替......

So, I have a object with structure similar to below, all of which are returned to me as stdClass objects

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

etc... (note these examples are not linked together).

Is it possible to use variable variables to call contact->phone as a direct property of $person?

For example:

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

This will not work as is and throws a E_NOTICE so I am trying to work out an alternative method to achieve this.

Any ideas?

In response to answers relating to proxy methods:

And I would except this object is from a library and am using it to populate a new object with an array map as follows:

array(
  'contactPhone' => 'contact->phone', 
  'contactEmail' => 'contact->email'
);

and then foreaching through the map to populate the new object. I guess I could envole the mapper instead...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

美煞众生 2024-10-31 11:30:28

如果我是你,我会创建一个简单的方法 ->property(); ,返回 $this->contact->phone

If i was you I would create a simple method ->property(); that returns $this->contact->phone

转瞬即逝 2024-10-31 11:30:28

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

Is it possible to use variable variables to call contact->phone as a direct property of $person?

It's not possible to use expressions as variable variable names.

But you can always cheat:

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;
    }
}
风吹雨成花 2024-10-31 11:30:28

试试这个代码

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

try this code

$property = $contact->phone;
echo $person->$property;
梦里寻她 2024-10-31 11:30:28

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

例如:

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

如果您需要访问名称中含有禁止字符的对象,则同样适用,这种情况在 SimpleXML 对象中经常发生。

$xml->{a-disallowed-field}

I think this is a bad thing to to as it leads to unreadable code is is plain wrong on other levels too, but in general if you need to include variables in the object syntax you should wrap it in braces so that it gets parsed first.

For example:

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

The same applies if you need to access an object that has disalowed characters in the name which can happen with SimpleXML objects regularly.

$xml->{a-disallowed-field}
殊姿 2024-10-31 11:30:28

如果它是合法的,并不意味着它也是道德的。这是 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;

  }

}

If it is legal it does not mean it is also moral. And this is the main issue with PHP, yes, you can do almost whatever you can think of, but that does not make it right. Take a look at the law of demeter:

Law of Demeter

try this if you really really want to:
json_decode(json_encode($person),true);

you will be able to parse it as an array not an object but it does your job for the getting not for the setting.

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;

  }

}

巴黎夜雨 2024-10-31 11:30:28

OOP 主要是保护对象的内部不受外界影响。您在这里尝试做的是提供一种通过 person 界面公开 phone 内部结构的方法。这不太好。

如果您想要一种方便的方法来获取“所有”属性,您可能需要为此编写一组显式的便利函数,如果您愿意,可以将其包装在另一个类中。这样,您就可以发展支持的实用程序,而不必触及(并可能破坏)核心数据结构:

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

OOP is much about shielding the object's internals from the outside world. What you try to do here is provide a way to publicize the innards of the phone through the person interface. That's not nice.

If you want a convenient way to get "all" the properties, you may want to write an explicit set of convenience functions for that, maybe wrapped in another class if you like. That way you can evolve the supported utilities without having to touch (and possibly break) the core data structures:

class conv {
 static function phone( $person ) {
   return $person->contact->phone;
 }

}

// imagine getting a Person from db
$person = getpersonfromDB();

print conv::phone( $p );

If ever you need a more specialized function, you add it to the utilities. This is imho the nices solution: separate the convenience from the core to decrease complexity, and increase maintainability/understandability.

Another way is to 'extend' the Person class with conveniences, built around the core class' innards:

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();
蓦然回首 2024-10-31 11:30:28

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

$person = (array) $person;

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

You could use type casting to change the object to an array.

$person = (array) $person;

echo $person['contact']['phone'];
神经暖 2024-10-31 11:30:28

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

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

In most cases where you have nested internal objects, it might be a good time to re-evaluate your data structures.

In the example above, person has contact and dob. The contact also contains address. Trying to access the data from the uppermost level is not uncommon when writing complex database applications. However, you might find your the best solution to this is to consolidate data up into the person class instead of trying to essentially "mine" into the internal objects.

墨落画卷 2024-10-31 11:30:28

尽管我讨厌这么说,但你可以进行 eval :

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

As much as I hate saying it, you could do an eval :

foreach ($properties as $property) {
     echo eval("return \$person->$property;");
}
清风疏影 2024-10-31 11:30:28

除了使用 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
    }
}

Besides making function getPhone(){return $this->contact->phone;} you could make a magic method that would look through internal objects for requested field. Do remember that magic methods are somewhat slow though.

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
    }
}
永不分离 2024-10-31 11:30:28

我决定放弃整个方法,转而采用更冗长但更干净、而且很可能更高效的方法。我一开始并不太热衷于这个想法,大多数人都在这里发表了意见,让我做出了决定。感谢您的回答。

编辑:

如果您有兴趣:

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

I have decided to scrap this whole approach and go with a more long-winded but cleaner and most probably more efficient. I wasn't too keen on this idea in the first place, and the majority has spoken on here to make my mind up for me. Thank for you for your answers.

Edit:

If you are interested:

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;
}
玻璃人 2024-10-31 11:30:28

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

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

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

In case you use your object in a struct-like way, you can model a 'path' to the requested node explicitly. You can then 'decorate' your objects with the same retrieval code.

An example of 'retrieval only' decoration code:

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;

(find it on codepad)

暖心男生 2024-10-31 11:30:28

如果你知道这两个函数的名称,你能做到吗? (未经测试)

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

如果它并不总是两个功能,您可以对其进行更多修改以使其工作。要点是,只要使用括号格式,您就可以使用可变变量调用链式函数。

If you know the two function's names, could you do this? (not tested)

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

If it's not always two functions, you could hack it more to make it work. Point is, you can call chained functions using variable variables, as long as you use the bracket format.

古镇旧梦 2024-10-31 11:30:28

我所知道的最简单、最干净的方法。

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

用法

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

Simplest and cleanest way I know of.

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

Usage

echo getValueByPath($person,'contact->email');
// Returns the value of that object path
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文