How to convert (cast) Object to Array without Class Name prefix in PHP?
class Teste{
private $a;
private $b;
function __construct($a, $b) {
$this->a = $a;
$this->b = $b;
}
}
var_dump((array)(new Teste('foo','bar')));
Result:
array
'�Teste�a' => string 'foo' (length=3)
'�Teste�b' => string 'bar' (length=3)
Expected:
array (
a => 'foo'
b => 'bar' )
From the manual:
If an object is converted to an array, the result is an array whose elements are the object's properties. The keys are the member variable names, with a few notable exceptions: integer properties are unaccessible; private variables have the class name prepended to the variable name; protected variables have a '*' prepended to the variable name. These prepended values have null bytes on either side. This can result in some unexpected behaviour:
You can therefore work around the issue like this:
$temp = (array)(new Teste('foo','bar'));
$array = array();
foreach ($temp as $k => $v) {
$k = preg_match('/^\x00(?:.*?)\x00(.+)/', $k, $matches) ? $matches[1] : $k;
$array[$k] = $v;
}
var_dump($array);
It does seem odd that there is no way to control/disable this behaviour, since there is no risk of collisions.
The "class name prefix" is part of the (internal) name of the property. Because you declared both as private PHP needs something to distinguish this from properties $a and $b of any subclass.
The easiest way to bypass it: Don't make them private. You can declare them as protected instead.
However, this isn't a solution in every case, because usually one declares something as private with an intention. I recommend to implement a method, that makes the conversion for you. This gives you even more control on how the resulting array looks like
public function toArray() {
return array(
'a' => $this->a,
'b' => $this->b
);
}
As far as I know, PHP doesn't have a simple way to do what you want. Most languages don't. You should look into reflection. Take a look at this document: http://www.php.net/manual/en/reflectionclass.getproperties.php
I've made a function that should work as expected:
function objectToArr($obj)
{
$result = array();
ReflectionClass $cls = new ReflectionClass($obj);
$props = $cls->getProperties();
foreach ($props as $prop)
{
$result[$prop->getName()] = $prop->getValue($obj);
}
}
You can use Reflection to solve this task. But as usual this is a strong indicator that your class design is somewhat broken. However:
function objectToArray($obj) {
// Create a reflection object
$refl = new ReflectionClass($obj);
// Retrieve the properties and strip the ReflectionProperty objects down
// to their values, accessing even private members.
return array_map(function($prop) use ($obj) {
$prop->setAccessible(true);
return $prop->getValue($obj);
}, $refl->getProperties());
}
// Usage:
$arr = objectToArray( new Foo() );
Related
class Util_Model
{
/**
* Get model property by property name chain.
* Usage: Util_Model::get_prop($order, 'item', 'name')
*/
public static function get_prop()
{
$obj = func_get_arg(0);
$props = array_slice(func_get_args(), 1);
if (!is_object($obj)) {
throw new \InvalidArgumentException('First parameter must be an object');
}
foreach ($props as $prop) {
if (preg_match('/^(.*)\(\)$/', $prop, $matches)) {
$obj = call_user_func(array($obj, $matches[1]));
} else {
$obj = $obj->{$prop};
}
if (!is_object($obj)) {
break;
}
}
return is_object($obj) ? (string)$obj : $obj;
}
}
$obj->{$prop} i wonder the meaning of this line, why there is a brace here? and why there is no error when {$prop} is null.if you don't understand my question, leave something I will amend it.thanks!
$obj->{$prop} means that $obj is trying to access a property whose name is present in the variable $prop. I'll explain with an example
class A {
public $d;
public $e;
public $f;
function X() {}
function Y() {}
function Z() {}
}
$obj = new A();
$prop = 'X';
$propVar = 'f';
$obj->{$prop}();
$obj->{$propVar};
In the above code, $prop contains the value 'X', so function X will be invoked, likewise if it was containing values 'Y' or 'Z', they would be invoked. So the invocation of function can be decided at runtime depending on the value the variable contains.
As for the case when $prop is null, no object is being accessed, so the reference of object is returned instead and no error is thrown.
It's the preferred syntax when using Variable Variables when accessing object properties. In this example, the brackets aren't required, though. Their main purpose is to avoid ambiguity:
$obj = (object) array(
'foo' => array('key' => 123)
);
$access = array('key' => 'foo');
var_dump($obj->{$access['key']});//will dump array(key => 123)
var_dump($obj->$access['key']);//ambiguous
The latter is ambiguous, because PHP might take the statement to mean "convert $access to its string value (which is Array), access the property with that name, and get the index key from that value", or it could mean, access the property with the name you find under $access['key'].
Either way, this can be useful, your code is taking it too far. You calling methods like this is not a good idea.
A valid use-case could be when dealing with JSON encoded data, or a parsed XML DOM, where there are numeric keys, or keys like foo-bar, you can't write:
$obj->123;
$obj->foo-bar;//- is invalid
For these cases, you use the variable variable notation:
$keys = [123, 'foo-bar'];
foreach ($keys as $key)
echo $obj->{$key}, PHP_EOL;
So I'm working with a bag class, and I'm trying to dynamically create variables from the bag class.
Here's what I have:
class BagFoo
{
public $a;
public $b;
public $c;
}
class Bar
{
private $bagFoo;
public function output()
{
foreach($this->bagFoo as $key => $value)
{
$$key = $value;
}
//output then happens here.
}
}
This allows me to call $a instead of $this->bagFoo->getA(); which I rather like, but the problem is I have to expose the member variables to implement it. I'd like to have the same dynamic variable assignment, but access the member variables through a getter, instead of accessing directly.
Solutions I've though of and didn't really like:
Having a getVars() function in BagFoo that would return an array of var names and their values, and then iterating through that.
Calling get_class_methods() and then doing parsing and iterating through the getters (ew).
I'm sure there's a way to do what I'm trying in a more elegant form, but I just can't think of how to implement it.
Your code would probably be more understandable if you just used an associative array to store your values.
class Bar
{
private $bagFoo = [];
public function __construct($arr)
{
$this->bagFoo = $arr;
foreach($arr as $key => $value)
{
$$key = $value;
}
echo $a; //echos 'aaaa'
}
}
$bar = new Bar([
'a' => 'aaaa',
'b' => 'bbbb',
'c' => 'cccc'
]);
Word of advice: Be very careful using $$ because you can overwrite variables in the current scope and cause all kinds of problems in your application. For example:
$bar = new Bar([
'_SERVER' => 'broken server vars!',
'_COOKIE' => 'broken cookies!',
'arr' => 'broken iterator!',
]);
How do I pass a reference to an object constructor, and allow that object to update that reference?
class A{
private $data;
function __construct(&$d){
$this->data = $d;
}
function addData(){
$this->data["extra"]="stuff";
}
}
// Somewhere else
$arr = array("seed"=>"data");
$obj = new A($arr);
$obj->addData();
// I want $arr to contain ["seed"=>"data", "extra"=>"stuff"]
// Instead it only contains ["seed"=>"data"]
You must store it everywhere as a reference.
function __construct (&$d) {
$this->data = &$d; // the & here
}
You'll have to tell PHP to assign a reference also to the private member data like this:
$this->data = &$d;
Depending on the context, you may not want to use references to external arrays, and it might be better to have that array inside an object that handles it.
Aslo notice that the constructor is called __construct not __construction.
This would do what you are asking for:
class Test {
private $storage;
public function __construct(array &$storage)
{
$this->storage = &$storage;
}
public function fn()
{
$this->storage[0] *= 10;
}
}
$storage = [1];
$a = new Test($storage);
$b = new Test($storage);
$a->fn();
print_r($a); // $storage[0] is 10
print_r($b); // $storage[0] is 10
$b->fn();
print_r($a); // $storage[0] is 100
print_r($b); // $storage[0] is 100
Alternative 1
Instead of using an array, you can also use an ArrayObject, ArrayIterator or SplFixedArray. Since those are objects, they will be passed by reference. All of these implement ArrayAccess so you can access them via square brackets, e.g.
$arrayObject = new ArrayObject;
$arrayObject['foo'] = 'bar';
echo $arrayObject['foo']; // prints 'bar'
Alternative 2
Instead of using a generic type, use a dedicated type. Find out what you are storing in that array. Is it a Config? A Registry? A UnitOfWork? Find out what it really is. Then make it an object and give it an API reflecting the responsibilities. Then inject that object and access it through that API.
See this paper by Martin Fowler to some guidance on When To Make A Type
How do I pass a reference to an object constructor, and allow that object to update that reference?
class A{
private $data;
function __construct(&$d){
$this->data = $d;
}
function addData(){
$this->data["extra"]="stuff";
}
}
// Somewhere else
$arr = array("seed"=>"data");
$obj = new A($arr);
$obj->addData();
// I want $arr to contain ["seed"=>"data", "extra"=>"stuff"]
// Instead it only contains ["seed"=>"data"]
You must store it everywhere as a reference.
function __construct (&$d) {
$this->data = &$d; // the & here
}
You'll have to tell PHP to assign a reference also to the private member data like this:
$this->data = &$d;
Depending on the context, you may not want to use references to external arrays, and it might be better to have that array inside an object that handles it.
Aslo notice that the constructor is called __construct not __construction.
This would do what you are asking for:
class Test {
private $storage;
public function __construct(array &$storage)
{
$this->storage = &$storage;
}
public function fn()
{
$this->storage[0] *= 10;
}
}
$storage = [1];
$a = new Test($storage);
$b = new Test($storage);
$a->fn();
print_r($a); // $storage[0] is 10
print_r($b); // $storage[0] is 10
$b->fn();
print_r($a); // $storage[0] is 100
print_r($b); // $storage[0] is 100
Alternative 1
Instead of using an array, you can also use an ArrayObject, ArrayIterator or SplFixedArray. Since those are objects, they will be passed by reference. All of these implement ArrayAccess so you can access them via square brackets, e.g.
$arrayObject = new ArrayObject;
$arrayObject['foo'] = 'bar';
echo $arrayObject['foo']; // prints 'bar'
Alternative 2
Instead of using a generic type, use a dedicated type. Find out what you are storing in that array. Is it a Config? A Registry? A UnitOfWork? Find out what it really is. Then make it an object and give it an API reflecting the responsibilities. Then inject that object and access it through that API.
See this paper by Martin Fowler to some guidance on When To Make A Type
What should I put into class to be able to do so?
$data = array(
'a' => 12345,
'b' => 67890,
);
$object = new Class($data);
echo $object->a; //outputs 12345
echo $object->b; //outputs 67890
Ignacio Vazquez-Abrams’s answer is nice, but I'd prefer a whitelist of attributes you would like to allow to be set this way:
foreach(array('attribute1', 'attribute2') as $attribute_name) {
if(array_key_exists($attribute_name, $data)) {
$this->$attribute_name = $data[$attribute_name];
}
}
This way you can make sure no private attributes are set.
Alternatively, if you're just after the object syntax, you can do:
$object = (object) $data;
$object = $data->a // = 12345
This is coercing the array into a StdClass.
class A {
private $data;
public function __get($key) {
return isset($this -> data[$key]) ? $this -> data[$key] : false;
}
public function __construct(array $data) {
$this -> data = $data;
}
}
foreach($arr as $key => $val)
{
this->$key = $val;
};
class MyClass {
private $a;
private $b;
public function __construct(array $arr) {
$this->a = $arr['a'];
$this->b = $arr['b'];
}
}
Depending on your UseCase, this might be an alternative to the given solutions as well:
class A extends ArrayObject {}
This way, all properties in the array will be accessible by object notation and array notation, e.g.
$a = new A( array('foo' => 'bar'), 2 ); // the 2 enables object notation
echo $a->foo;
echo $a['foo'];
As you can see by the class declaration, ArrayObject implements a number of interfaces that allow you to use the class like an Array:
Class [ <internal:SPL> <iterateable> class ArrayObject
implements IteratorAggregate, Traversable, ArrayAccess, Countable ]
It also adds a number of methods to complete array access capabilities. Like said in the beginning, this might not be desirable in your UseCase, e.g. when you don't want the class to behave like an array.