I am currently in the development of my Class in PHP.
I have an array with values in it, and I would like to use the array fieldname as a $this reference. Let me show you what I got:
<?php
class Server {
private $playlist;
private $mp3;
private static $ressourceFolder;
private static $sudoUser;
And in my array it contains:
array(6) {
["playlist"]=>
int(8002)
["mp3"]=>
int(1024)
["ressourceFolder"]=>
bool(true)
["sudoUser"]=>
bool(true)
}
So I would like to use in my foreach something to get the value of the array field into the class global variable, the array fieldname is the same as the variable so this 'should' work, but it doesn't :(
foreach($ressourceArray as $ressourceField=>$ressourceValue) {
$this->$ressourceField = $ressourceValue;
}
I would really appreciate if someone could tell me why this can't work and how to make this 'workable'...
Thanks in advance!
It does work, see Demo:
<?php
$array = array("playlist"=> 8002, "mp3"=>1024);
class Mix {
public function __construct($array) {
foreach($array as $key => $value) {
$this->$key = $value;
}
}
}
$class = new Mix($array);
var_dump($class);
It will assign new public members to the object $this based on key/value pairs of the array.
Dealing with bad named keys in the array
If the keys contain values that are not valid variable names, it can be non-trivial to access the properties later ({property-name}), see PHP curly brace syntax for member variable.
Casting the array to object before adding will help to prevent fatal errors for those key names that are completely invalid:
$object = (object) $array;
# iterate over object instead of array:
foreach($object as $key => $value) {
$this->$key = $value;
}
Those keys are just dropped by the cast.
You may get it working with the magic method __set and __get. See: http://php.net/manual/en/language.oop5.magic.php
And in my array it contains:
What array? That looks like a array dump of an instance of the class.
into the class global variable
What global class variable? Classes are not variables. Variables may hold references to objects, or class names.
Assuming you want to iterate through the properties of an object, and
$ressourceArray = new Server();
The code will work as expected.
If the loop is within a class method, then the loop should be....
foreach($this as $ressourceField=>$ressourceValue) {
$this->$ressourceField = $ressourceValue;
}
If you mean that you are trying to initialize the object properties from an array...
class Server {
...
function setValues($ressourceArray)
{
foreach($ressourceArray as $ressourceField=>$ressourceValue) {
$this->$ressourceField = $ressourceValue;
}
}
(BTW there's only one 's' in 'resource')
Related
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() );
I'm still new to OOP and this is probably a simple question, not sure if I'm overthinking this.
Let's say we have a simple class like the following that we can use to instantiate an object that can generate an array:
class gen_arr {
public $arr = array();
public function fill_arr() {
$this->arr["key"] = "value";
}
}
// instantiate object from gen_arr
$obj = new gen_arr();
Now if you wanted to get the value of the object's array's item, would you generate an array first and then echo the value like:
$arr = $obj->fill_arr();
echo $arr["key"];
Or would you access the object's property directly?
echo $obj->arr["key"]
In the actual code the property is private and there is a method that allows the viewing of the property array, the above is just to simplify the question.
Are there performance considerations and/or just best practices when it comes to this kind of case?
UPDATE:
It's still unclear from the answers if the best way is to generate an array from the property and access that array or just access the property directly (through the getter method)
Since you are filling the array with items only on fill_arr, those items wont be availabl until you call $arr = $obj->fill_arr();.
If you want to directly call the array, then you have to fill this array on the constructor function of this call like this:
class gen_arr {
public $arr = array();
function __construct() {
$this->arr["key"] = "value";
}
}
First off, the class you shared with us has a range of problems:
its sole instance property is public and can be modified by anyone
you have some temporal coupling, the method fill_arr() needs to be invoked before accessing the the value makes any sense
Encapsulation
Reduce the visibility of the instance property from public to private, so that the property can only be modified by the object itself, and provide an accessor instead:
class gen_arr
{
private $arr;
public function fill_arr()
{
$this->arr["key"] = "value";
}
public function arr()
{
return $this->arr;
}
}
Temporal Coupling
Remove the method fill_arr() and instead initialize the property $arr in one of the following options:
initialize field lazily when accessed the first time
initialize field in the constructor
initialize field with a default value
initialize field with a value injected via constructor
Initialize field lazily when accessed the first time
Initialize the field when it's accessed the first time:
class gen_arr
{
private $arr;
public function arr()
{
if (null === $this->arr) {
$this->arr = [
'key' => 'value',
];
}
return $this->arr;
}
}
Initialize field in the constructor
Assign a value during construction:
class gen_arr
{
private $arr;
public function __construct()
{
$this->arr = [
'key' => 'value',
];
}
public function arr()
{
return $this->arr;
}
}
Initialize field with a default value
Assign a value to the field directly, which works fine if you don't need to do any computation:
class gen_arr
{
private $arr = [
'key' => 'value',
];
public function arr()
{
return $this->arr;
}
}
Initialize field with a value injected via constructor
If the values are not hard-coded or otherwise calculated (as in the previous examples), and you need to be able to instantiate objects with different values, inject values via constructor:
class gen_arr
{
private $arr;
public function __construct(array $arr)
{
$this->arr = $arr;
}
public function arr()
{
return $this->arr;
}
}
Accessing and dereferencing values
This seems like this is your actual question, so the answer is - of course - It depends!.
Let's assume we have provided an accessor instead of accessing the otherwise public field directly:
Since PHP 5.4, the following is possible:
$object = new gen_arr();
echo $object->arr()['key'];
If you are still using an older version of PHP, you obviously can't do that and have to do something like this instead:
$object = new gen_arr();
$arr = $object->arr();
echo $arr['key'];
Largely, though, the answer to this question depends on the circumstances, and what you want to achieve. After all, readability is key for maintenance, so it might just make sense for you to introduce an explaining variable.
Note About your example, you could just use an ArrayObject instead:
$arr = new \ArrayObject([
'key' => 'value',
]);
echo $arr['key']);
For reference, see:
http://wiki.c2.com/?EncapsulationDefinition
http://blog.ploeh.dk/2011/05/24/DesignSmellTemporalCoupling/
http://php.net/manual/en/language.oop5.properties.php
http://wiki.c2.com/?ItDepends
http://php.net/manual/en/migration54.new-features.php
https://refactoring.com/catalog/extractVariable.html
http://wiki.c2.com/?IntroduceExplainingVariable
http://php.net/manual/en/class.arrayobject.php
For an example, see:
https://3v4l.org/qVVBM
First fill up the array
$gen_arr = new gen_arr();
$gen_arr->fill_arr();
then get the values with a getter method
$val = $gen_arr->getValue($key);
A getter method would be like this
public function getValue($key) {
return $this->arr[$key];
}
And certailny make the $arr property private
I am building a class and initially wanted to overload the constructs but discovered this is not allowed in PHP. My solution was using variable arguments for the one constructor. However, I am having some issues using string literals in a key => value pair and assigning the class properties. This leads me to ask my main question - is it actually possible to use variable variables to assign class properties via the constructor?
See example below:
class funrun{
protected $run_id;
protected $fun_id;
protected $funrun_title;
protected $date;
function __construct(){
if (func_num_args() > 0){
$args = func_get_args(0);
foreach($args as $key => $value){
$this->$key = $value;
}
$this->date = date();
function __get($name){
return $this->name;
}
function __set($name,$value){
$this->name = $value;
}
}
This seems to correctly assign the values. But then when I do the following:
$settings = array ('run_id' => 5, 'fun_id' => 3);
$fun_example = new funrun($settings);
echo $fun_example->run_id;
I get an error that the getter method isn't functioning:
Undefined property: funrun::$name
However, when I switch the class code to $this->key, the class property doesn't seem to be assigned at all. When I do $fun_example->$run_id, nothing is returned.
What am I missing here? Is there anyway to use an array with string literals to assign class properties? If not, what is a good way to tackle the variable argument issue with constructors?
$this->name is looking for a property named name. Variable properties are written as:
$this->$name
See the paragraph beginning with Class properties may also be accessed using variable property names. in the PHP documentation on variable variables
Your constructor is incorrectly written. It's iterating over the argument list, expecting it to be an associative array. But you're passing the settings as a single argument. So it should be:
function __construct($args) {
foreach ($args as $key => $value) {
$this->$key = $value;
}
$this->date = time();
}
This thread didn't helped me.
If I use
$class_vars = get_class_vars(get_class($this));
foreach ($class_vars as $name => $value) {
echo "$name : $value\n";
}
I get
attrib1_name : attrib2_name : attrib3_name
There are no values. Also a private attribute is shown, which I don't want.
If I use
echo "<pre>";
print_r(get_object_vars($this));
echo "</pre>";
I get
Array
(
[atrrib1_name] => attrib1_value
[attrib2_name] => attrib2_value
)
Here again I have a private attribute and all sub attributes. But this time I have the values. How can I constrain this to one level?
Isn't there a possibility to show all public attributes with their values of an object?
You are seeing non-public properties because get_class_vars works according to current scope. Since you are using $this your code is inside the class, so the non-public properties are accessible from the current scope. The same goes for get_object_vars which is probably a better choice here.
In any case, a good solution would be to move the code that retrieves the property values out of the class.
If you do not want to create a free function for that (why? seriously, reconsider!), you can use a trick that involves an anonymous function:
$getter = function($obj) { return get_object_vars($obj); };
$class_vars = $getter($this);
See it in action.
Update: Since you are in PHP < 5.3.0, you can use this equivalent code:
$getter = create_function('$obj', 'return get_object_vars($obj);');
$class_vars = $getter($this);
You can do this easily with php Reflection api
Extending Mr.Coder's answer, here is a snippet to fetch the public attributes of the object (name and value) as an array
public function getPublicProperties()
{
$results = [];
$reflectionObject = (new ReflectionObject($this));
$properties = $reflectionObject->getProperties(ReflectionProperty::IS_PUBLIC);
foreach ($properties as $property) {
$results[$property->getName()] = $property->getValue($this);
}
return $results;
}
Use get_object_vars.
see: http://dk.php.net/manual/en/function.get-object-vars.php
I Fully recognize what you are trying to achieve so why not have something external like this to help out... (pasted from PHPFiddle)
<?php
final class utils {
public static function getProperties(& $what) {
return get_object_vars($what);
}
}
class ball {
var $name;
private $x, $y;
function __construct($name,$x,$y) {
}
function publicPropsToArray() {
return utils::getProperties($this);
}
function allPropsToArray() {
return get_object_vars($this);
}
}
$ball1 = new ball('henry',5,6);
//$ball2 = new ball('henry',3,4);
echo "<pre>";
print_r($ball1->publicPropsToArray());
echo "\r\n\r\n";
print_r($ball1->allPropsToArray());
echo "\r\n\r\n";
?>
This way I can both access all properties of the object or for something such as a database access layer or similarly for a function that send "safe" data to a view or another un-privileged model I can send just the public properties, but have the behaviour defined within the object.
Sure this leads to coupling with a utility class, but to be fair not all couplings are bad, some are nesecarry to achieve an end goal, dont get bogged down by these things
I have an object in PHP, of the type MyObject.
$myObject instanceof MyObject
Now, in the class MyObject, there is a non-static function, and in there, I use the reference to "me", like $this, but I also have another object there.
Is it possible, without doing $this = $myObject, to achieve more or less the same effect, like something of the sort set_object_vars($this, get_object_vars($myObject))?
<?php
class MyObject
{
public function import(MyObject $object)
{
foreach (get_object_vars($object) as $key => $value) {
$this->$key = $value;
}
}
}
Will do what you want I guess, but you should be aware of the following:
get_object_vars will only find non-static properties
get_object_vars will only find accessible properties according to scope
The according to scope part is quite important and may deserve a little more explanation. Did you know that properties scope are class dependent rather than instance dependent in PHP?
It means that in the example above, if you had a private $bar property in MyObject, get_object_vars would see it, since you are in an instance of a MyObject class. This will obviously not work if you're trying to import instances of another class.
#Geoffrey Bachelet we can improve this:
class MyObject
{
//object or array as parameter
public function import($object)
{
$vars=is_object($object)?get_object_vars($object):$object;
if(!is_array($vars)) throw Exception('no props to import into the object!');
foreach ($vars as $key => $value) {
$this->$key = $value;
}
}
}
The difference is here that you can pass an ordinary array (hashtable) as well as an object. Think in example about some data coming from the database.