这是在PHP类中处理getter/setter的合理方法吗?

这是在PHP类中处理getter/setter的合理方法吗?,php,oop,Php,Oop,我将尝试一下这个问题的形式,我非常愿意接受关于更好的处理方法的建议 我不想在问题中只转储一堆代码,所以我将该类的代码发布在refactormycode上 我的想法是,人们可以在这里发布代码片段,或者在refactormycode上进行更改,并将链接发布回他们的重构。在此基础上,我将补票并接受答案(假设有明确的“获胜者”) 无论如何,在课堂上: 我看到了很多关于getter/setter类方法的争论,直接访问简单的属性变量更好,还是每个类都定义了显式的get/set方法更好,诸如此类。我喜欢使用

我将尝试一下这个问题的形式,我非常愿意接受关于更好的处理方法的建议

我不想在问题中只转储一堆代码,所以我将该类的代码发布在
refactormycode

我的想法是,人们可以在这里发布代码片段,或者在
refactormycode
上进行更改,并将链接发布回他们的重构。在此基础上,我将补票并接受答案(假设有明确的“获胜者”)

无论如何,在课堂上:

我看到了很多关于getter/setter类方法的争论,直接访问简单的属性变量更好,还是每个类都定义了显式的get/set方法更好,诸如此类。我喜欢使用显式方法,以防以后需要添加更多逻辑。然后,您不必修改任何使用该类的代码。但是我讨厌有一百万个这样的函数:

public function getFirstName()
{
   return $this->firstName;
}
public function setFirstName($firstName)
{
   return $this->firstName;
}
/**
* Handles default set and get calls
*/
public function __call($method, $params) {

    //did you call get or set
    if ( preg_match( "|^[gs]et([A-Z][\w]+)|", $method, $matches ) ) {

        //which var?
        $var = strtolower($matches[1]);

        $r = new ReflectionClass($this);
        $properties = $r->getdefaultProperties();

        //if it exists
        if ( array_key_exists($var,$properties) ) {
            //set
            if ( 's' == $method[0] ) {
                $this->$var = $params[0];
            }
            //get
            elseif ( 'g' == $method[0] ) {
                return $this->$var;
            }
        }
    }
}
现在我确信我不是第一个这样做的人(我希望有更好的方法,有人可以向我推荐)

基本上,PropertyHandler类有一个_调用魔术方法。任何通过_调用以“get”或“set”开头的方法都会被路由到将值设置或检索到关联数组中的函数。数组中的键是获取或设置后调用方法的名称。因此,如果进入_调用的方法是“getFirstName”,那么数组键是“FirstName”

我喜欢使用_调用,因为它会自动处理子类已经定义了“getFirstName”方法的情况。我的印象(我可能错了)是uuu get&uuuu set魔术方法不能做到这一点

下面是一个如何工作的示例:

class PropTest extends PropertyHandler
{
    public function __construct()
    {
        parent::__construct();
    }
}

$props = new PropTest();

$props->setFirstName("Mark");
echo $props->getFirstName();
请注意,PropTest实际上没有“setFirstName”或“getFirstName”方法,PropertyHandler也没有。所做的只是操纵数组值

另一种情况是,您的子类已经在扩展其他内容。因为在PHP中不能有真正的多重继承,所以可以使子类有一个PropertyHandler实例作为私有变量。您必须再添加一个函数,但事情的行为方式完全相同

class PropTest2
{
    private $props;

    public function __construct()
    {
        $this->props = new PropertyHandler();
    }

    public function __call($method, $arguments)
    {
        return $this->props->__call($method, $arguments);
    }
}

$props2 = new PropTest2();

$props2->setFirstName('Mark');
echo $props2->getFirstName();
请注意,子类有一个_调用方法,它只将所有内容传递给PropertyHandler _调用方法


反对以这种方式处理getter和setter的另一个很好的理由是,这样做确实很难记录

事实上,基本上不可能使用任何类型的文档生成工具,因为不存在要记录的显式方法


我现在几乎放弃了这种方法。这是一个有趣的学习练习,但我认为它牺牲了太多的清晰度。

我这样做的方式如下:

class test {
    protected $x='';
    protected $y='';

