这完全取决于您要达到的目标。如果您希望实现任何接口,将所有属性存储在(关联)数组中将使您的生活更轻松Traversable
,尽管也有一些方法可以使用预定义的属性来做到这一点。
如果最好,您的意思是最快的:预先定义每个属性将使您的类更高效,正如我已经解释过
的那样 当然,还有很多原因说明第一种方法更好,我在这里列出了它们
基本上,您需要知道的是 PHP 的对象“重载”很慢(声明属性为O( 1 ),重载属性为O(n))。魔术方法也是如此,例如__get
and __set
。他们很慢。考虑一下:
class Declared
{
private $foo = 123;
public function setFoo($val)
{
$this->foo = (int) $val;
return $this;
}
public function getFoo()
{
return $this->foo;
}
public function __get($name)
{
$name = 'get'.ucfirst($name);
if (method_exists($this, $name))
{
return $this->{$name}():
}
//return null or throw exception
throw new RuntimeExcpetion('attempted to access non-existing property using '.$name.' in '.__METHOD__);
}
public function __set($name, $val)
{
$mname = 'set'.ucfirst($name);
if (method_exists($this, $mname))
{
return $this->{$mname}($val):
}
throw new RuntimeException($name.' is an invalid property name');
}
}
我不仅可以确定在任何时候都不会添加新属性,而且因为我使用的是自定义 getter 和 setter,我还可以确保每个属性的类型都是我想要/期望的。在这个例子中,我需要$foo
是一个整数,所以在 setter 中,我将作为参数传递的任何值转换为 int。您也可以使用魔术__set
方法来做到这一点,但是您处理的属性越多,该方法就会变得越混乱(大量的 if 和 else)。
现在,将其与此类进行比较:
class NotDeclared
{
private $data = array('foo' => 123);
public function __get($name)
{
return isset($this->data[$name]) ? $this->data[$name] : null;
}
public function __set($name, $value)
{
$this->[$data] = $value;
}
}
现在,这门课看起来确实短了很多,所以我明白为什么这会吸引任何人。然而,让我们在使用方面进行比较:
$declared = new Declared;
$notDeclared = new NotDeclared;
echo $declared->foo;//ok
echo $declared->getFoo();//ok
echo $notDeclared->foo;//ok
$declared->foo = 34;//fine
$declared->setFoo($declared->foo*2);//just fine
echo $declared->fo;//TYPO => exception
echo $notDeclared->fo;//no errors show, but echoes nothing: invisible bug?
//Worse, still: 2 mistakes in one go
$notDeclared->fo = 'Typo, meant foo, but assign string to expected integer property';
$declared->fo = 'asd';//throws excpetion at once
//singe $notDeclared->fo didn't throw excpetions, I'm assuming I reassigned $foo, yet
echo $notDeclared->foo;//ecoes old value
因为我__set
通过在用户尝试分配不存在的属性时抛出异常来限制 的使用,所以任何拼写错误都会立即抛出异常。如果你不这样做,你可能最终不得不回溯你的对象的一个实例,以便发现一个愚蠢的错字,就像我在这里展示的例子一样fo
更重要的是:您必须考虑何时调用魔术方法:
$instance->newProperty;
幕后发生了什么?
- 检查对象实例是否有一个名为的公共属性
newProperty
- 检查被调用方法的对象
newProperty
(语法错误)
- 检查对象的
__get
方法
- 调用
__get
方法
- 查找数据属性 (
$this->data
)
- 检查数组的
newProperty
键 ( isset
)
- 三进制的返回结果(即null)
这意味着$instance->newProperty
需要 PHP 查找newPropery
,然后查找__get
方法,$data
然后查找属性,然后查找该newPropery
数组中的键,然后返回 null 或值。这是对一个对象的大量搜索。
这就是为什么我总是推荐第一个选项。