PHP: Class property chaining in variable variables

2019-05-16 10:24发布

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...

14条回答
Ridiculous、
2楼-- · 2019-05-16 11:23

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();
查看更多
唯我独甜
3楼-- · 2019-05-16 11:23

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)

查看更多
登录 后发表回答