是否可以跳过从类内部代码调用__set

时间:2014-02-18 04:02:40

标签: php

我写了__set以确保在setter之前完成某些事情

public function __setter($name, $value) {
    if ($this->needDoSomething) {$this->doSomeThingNecessaryBeforeSetAnything();}
    $this->needDoSomething = false;
    $this->$name = $value;
}

然而,神奇的方法会影响性能。在同一个班级,我有另一个功能

private function loadData() {
    if ($this->needDoSomething) {$this->doSomeThingNecessaryBeforeSetAnything();}
    foreach ($data as $key=>$value) {
        $this->$key = $value;
    }
}

由于已经调用了doSomeThingNecessaryBeforeSetAnything(),我不需要调用__set,而是想直接设置属性。这对性能有很大帮助 但是,我无法删除__set,因为类外有很多遗留代码,我需要它来使逻辑正确。

似乎用PHP我无法动态添加或删除对象的方法。有任何想法吗?

编辑:性能是由__set本身引起的,因为我有大量的对象,每个对象都有大量的属性需要设置。下面的代码显示__set比直接设置属性慢6倍。

class Test {}
class Test2 {
    public function __set($name, $value) {
        $this->$name = $value;
    }
}

function runOnObject($o) {
    $t = microtime(true);
    for ($i=0; $i<100000; $i++) {
        $prop = "prop{$i}";
        $o->$prop = $i;
    }
    echo "".(microtime(true) - $t)." second ";
}

echo runOnObject(new Test()). "(With out __set)<p>";
echo runOnObject(new Test2()). "(With __set)";

结果: 0.084139823913574秒(没有__set) 0.47258400917053秒(带__set)

2 个答案:

答案 0 :(得分:0)

如果添加__get,则可以将属性存储在私有数据结构(例如数组)中,允许直接访问类中的数据,同时仍保持相同的公共接口。

像这样:     

class Container
{
    private $properties = array();

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

    public function __get($key)
    {
        if (!isset($this->properties[$key])) {
            throw new Exception('Invalid Property ' . $key);
        }
        return $this->properties[$key];
    }

    public function loadData($data)
    {
        $this->doSomethingExpensive();
        foreach ($data as $key => $value) {
            $this->properties[$key] = $value;
        }
    }

    private function doSomethingExpensive()
    {
        echo 'Doing Work...' . PHP_EOL;
    }
}

// Example

$c = new Container();
$c->loadData(array(
    'alpha' => 'A',
    'beta' => 'D',
    'charlie' => 'C',
    'delta' => 'D'
));

var_dump($c->alpha, $c->beta);

如果这会更快,我不知道,这取决于您的具体用例,因为您避免重复运行“昂贵”代码,但使用__get会有一些开销。

答案 1 :(得分:0)

显然不完全是您想要的,但另一个想法是...使用ArrayAccess代替__set动态集功能。虽然你必须更新'吨'的客户代码(不确定可能有多么不切实际)。

<?php
class A implements ArrayAccess
{
    private $_doThings = false;

    public function __construct() {
        $this->loadData();
    }

    public function offsetExists($k) { return isset($this->$k); }
    public function offsetGet($k) { return $this->$k; }
    public function offsetUnset($k) { unset($this->$k); }
    public function offsetSet($k, $v) {
        if($this->_doThings)
            $this->doSomeThingNecessaryBeforeSetAnything();
        $this->$k = $v;
    }

    private function doSomeThingNecessaryBeforeSetAnything() {
        echo __METHOD__ . PHP_EOL;
    }

    private function loadData() {
        $this->doSomeThingNecessaryBeforeSetAnything();
        $this->_doThings = false;
        foreach (array('a' => 1, 'b' => 2, 'c' => 3) as $key=>$value) {
            $this->$key = $value;
        }
    }
}

演示代码

$a = new A();
// Need to change all calls like $a->c = 4 to $a['c'] = 4
$a['c'] = 4;
var_dump($a);

因此需要进行痛苦的代码更改,但您可以获得两全其美的效果。动态行为和表现。