处理PHP属性像C#中的那样(getters和setters)

4

我一直在研究是否可以像C#语言一样在PHP类中使用属性,我发现这些问题提供了一些很好的答案和指引,但并没有真正帮助到我:

尽管,正如我所说,这些提供了很好的答案,但我仍然不能决定如何做,例如,我想能够做这样的事情:

class Foo 
{
    public $bar
    {
        get { return $this->bar; }
        set { $this->bar = value; }
    }
}

但我无法做到!

在PHP的限制条件下,最好的方法是什么?

我能想到的一种方法是这样的:

class user
{
    private static $link;
    private static $init;

    private function __construct ()
    {
        global $link;
        static::$link = $link;
    }

    public static function __INIT__ ()
    {
        return static::$init = (null === static::$init ? new self() : static::$init);
    }

    public static $username;

    public function username( $value = '' )
    {
        if ( $value == '' || empty ( $value ) || !$value )
            return static::$username;
        else
            static::$username = $value;
    }
}

没有使用魔术方法的 setUsernamegetUsername 有什么问题吗? - S.Pols
@S.Pols,你什么意思? - Can O' Spam
为什么不直接创建一个函数 setUsername($variable) 和一个 getUsername(),而不是试图将函数用作getter和setter(在我看来这是错误的)。在我看来,一个函数应该只有一个职责。 - S.Pols
1
@S.Pols,我同意,一个函数,一个目的,但对于这个问题,我真正寻找的是如何在PHP中实现类似C#的实现方式。 - Can O' Spam
如果($value =='' || empty($value)||!$value),你几乎肯定不想那样做,因为它将阻止设置多个有效值。使用“null”,并测试“is_null”。当然,这仍然会使将值设置为空值变得不可能。将获取和设置组合成一个函数的固有缺陷。 - ToolmakerSteve
2个回答

4
这就是PHP的魔术方法的作用。它们允许您通过中介方法动态设置属性。这里,让我给您举个例子:
abstract class GetterSetterExample
{
    public function __get($name)
    {
        $method = sprintf('get%s', ucfirst($name));

        if (!method_exists($this, $method)) {
            throw new Exception();
        }

        return $this->$method();
    }

    public function __set($name, $v)
    {
        $method = sprintf('set%s', ucfirst($name));

        if (!method_exists($this, $method)) {
            throw new Exception();
        }

        $this->$method($v);
    }
}

class Cat extends GetterSetterExample
{
    protected $furColor;

    protected function getFurColor()
    {
        return $this->furColor;
    }

    protected function setFurColor($v)
    {
        $this->furColor = $v;
    }
}

$cat = new Cat();
$cat->furColor = 'black';

是的…… 这也让我感到恐惧。

这就是为什么大多数人满足于使用像 Cat 中那样的公共方法。显然,有关添加“真正”的getter和setter的讨论正在进行中,但是在那些由于可读性、语义和原则等问题而不喜欢它们的人之间存在很多冲突...


这似乎比它需要的要复杂得多...使用魔术方法与在我的问题中举例的自定义方法(请参见用户名方法和属性)相比,有什么优点? - Can O' Spam
@SamSwift 这根本没有任何优点。从前我也是以你现在的方式实现 getters 和 setters,但 PHP 的世界正在学习更青睐于更加静态声明和类型安全的代码,而唯一的方法就是使用具有单一返回类型的真正函数。 - Flosculus
所以,与其有一个返回 string|void 的函数,更被广泛接受的是采用你的示例中实现的方式?我个人认为这样做有用,并且有一定的优势(代码更快,只需使用 =),但同时,我也非常喜欢 C# 的 get { ... } set { ... } 属性。 - Can O' Spam
@SamSwift 不,我的意思是尽管上面的解决方案模拟了你想要的“属性”风格API,但它因为你所担心的原因而失败(它非常复杂)。S.Pols发表的评论是普遍接受的解决方案。然而,有些人认为属性不应存在,类也不应该打开它们的属性,而只应执行操作。 - Flosculus

3

也许你需要考虑它是否真的有必要。这只是一个简写来编写获取器和设置器。

private String _name;

public String Name
{
    get
    {
        return _name;
    }
    set
    {
        _name = value;
    }
}

与以下代码完全等效:

private $_name;

public function getName()
{
    return $_name;
}

public function setName($name)
{
    $_name = $name;
}

只有调用方式有所不同:

C#  : Object.Name = "This is my Name";
PHP : Object->setName ("This is my Name");

如果你真的偏爱C#语法(我认为这只是一个习惯),我认为Flosculus的回答是唯一的选择。如果你意识到代码实现的功能,它并不那么复杂。

__set() is run when writing data to inaccessible properties.
__get() is utilized for reading data from inaccessible properties.

在这个例子中,由于furColor是一个受保护的变量,所以无法访问。
那么在这里$cat->furColor = 'black会发生什么呢?
  • furColor无法访问,因此调用了魔术方法__set。
  • 它在GetterSetterExample中创建了一个名为setFurColor的setter,并检查其是否存在。
  • 是的,它确实存在,因为Cat实现了它。
  • setFurColor被调用并设置了变量。
最终,这段代码与上面示例中的常规getter和setter执行的工作完全相同。这只是一种“解决办法”,以模拟C#的属性。 回到我在本回答开头提出的第一个问题,是否必要?我认为不是。PHP不支持属性,因此无法清楚地说明为什么要尝试模拟它。 常规的getter和setter是正确的方法,正如我展示的示例一样,可以完成相同的任务。 魔术方法很慢,如果您想以类型安全的方式进行操作,祝您好运。Flosculus的示例代码并不复杂,但这种方法可能会变得(不必要地)复杂。

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