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

可以在PHP中动态创建实例属性吗?,php,design-patterns,oop,class,Php,Design Patterns,Oop,Class,有没有办法动态创建所有实例属性?例如,我希望能够在构造函数中生成所有属性,并且在类被实例化后仍然能够访问它们:$object->property。请注意,我希望单独访问属性,而不是使用数组;下面是一个我不想要的例子: 更具体地说,当我处理具有大量属性的类时,我希望能够选择数据库中的所有列(表示属性)并从中创建实例属性。每个列值都应存储在单独的实例属性中。您可以: $variable = 'foo'; $this->$variable = 'bar'; 将调用对象的属性foo设置为bar

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

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

您可以:

$variable = 'foo';
$this->$variable = 'bar';
将调用对象的属性
foo
设置为
bar

您还可以使用以下功能:

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

这也会将
foo
(而不是
foo
)设置为
bar

,这完全取决于您想要什么。你能动态修改类吗?不是真的。但是您能否动态地创建对象属性,就像在该类的一个特定实例中一样?对

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

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

动态的


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

您可以使用实例变量作为任意值的持有者,然后使用“get magic”方法将其作为常规属性检索:

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;
    }
}
是的,你可以

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;
输出

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->列访问该构造函数。使用uuu get和uuu set魔术方法处理类中未定义的属性也是一种很好的做法。更多信息可以在这里找到


有点像。有一些神奇的方法可以让您在运行时钩住自己的代码来实现类行为:

class foo {
  public function __get($name) {
    return('dynamic!');
  }
  public function __set($name, $value) {
    $this->internalData[$name] = $value;
  }
}
这是动态getter和setter方法的一个示例,它允许您在访问对象属性时执行行为。比如说

print(new foo()->someProperty);
在本例中,将打印“dynamic!”并且您还可以为任意命名的属性指定一个值,在这种情况下u set()方法将被静默调用。__调用($name,$params)方法对对象方法调用执行相同的操作。在特殊情况下非常有用。但大多数情况下,您都可以:

class foo {
  public function __construct() {
    foreach(getSomeDataArray() as $k => $value)
      $this->{$k} = $value;
  }
}
…因为在大多数情况下,您只需将数组的内容转储到相应命名的类字段中一次,或者至少在执行路径中转储到非常显式的点。因此,除非您确实需要动态行为,否则请使用最后一个示例用数据填充对象

这称为重载


处理这种快速发展的方式真的很复杂。我喜欢答案和神奇的方法,但我认为最好使用CodeSmith这样的代码生成器

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


这样我就有了无错误(无输入错误)的可读代码。如果数据库模型发生更改,请再次运行生成器。。。这对我很有用。

这里有一个简单的函数,可以在不公开类成员的情况下填充对象成员。 它还将构造函数留给您自己使用,在不调用构造函数的情况下创建对象的新实例!所以,您的域对象不依赖于数据库



注意:从对象检索数据类似,例如使用$reflProp->setValue($entity,$value); P.P.S.此功能深受启发,非常棒

扩展stdClass

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

我希望这就是您所需要的。

如果您真的必须这样做,最好的方法是重载ArrayObject,这样可以保持迭代支持(foreach),仍然可以循环使用您的所有属性

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
我注意到你说“不使用数组”,我只是想向你保证,虽然从技术上讲,数组是在后台使用的,但你永远不必看到它。您可以通过->properyname或foreach($name=>$value中的类)访问所有属性

这是我昨天做的一个示例,注意这也是强类型的。因此,如果您尝试提供“字符串”,则标记为“整数”的属性将抛出错误

你当然可以把它去掉

还有一个AddProperty()成员函数,尽管示例中没有演示。这将允许您稍后添加属性