    function set_y ($y) {
        print "specific function set_y\n";
        $this->y = $y;
    }

    function __call($function , $args) {
        print "generic function $function\n";
        list ($name , $var ) = split ('_' , $function );
        if ($name == 'get' && isset($this->$var)) {
            return $this->$var;
        }
        if ($name == 'set' && isset($this->$var)) {
            $this->$var= $args[0];
            return;
        }
        trigger_error ("Fatal error: Call to undefined method test::$function()");
    }
}

$p = new test();
$p->set_x(20);
$p->set_y(30);
print $p->get_x();
print $p->get_y();

$p->set_z(40);
将输出(为清晰起见添加了换行符)


是的,这是正确的,变量必须手动声明,但我发现这样更好,因为我担心setter中的输入错误

$props2->setFristName('Mark');

将自动生成一个新属性(FristName而不是FirstName),这将使调试更加困难。

我喜欢使用方法,而不仅仅是使用公共字段,但我的问题是PHP的默认实现(使用u get()和u set())或者,您的自定义实现是您没有基于每个属性建立getter和setter。我的问题是,添加“more logic later”需要添加应用于使用getter/setter访问的所有属性的覆盖逻辑,或者使用if或switch语句来评估正在访问的属性,以便应用特定逻辑

我喜欢你的解决方案,也为你鼓掌——我只是不满意PHP在隐式访问器方法方面的局限性。

我的问题是,添加“more logic later”需要添加应用于使用getter/setter访问的所有属性的覆盖逻辑,或者使用if或switch语句来评估正在访问的属性,以便应用特定逻辑

不完全是这样。以我的第一个例子:

class PropTest extends PropertyHandler
{
    public function __construct()
    {
        parent::__construct();
    }
}

$props = new PropTest();

$props->setFirstName("Mark");
echo $props->getFirstName();
假设我需要添加一些逻辑来验证名字。我所要做的就是在我的子类中添加一个setFirstName方法,然后自动使用该方法

class PropTest extends PropertyHandler
{
    public function __construct()
    {
        parent::__construct();
    }

    public function setFirstName($name)
    {
        if($name == 'Mark')
        {
            echo "I love you, Mark!";
        }
    }
}
我只是不满意PHP在隐式访问器方法方面的局限性

我完全同意。我喜欢Python处理这个问题的方式(我的实现只是对它的拙劣模仿)。

@Mark


但是,即使是您的方法也需要一个新的方法声明,这在某种程度上剥夺了将其放入方法中以便您可以添加更多逻辑的优势,因为添加更多逻辑需要老式的方法声明。在它的默认状态下(这是它在检测/做什么方面给人留下深刻印象的地方),您的技术(在PHP中)与公共字段相比没有任何优势。您限制了对字段的访问,但通过没有任何限制的访问器方法授予全权。我不知道在任何语言中,未经检查的显式访问器都比公共字段有任何优势,但是如果我错了,人们可以而且应该自由地纠正我。

我总是以类似的方式处理这个问题,使用一个u调用,它在我的许多类中几乎都是锅炉板代码。但是,它很紧凑,并且使用反射类仅为您已经设置的属性添加getter/setter(不会添加新的)。简单地显式添加getter/setter将添加更复杂的功能。预计是

代码如下所示:

public function getFirstName()
{
   return $this->firstName;
}
public function setFirstName($firstName)
{
   return $this->firstName;
}
/**
* Handles default set and get calls
*/
public function __call($method, $params) {

    //did you call get or set
    if ( preg_match( "|^[gs]et([A-Z][\w]+)|", $method, $matches ) ) {

        //which var?
        $var = strtolower($matches[1]);

        $r = new ReflectionClass($this);
        $properties = $r->getdefaultProperties();

        //if it exists
        if ( array_key_exists($var,$properties) ) {
            //set
            if ( 's' == $method[0] ) {
                $this->$var = $params[0];
            }
            //get
            elseif ( 'g' == $method[0] ) {
                return $this->$var;
            }
        }
    }
}
将其添加到已声明默认属性的类中,如:

class MyClass {
    public $myvar = null;
}

$test = new MyClass;
$test->setMyvar = "arapaho";

echo $test->getMyvar; //echos arapaho    
反思课