什么时候/我应该在PHP中使用__construct(),__ get(),__ set()和__call()?

时间:2008-10-30 15:32:57

标签: php magic-function

A similar question discusses __construct,但我将其留在我的标题中,以便搜索找到此人的人。

显然,__ get和__set采用的参数是获取或设置的变量。但是,您必须知道变量名称(例如,知道该人的年龄是$ age而不是$ myAge)。所以,如果您必须知道变量名称,我不明白这一点,特别是如果您正在使用您不熟悉的代码(例如库)。

我找到了一些解释__get()__set()__call()的网页,但我仍然不知道为什么或什么时候它们有用。

10 个答案:

答案 0 :(得分:6)

This page可能会有用。 (注意你所说的不正确 - __set()将变量的名称和值作为参数。__get()只取变量的名称。)

__get()__set()在您希望提供对变量的通用访问的库函数中很有用。例如,在ActiveRecord类中,您可能希望人们能够将数据库字段作为对象属性进行访问。例如,在Kohana PHP框架中,您可以使用:

$user = ORM::factory('user', 1);
$email = $user->email_address;

这是使用__get()__set()完成的。

使用__call()时可以实现类似的功能,即您可以检测到有人在调用getProperty()和setProperty()并进行相应处理。

答案 1 :(得分:5)

__ get(),__ set()和__call()是PHP所谓的“魔术方法”,这是一个绰号我觉得有点傻 - 我认为“钩子”更容易。无论如何,我离题了......

这些的目的是为访问对象上的数据名称(属性或方法)提供执行案例,可用于各种“聪明”的思考如变量隐藏,消息转发等

但是有一个成本 - 调用它们的调用比定义的数据库调用慢大约10倍。

答案 2 :(得分:3)

重新定义__get__set在核心课程中尤其有用。例如,如果您不希望您的配置被意外覆盖,但仍希望从中获取数据:

class Example
{
    private $config = array('password' => 'pAsSwOrD');
    public function __get($name)
    {
        return $this->config[$name];
    }
}

答案 3 :(得分:2)

魔术方法的另一个有用的应用,特别是__get__set以及__toString是模板。只需编写使用魔术方法的简单适配器,就可以使代码独立于模板引擎。如果您想要移动到另一个模板引擎,只需更改这些方法。

class View {

    public $templateFile;
    protected $properties = array();

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

    public function __get($property) {
        return @$this->properties[$property];
    }

    public function __toString() {
        require_once 'smarty/libs/Smarty.class.php';
        $smarty = new Smarty();
        $smarty->template_dir = 'view';
        $smarty->compile_dir = 'smarty/compile';
        $smarty->config_dir = 'smarty/config';
        $smarty->cache_dir = 'smarty/cache';
        foreach ($this->properties as $property => $value) {
            $smarty->assign($property, $value);
        }
        return $smarty->fetch($this->templateFile);
    }

}

这种方法的隐藏好处是您可以将View对象嵌套在另一个内:

$index = new View();
$index->templateFile = 'index.tpl';

$topNav = new View();
$topNav->templateFile = 'topNav.tpl';

$index->topNav = $topNav;

index.tpl中,嵌套看起来像这样:

<html>
<head></head>
<body>
    {$topNav}
    Welcome to Foobar Corporation.
</body>
</html>

只要echo $index;

,所有嵌套的View对象就会动态转换为字符串(确切地说是HTML)

答案 4 :(得分:1)

我认为你的代码设计很糟糕。如果您知道并做好设计,则无需在代码中使用__set()__get()。阅读代码非常重要,如果您使用的是工作室(例如Zend工作室),__set()__get()则无法看到您的类属性。

答案 5 :(得分:1)

PHP允许我们动态创建类变量,这可能会导致问题。您可以使用__set和__get方法来限制此行为。请参见下面的示例...

class Person { 
      public $name;
      public function printProperties(){
       print_r(get_object_vars($this));
      }
}

$person = new Person();
$person->name = 'Jay'; //This is valid
$person->printProperties();
$person->age = '26';  //This shouldn't work...but it does 
$person->printProperties();

为了防止上述情况,你可以这样做..

public function __set($name, $value){
    $classVar = get_object_vars($this);
    if(in_array($name, $classVar)){
    $this->$name = $value;
    }
}

希望这会有所帮助......

答案 6 :(得分:0)

他们是做“聪明”的事情。

例如,您可以使用__set()__get()与数据库通信。您的代码将是:$myObject->foo = "bar";,这可能会在后台更新数据库记录。当然你必须对此非常小心,否则你的表现会受到影响,因此引用“聪明”:)

答案 7 :(得分:0)

在处理包含应易于访问的数据的PHP对象时,

Overloading方法特别有用。在访问不存在的属性时调用__get(),在尝试编写不存在的属性时调用__set(),并在调用不存在的方法时调用__call()

例如,假设有一个管理您的配置的类:

class Config
{
    protected $_data = array();

    public function __set($key, $val)
    {
        $this->_data[$key] = $val;
    }

    public function __get($key)
    {
        return $this->_data[$key];
    }

    ...etc

}

这使得读取和写入对象变得更加容易,并且允许您在读取或写入对象时使用自定义功能进行更改。 例如:

$config = new Config();
$config->foo = 'bar';

echo $config->foo; // returns 'bar'

答案 8 :(得分:0)

使用它们的一个很好的理由是注册系统(我认为Zend Framework将其实现为注册表或配置类iirc),因此您可以执行以下操作:

$conf = new Config();
$conf->parent->child->grandchild = 'foo';

这些属性中的每一个都是自动生成的Config对象,类似于:

function __get($key) {
    return new Config($key);
}

显然如果$ conf-&gt; parent已存在,则不会调用__get()方法,因此使用它来生成新变量是一个很好的技巧。

请记住我刚才引用的代码不是功能,我只是为了举例而快速编写代码。

答案 9 :(得分:0)

可能不是世界上最干净的设计,但我遇到的情况是我有很多代码引用了类中的实例变量,即:

$obj->value = 'blah';
echo $obj->value;

但后来,我想在某些情况下设置“value”时做一些特别的事情,所以我重命名了value变量并用我需要的更改实现了__set()和__get()。

其余的代码并不知道区别。