示例用法:

    $Action = new StronglyTypedDynamicObject("Action",
            new StrongProperty("Player", "ActionPlayer"),   // ActionPlayer
            new StrongProperty("pos", "integer"),
            new StrongProperty("type", "integer"),
            new StrongProperty("amount", "double"),
            new StrongProperty("toCall", "double"));

    $ActionPlayer = new StronglyTypedDynamicObject("ActionPlayer",
            new StrongProperty("Seat", "integer"),
            new StrongProperty("BankRoll", "double"),
            new StrongProperty("Name", "string"));

    $ActionPlayer->Seat = 1;
    $ActionPlayer->Name = "Doctor Phil";

    $Action->pos = 2;
    $Action->type = 1;
    $Action->amount = 7.0;
    $Action->Player = $ActionPlayer;

    $newAction = $Action->factory();
    $newAction->pos = 4;

    print_r($Action);
    print_r($newAction);


    class StrongProperty {
            var $value;
            var $type;
            function __construct($name, $type) {
                    $this->name = $name;
                    $this->type = $type;
            }

    }

    class StronglyTypedDynamicObject extends ModifiedStrictArrayObject {

            static $basic_types = array(
                    "boolean",
                    "integer",
                    "double",
                    "string",
                    "array",
                    "object",
                    "resource",
            );

            var $properties = array(
                    "__objectName" => "string"
            );

            function __construct($objectName /*, [ new StrongProperty("name", "string"), [ new StrongProperty("name", "string"), [ ... ]]] */) {
                    $this->__objectName = $objectName;
                    $args = func_get_args();
                    array_shift($args);
                    foreach ($args as $arg) {
                            if ($arg instanceof StrongProperty) {
                                    $this->AddProperty($arg->name, $arg->type);
                            } else {
                                    throw new Exception("Invalid Argument");
                            }
                    }
            }

            function factory() {
                    $new = clone $this;
                    foreach ($new as $key => $value) {
                            if ($key != "__objectName") {
                                    unset($new[$key]);
                            }
                    }

                    // $new->__objectName = $this->__objectName;
                    return $new;
            }

            function AddProperty($name, $type) {
                    $this->properties[$name] = $type;
                    return;

                    if (in_array($short_type, self::$basic_types)) {
                            $this->properties[$name] = $type;
                    } else {
                            throw new Exception("Invalid Type: $type");
                    }
            }

            public function __set($name, $value) {
                    self::sdprintf("%s(%s)\n", __FUNCTION__, $name);
                    $this->check($name, $value);
                    $this->offsetSet($name, $value);
            }

            public function __get($name) {
                    self::sdprintf("%s(%s)\n", __FUNCTION__, $name);
                    $this->check($name);
                    return $this->offsetGet($name);
            }

            protected function check($name, $value = "r4nd0m") {
                    if (!array_key_exists($name, $this->properties)) {
                            throw new Exception("Attempt to access non-existent property '$name'");
                    }

                    $value__objectName = "";
                    if ($value != "r4nd0m") {
                            if ($value instanceof StronglyTypedDynamicObject) {
                                    $value__objectName = $value->__objectName;
                            }
                            if (gettype($value) != $this->properties[$name] && $value__objectName != $this->properties[$name]) { 
                                    throw new Exception("Attempt to set {$name} ({$this->properties[$name]}) with type " . gettype($value) . ".$value__objectName");
                            }
                    }
            }
    }

    class ModifiedStrictArrayObject extends ArrayObject {
            static $debugLevel = 0;

            /* Some example properties */

            static public function StaticDebug($message) {
                    if (static::$debugLevel > 1) {
                            fprintf(STDERR, "%s\n", trim($message));
                    }
            }

            static public function sdprintf() {
                    $args = func_get_args();
                    $string = call_user_func_array("sprintf", $args);
                    self::StaticDebug("D            " . trim($string));
            }

            protected function check($name) {
                    if (!array_key_exists($name, $this->properties)) {
                            throw new Exception("Attempt to access non-existent property '$name'");
                    }
            }

            //static public function sget($name, $default = NULL) {
            /******/ public function get ($name, $default = NULL) {
                    self::sdprintf("%s(%s)\n", __FUNCTION__, $name);
                    $this->check($name);
                    if (array_key_exists($name, $this->storage)) {
                            return $this->storage[$name];
                    }
                    return $default;
            }

            public function offsetGet($name) { 
                    self::sdprintf("%s(%s)\n", __FUNCTION__, implode(",", func_get_args()));
                    $this->check($name);
                    return call_user_func_array(array(parent, __FUNCTION__), func_get_args());
            }
            public function offsetSet($name, $value) { 
                    self::sdprintf("%s(%s)\n", __FUNCTION__, implode(",", func_get_args()));
                    $this->check($name);
                    return call_user_func_array(array(parent, __FUNCTION__), func_get_args());
            }
            public function offsetExists($name) { 
                    self::sdprintf("%s(%s)\n", __FUNCTION__, implode(",", func_get_args()));
                    $this->check($name);
                    return call_user_func_array(array(parent, __FUNCTION__), func_get_args());
            }
            public function offsetUnset($name) { 
                    self::sdprintf("%s(%s)\n", __FUNCTION__, implode(",", func_get_args()));
                    $this->check($name);
                    return call_user_func_array(array(parent, __FUNCTION__), func_get_args());
            }

            public function __toString() {
                    self::sdprintf("%s(%s)\n", __FUNCTION__, $name);
                    foreach ($this as $key => $value) {
                            $output .= "$key: $value\n";
                    }
                    return $output;
            }

            function __construct($array = false, $flags = 0, $iterator_class = "ArrayIterator") { 
                    self::sdprintf("%s(%s)\n", __FUNCTION__, implode(",", func_get_args()));
                    parent::setFlags(parent::ARRAY_AS_PROPS);
            }
    }

