动态创建新属性的方法

97

在对象的方法中,如何从给定的参数创建一个属性?

class Foo{

  public function createProperty($var_name, $val){
    // here how can I create a property named "$var_name"
    // that takes $val as value?

  }

}

我希望能够像这样访问属性:

$object = new Foo();
$object->createProperty('hello', 'Hiiiiiiiiiiiiiiii');

echo $object->hello;

还有可能将此属性设置为public/protected/private吗?我知道在这种情况下应该是public,但是我可能希望添加一些魔法方法来获取受保护的属性等等 :)


我认为我找到了解决方案:

  protected $user_properties = array();

  public function createProperty($var_name, $val){
    $this->user_properties[$var_name] = $val;

  }

  public function __get($name){
    if(isset($this->user_properties[$name])
      return $this->user_properties[$name];

  }

你认为这是一个好主意吗?

4个回答

130

有两种方法可以实现。

第一种,您可以直接从类外动态创建属性:

class Foo{

}

$foo = new Foo();
$foo->hello = 'Something';

如果您想通过createProperty方法创建属性:

class Foo{
    public function createProperty($name, $value){
        $this->{$name} = $value;
    }
}

$foo = new Foo();
$foo->createProperty('hello', 'something');

谢谢。我需要它在类内部。有点复杂解释,这些属性实际上是站点扩展的对象,站点管理员可以启用/禁用 :) 但我会使用我的解决方案,我认为将它们保留在数组中更好。 - Alex
4
可以将它们设为私有或受保护吗? - Marcio Mazzucato
3
我认为Sun是正确的。这行代码“$this->$name = $value;”实际上应该是“$this->name = $value;”。 - Victor D.
18
或者是 $this->{$name} = $value;,对吗? - Victor
4
如果您想要像这种情况下的动态属性名称,比如我们想要“hello”属性,则使用$this->{$name}而不是$this->name,因为$this->name只会创建一个名为“name”的属性。 - ismnoiet
显示剩余3条评论

26

以下示例适用于不想声明整个类的人。

$test = (object) [];

$prop = 'hello';

$test->{$prop} = 'Hiiiiiiiiiiiiiiii';

echo $test->hello; // prints Hiiiiiiiiiiiiiiii

7
属性重载非常缓慢。如果可以的话,请尽量避免使用它。同样重要的是实现另外两个魔术方法:
__isset(); __unset();
如果您不想在使用这些对象“属性”时后期发现一些常见错误,
这里有一些示例:

http://www.php.net/manual/en/language.oop5.overloading.php#language.oop5.overloading.members

在Alex的评论后进行了编辑:

你可以自己检查两种解决方案之间的时间差异(更改$REPEAT_PLEASE)

<?php

 $REPEAT_PLEASE=500000;

class a {}

$time = time();

$a = new a();
for($i=0;$i<$REPEAT_PLEASE;$i++)
{
$a->data = 'hi';
$a->data = 'bye'.$a->data;
}

echo '"NORMAL" TIME: '.(time()-$time)."\n";

class b
{
        function __set($name,$value)
        {
                $this->d[$name] = $value;
        }

        function __get($name)
        {
                return $this->d[$name];
        }
}

$time=time();

$a = new b();
for($i=0;$i<$REPEAT_PLEASE;$i++)
{
$a->data = 'hi';
//echo $a->data;
$a->data = 'bye'.$a->data;
}

echo "TIME OVERLOADING: ".(time()-$time)."\n";

1
我有点沉迷于__get / __set / __call。我几乎在每个类中使用它们,以获得在使用该类时的良好API...到目前为止还没有用到__isset..你能提供一些与重载相关的基准测试链接或其他信息吗? - Alex
很抱歉这个消息不太好,但是__call 也非常缓慢。 - Abraham Covelo
1
请提供一些关于“属性重载较慢”的参考/证据。 - Pacerier
1
@Pacerier,请使用我在答案中提供的基准代码,在您自己的服务器上检查两种解决方案所花费的时间。 - Abraham Covelo

6
请使用以下语法:$object->{$property},其中$property是一个字符串变量,$object可以是this(如果它在类内部)或任何实例对象。
示例:http://sandbox.onlinephpfunctions.com/code/108f0ca2bef5cf4af8225d6a6ff11dfd0741757f
 class Test{
    public function createProperty($propertyName, $propertyValue){
        $this->{$propertyName} = $propertyValue;
    }
}

$test = new Test();
$test->createProperty('property1', '50');
echo $test->property1;

结果:50


1
这就是我一直在寻找的答案,我已经知道你可以引用先前未声明的对象属性... $object->example = "hello"; 我更想知道的是如何在动态指定要调用的属性的同时,动态地向对象添加属性。 - andmar8

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接