Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you create instance properties dynamically in PHP?

Is there any way to create all instance properties dynamically? For example, I would like to be able to generate all attributes in the constructor and still be able to access them after the class is instantiated like this: $object->property. Note that I want to access the properties separately, and not using an array; here's an example of what I don't want:

class Thing {
    public $properties;
    function __construct(array $props=array()) {
        $this->properties = $props;
    }
}
$foo = new Thing(array('bar' => 'baz');
# I don't want to have to do this:
$foo->properties['bar'];
# I want to do this:
//$foo->bar;

To be more specific, when I'm dealing with classes that have a large number of properties, I would like to be able to select all columns in a database (which represent the properties) and create instance properties from them. Each column value should be stored in a separate instance property.

like image 676
Brayn Avatar asked May 06 '09 14:05

Brayn


People also ask

What is dynamic property PHP?

Dynamic properties are property that are not declared in the class, but in the code in your class, you want to use a not declared property: class User { public string $name; public function __construct($name="", $age = 0) { $this->name = $name; // Assigns the not existent property age.

What is dynamic property?

Theoretically, it can be defined as the ratio of stress to strain resulting from an oscillatory load applied under tensile, shear, or compression mode.

How do you declare and access properties of a class in PHP?

Here, we declare a static property: $value. Then, we echo the value of the static property by using the class name, double colon (::), and the property name (without creating a class first).


8 Answers

Sort of. There are magic methods that allow you to hook your own code up to implement class behavior at runtime:

class foo {
  public function __get($name) {
    return('dynamic!');
  }
  public function __set($name, $value) {
    $this->internalData[$name] = $value;
  }
}

That's an example for dynamic getter and setter methods, it allows you to execute behavior whenever an object property is accessed. For example

print(new foo()->someProperty);

would print, in this case, "dynamic!" and you could also assign a value to an arbitrarily named property in which case the __set() method is silently invoked. The __call($name, $params) method does the same for object method calls. Very useful in special cases. But most of the time, you'll get by with:

class foo {
  public function __construct() {
    foreach(getSomeDataArray() as $k => $value)
      $this->{$k} = $value;
  }
}

...because mostly, all you need is to dump the content of an array into correspondingly named class fields once, or at least at very explicit points in the execution path. So, unless you really need dynamic behavior, use that last example to fill your objects with data.

This is called overloading http://php.net/manual/en/language.oop5.overloading.php

like image 179
Udo Avatar answered Sep 29 '22 23:09

Udo


It depends exactly what you want. Can you modify the class dynamically? Not really. But can you create object properties dynamically, as in one particular instance of that class? Yes.

class Test
{
    public function __construct($x)
    {
        $this->{$x} = "dynamic";
    }
}

$a = new Test("bar");
print $a->bar;

Outputs:

dynamic

So an object property named "bar" was created dynamically in the constructor.

like image 32
Chad Birch Avatar answered Sep 28 '22 23:09

Chad Birch


Yes, you can.

class test
{
    public function __construct()
    {
        $arr = array
        (
            'column1',
            'column2',
            'column3'
        );

        foreach ($arr as $key => $value)
        {
            $this->$value = '';
        }   
    }

    public function __set($key, $value)
    {
        $this->$key = $value;
    }

    public function __get($value)
    {
        return 'This is __get magic '.$value;
    }
}

$test = new test;

// Results from our constructor test.
var_dump($test);

// Using __set
$test->new = 'variable';
var_dump($test);

// Using __get
print $test->hello;

Output

object(test)#1 (3) {
  ["column1"]=>
  string(0) ""
  ["column2"]=>
  string(0) ""
  ["column3"]=>
  string(0) ""
}
object(test)#1 (4) {
  ["column1"]=>
  string(0) ""
  ["column2"]=>
  string(0) ""
  ["column3"]=>
  string(0) ""
  ["new"]=>
  string(8) "variable"
}
This is __get magic hello

This code will set dynamic properties in the constructor which can then be accessed with $this->column. It's also good practice to use the __get and __set magic methods to deal with properties that are not defined within the class. More information them can be found here.

http://www.tuxradar.com/practicalphp/6/14/2

http://www.tuxradar.com/practicalphp/6/14/3

like image 28
The Pixel Developer Avatar answered Sep 28 '22 23:09

The Pixel Developer


You can use an instance variable to act as a holder for arbitrary values and then use the __get magic method to retrieve them as regular properties:

class My_Class
{
    private $_properties = array();

    public function __construct(Array $hash)
    {
         $this->_properties = $hash;
    }

    public function __get($name)
    {
         if (array_key_exists($name, $this->_properties)) {
             return $this->_properties[$name];
         }
         return null;
    }
}
like image 30
Carlton Gibson Avatar answered Sep 26 '22 23:09

Carlton Gibson


Why is every example so complicated?

<?php namespace example;

error_reporting(E_ALL | E_STRICT); 

class Foo
{
    // class completely empty
}

$testcase = new Foo();
$testcase->example = 'Dynamic property';
echo $testcase->example;
like image 31
srcspider Avatar answered Sep 29 '22 23:09

srcspider


Here is simple function to populate object members without making class members public. It also leaves constructor for your own usage, creating new instance of object without invoking constructor! So, your domain object doesn't depend on database!


/**
 * Create new instance of a specified class and populate it with given data.
 *
 * @param string $className
 * @param array $data  e.g. array(columnName => value, ..)
 * @param array $mappings  Map column name to class field name, e.g. array(columnName => fieldName)
 * @return object  Populated instance of $className
 */
function createEntity($className, array $data, $mappings = array())
{
    $reflClass = new ReflectionClass($className);
    // Creates a new instance of a given class, without invoking the constructor.
    $entity = unserialize(sprintf('O:%d:"%s":0:{}', strlen($className), $className));
    foreach ($data as $column => $value)
    {
        // translate column name to an entity field name
        $field = isset($mappings[$column]) ? $mappings[$column] : $column;
        if ($reflClass->hasProperty($field))
        {
            $reflProp = $reflClass->getProperty($field);
            $reflProp->setAccessible(true);
            $reflProp->setValue($entity, $value);
        }
    }
    return $entity;
}

/******** And here is example ********/

/**
 * Your domain class without any database specific code!
 */
class Employee
{
    // Class members are not accessible for outside world
    protected $id;
    protected $name;
    protected $email;

    // Constructor will not be called by createEntity, it yours!
    public function  __construct($name, $email)
    {
        $this->name = $name;
        $this->emai = $email;
    }

    public function getId()
    {
        return $this->id;
    }

    public function getName()
    {
        return $this->name;
    }

    public function getEmail()
    {
        return $this->email;
    }
}


$row = array('employee_id' => '1', 'name' => 'John Galt', 'email' => '[email protected]');
$mappings = array('employee_id' => 'id'); // Employee has id field, so we add translation for it
$john = createEntity('Employee', $row, $mappings);

print $john->getName(); // John Galt
print $john->getEmail(); // [email protected]
//...

P.S. Retrieving data from object is similar, e.g. use $reflProp->setValue($entity, $value); P.P.S. This function is heavily inspired by Doctrine2 ORM which is awesome!

like image 41
Sergiy Sokolenko Avatar answered Sep 26 '22 23:09

Sergiy Sokolenko


Extend stdClass.

class MyClass extends stdClass
{
    public function __construct()
    {
        $this->prop=1;
    }
}

I hope this is what you need.

like image 23
Anthony Avatar answered Sep 29 '22 23:09

Anthony


You can:

$variable = 'foo';
$this->$variable = 'bar';

Would set the attribute foo of the object it's called on to bar.

You can also use functions:

$this->{strtolower('FOO')} = 'bar';

This would also set foo (not FOO) to bar.

like image 26
Koraktor Avatar answered Sep 27 '22 23:09

Koraktor