你可以在 PHP 中动态创建实例属性吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/829823/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 23:59:54  来源:igfitidea点击:

Can you create instance properties dynamically in PHP?

phpdesign-patternsoopclass

提问by Brayn

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'twant:

有没有办法动态创建所有实例属性?例如,我想能够产生在构造函数中的所有属性,仍然能够访问它们的类实例化这样的后:$object->property。请注意,我想单独访问属性,而不是使用数组;这是我想要的一个例子:

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.

更具体地说,当我处理具有大量属性的类时,我希望能够选择数据库中的所有列(代表属性)并从中创建实例属性。每个列值都应存储在单独的实例属性中。

回答by Udo

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

这是动态 getter 和 setter 方法的示例,它允许您在访问对象属性时执行行为。例如

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:

在这种情况下,将打印“动态!” 并且您还可以为任意命名的属性分配一个值,在这种情况下 __set() 方法被静默调用。__call($name, $params) 方法对对象方法调用执行相同的操作。在特殊情况下非常有用。但大多数时候,你会得到:

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

这称为重载 http://php.net/manual/en/language.oop5.overloading.php

回答by Chad Birch

It depends exactly what you want. Can you modify the classdynamically? Not really. But can you create objectproperties 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.

因此,在构造函数中动态创建了一个名为“bar”的对象属性。

回答by Carlton Gibson

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:

您可以使用实例变量作为任意值的持有者,然后使用 __get 魔术方法将它们作为常规属性检索:

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

回答by The Pixel Developer

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.

此代码将在构造函数中设置动态属性,然后可以使用 $this->column 访问这些属性。使用 __get 和 __set 魔术方法来处理未在类中定义的属性也是一种很好的做法。可以在此处找到更多信息。

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

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

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

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

回答by srcspider

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;

回答by Sergiy Sokolenko

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 ORMwhich is awesome!

PS 从对象中检索数据类似,例如使用 $reflProp->setValue($entity, $value); PPS 这个功能深受Doctrine2 ORM 的启发,非常棒!

回答by Anthony

class DataStore // Automatically extends stdClass
{
  public function __construct($Data) // $Data can be array or stdClass
  {
    foreach($Data AS $key => $value)  
    {
        $this->$key = $value;    
    }  
  }
}

$arr = array('year_start' => 1995, 'year_end' => 2003);
$ds = new DataStore($arr);

$gap = $ds->year_end - $ds->year_start;
echo "Year gap = " . $gap; // Outputs 8

回答by Anthony

Extend stdClass.

扩展标准类。

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

I hope this is what you need.

我希望这是你所需要的。

回答by Koraktor

You can:

你可以:

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

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

foo其调用的对象的属性设置为bar

You can also use functions:

您还可以使用函数:

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

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

这也将foo(不FOO)设置为bar.

回答by zidane

This is really complicated way to handle this kind of rapid development. I like answers and magic methods but in my opinion it is better to use code generators like CodeSmith.

这是处理这种快速开发的非常复杂的方法。我喜欢答案和魔术方法,但在我看来,最好使用 CodeSmith 之类的代码生成器。

I have made template that connect to database, read all columns and their data types and generate whole class accordingly.

我制作了连接到数据库的模板,读取所有列及其数据类型并相应地生成整个类。

This way I have error free (no typos) readable code. And if your database model changes run generator again... it works for me.

这样我就有了无错误(没有错别字)可读的代码。如果您的数据库模型更改再次运行生成器......它对我有用。