__get() 和 __set() 的最佳实践

3
从这个问题中,我们得知可以使用__get()__set()来访问私有变量。我想了解一般情况下它们是如何使用的。我想知道何时或在哪里最好使用重载函数,并且如果您使用过,请分享一下您的经验。
请注意,我们正在谈论这些函数:http://us2.php.net/manual/en/language.oop5.magic.php

1
你所提到的页面标题中的“魔术方法”一词让我立刻感到怀疑。 ;) - Robert Harvey
4个回答

3

懒加载模型(使用__get())

我不太记得在我的应用程序中经常使用PHP的魔术方法,但我记得有一种情况下__get()非常有用。

早些时候,我正在开发一个CakePHP框架的应用程序,其中有很多模型,即使方法只使用其中一个或两个模型,所有在特定控制器中使用的模型都会被初始化(这就是Cake的工作方式)。因此,我决定将其更改为懒加载模型(即在首次使用时加载模型)。

我所做的就是添加了一个非常简单的__get()函数,该函数查找具有特定名称的模型并加载它。大约只有3-4行代码。我在AppController中定义了它(所有CakePHP类都派生自该控制器),突然间我的应用程序速度更快,使用的内存更少。

后来,我进一步实现了懒加载组件。

动态模型方法(使用__call())

另一个很好的例子,也来自于CakePHP,是关于Cake如何搜索模型。基本上,您有两种方法:find()findAll(),但您还可以使用findBy<FieldName>()findAllBy<FieldName>()方法进行搜索。

例如,如果您有一个数据库表

notes(id, date, title, body)

创建相应的Cake模型。你可以使用findById()findByTitle()等方法。只需要使用驼峰式命名的数据库字段,就能更快地搜索任何字段。
Cake使用__call()魔术方法实现此功能。如果试图执行不存在的方法,则会调用此方法,并动态创建来自方法名称和参数的条件,然后仅运行find()findAll()。这种实现非常简单,能够带来很多好处。

2

我使用__get() 和 __set() 来访问一个私有数组中的元素,例如:

class Something {
    private $data;

    public function __set($key, $value) {
        //put validation here
        $this->data[$key] = $value;
    }

    public function __get($key) {
        if (!array_key_exists($this->data, $key)) {
             throw new Exception("Invalid member $key.");
        } else {
             return $this->data[$key];
        }
    }
}

因此,通过一些验证,例如$person->age = "asdf"将立即抛出异常(而如果年龄是公共成员,则没有问题)。

此外,在__set()中,如果您不想在类中有任意“成员”,可以限制有效的键。


但是这与常规用法$person['age']相比,有什么更高的价值呢? - Flavius Stef
在__set()中,你不仅可以像Jake所说的那样限制允许的属性,还可以验证正在设置的值。 - grantwparks

0

一些示例:

class formatedContainer {
    private $holder;
    protected $mode = "formated";

    public function __set($var, $value) {
        $formated = chunk_split($value, 4, "-");
        if(substr($formated, -1) == "-") 
            $formated = substr($formated, 0, strlen($formated) - 1);
        $this->holder[$var] = array('formated' => $formated, 'plain' => $value);
    }

    public function __get($var) {
        return $this->holder[$var][$this->mode];
    }

    public function getPlain() {
        $this->mode = "plain";
    }

    public function getFormated() {
        $this->mode = "formated";
    }
}

$texts = new formatedContainer();
$texts->myText = md5(uniqid());
$texts->anotherText = md5("I don't change!");

//Prints something like: 440e-6816-b2f5-7aa5-9627-9cc8-26ef-ef3b
echo $texts->myText;

$texts->getPlain();

//Prints something like: 8559d37c5a02714dca8bd1ec50a4603a
echo "<br/>" . $texts->anotherText;

有点没用,但我想你可以得到一个想法。:}


0

网上有很多例子,使用__get()__set()与私有“属性数组”结合使用。我在类定义中想要的有趣变化是能够实际声明公共属性并仍然使用这些魔术拦截器--以获得更自我记录的代码并使我的IDE能够完成代码补全等功能。通常,如果声明了这些属性,__get()__set()将不会被调用。我发现如果在我的类构造函数中unset()这些相同的属性,我可以同时获得最佳效果。


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