为什么每个例子都这么复杂

<?php namespace example;

error_reporting(E_ALL | E_STRICT); 

class Foo
{
    // class completely empty
}

$testcase = new Foo();
$testcase->example = 'Dynamic property';
echo $testcase->example;
阅读@Udo后的
。我提出了以下模式,它不会在类实例中增加构造函数数组参数中的任何项,但仍然允许您键入更少的内容,并且可以轻松地向类添加新属性

class DBModelConfig
{
    public $host;
    public $username;
    public $password;
    public $db;
    public $port = '3306';
    public $charset = 'utf8';
    public $collation = 'utf8_unicode_ci';

    public function __construct($config)
    {
        foreach ($config as $key => $value) {
            if (property_exists($this, $key)) {
                $this->{$key} = $value;
            }
        }
    }
}
然后可以传递如下数组:

[
    'host'      => 'localhost',
    'driver'    => 'mysql',
    'username'  => 'myuser',
    'password'  => '1234',
    'charset'   => 'utf8',
    'collation' => 'utf8_unicode_ci',
    'db'        => 'key not used in receiving class'
]

我个人喜欢你的解决方案,因为你可以更好地控制变量在作用域上的使用方式以及你拥有的东西,但他刚刚发布了这个。编辑:对不起,忘了提一下:没有使用数组来保存属性,每个属性都在一个单独的“变量”中。谢谢你的评论-我认为这是解决问题的自然方法。恐怕我不太明白Brayn的编辑到底是什么意思——而且作为一个新手,我没有足够的声望点来发表评论。哦……嗨,我真的很喜欢这个答案。我试图编辑它以进行修复,但Stackoverflow拒绝了更改,因为它是单字符编辑:
返回此->\u属性[$name]
可能应该是
返回$this->_属性[$name]这是我需要的,但是这些属性是私有的还是公共的?或者我可以这样将它们私有化:private$this->{$k}=>$value?它们将是公共的,据我所知,不可能在运行时将它们设置为私有或受保护。对于“私有”可见性,您可以声明一个私有数组类型字段,然后填充该字段。“受保护”也是如此。最好让事情尽可能简单,这样你就可以
class DBModelConfig
{
    public $host;
    public $username;
    public $password;
    public $db;
    public $port = '3306';
    public $charset = 'utf8';
    public $collation = 'utf8_unicode_ci';

    public function __construct($config)
    {
        foreach ($config as $key => $value) {
            if (property_exists($this, $key)) {
                $this->{$key} = $value;
            }
        }
    }
}
[
    'host'      => 'localhost',
    'driver'    => 'mysql',
    'username'  => 'myuser',
    'password'  => '1234',
    'charset'   => 'utf8',
    'collation' => 'utf8_unicode_ci',
    'db'        => 'key not used in receiving class'
]