如何将变量设为私有特征?

时间:2012-11-27 10:59:57

标签: php oop traits

我想在一个类中多次重用一个功能。此功能依赖于私有变量:

trait Address {
    private $address;

    public function getAddress() {
        return $this->address;
    }

    public function setAddress($address) {
        $this->address = $address;
    }
}

我找到use the trait twice的唯一方法如下:

class User  {
    use Address {
        getAddress as getHomeAddress;
        setAddress as setHomeAddress;

        getAddress as getWorkAddress;            
        setAddress as setWorkAddress;
    }
}

问题是,通过这样做,私有变量$address在不同的方法之间共享,代码将无法按预期工作:

$user = new User();
$user->setHomeAddress('21 Jump Street');
echo $user->getWorkAddress(); // 21 Jump Street

是否有一个真正使用特征两次的解决方案,而不共享其私有变量?

4 个答案:

答案 0 :(得分:19)

使用use声明特征不会创建该特征的实例。特征基本上只是复制并粘贴到使用类中的代码。 as只会为该方法创建一个别名,例如它会添加类似

的内容
public function getHomeAddress()
{
    return $this->getAddress();
}

到您的User类。但它仍然只是一个特征。不会有两个不同的$address属性,只有一个属性。

您可以将方法设为私有,然后通过方法名称上的switch / casing通过__call委托对它进行任何公共调用,并使用数组作为地址,例如

trait Address {
    private $address = array();

    private function getAddress($type) {
        return $this->address[$type];
    }

    private function setAddress($type, $address) {
        $this->address[$type] = $address;
    }

    public function __call($method, $args) {
        switch ($method) {
            case 'setHomeAddress':
                return $this->setAddress('home', $args[0]);
            // more cases …
        }
    }
}

但这只是一堆蠕虫。

换句话说,你不能理所当然地做你想要做的特质。要么使用两种不同的特征。或者使用旧的聚合并添加具体的代理方法。

答案 1 :(得分:2)

我可能对派对来说有点晚了,但是你想要创造的行为并不是应该被特质所涵盖的,而是通过简单的对象构成。

<?php
class Adddress {
    private $street;
    private $number;
    public function __construct(string $street, ?string $number) {}
    public function street() : string {}
    public function number() : string {}
}
class User {
    private $homeAddress;
    private $workAddress;
    public function getHomeAddress() : Address {}
    public function setHomeAddress(Address $homeAddress) : self {}
    public function getWorkAddress() : Address {}
    public function setWorkAddress(Address $workAddress) : self {}
}

答案 2 :(得分:2)

几年下来,我继续关注bug 63629中的评论,并得出了以下结论:

{'a':300, 'b':200, 'c':300, 'd':400}

输出

<?php
trait TestTrait
{
    private $addresses = [];
    public function getAddress() {
        $calledAs = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0]['function'];
        return $this->addresses[substr($calledAs, 3)];
    }

    public function setAddress($address) {
        $calledAs = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0]['function'];
        $this->addresses[substr($calledAs, 3)] = $address;
    }
}
class TestClass
{
    use TestTrait { getAddress as getHomeAddress; setAddress as setHomeAddress; }
    use TestTrait { getAddress as getWorkAddress; setAddress as setWorkAddress; }
}
$c = new TestClass();

$c->setHomeAddress("High Street, Luton");
echo $c->getHomeAddress();
echo "\n";
$c->setWorkAddress("Business Name, London");
echo $c->getWorkAddress();
echo "\n";

可以做到! (多亏了Dave Farrell的回答启发了这一观点。)debug_backtrace的论据是试图最大程度地减少内存使用,但我不确定这对性能有多大影响。

答案 3 :(得分:1)

我确认你可以多次为同一个功能添加别名,这对我来说是一个惊喜。虽然ZendStudio似乎只对该函数的最后一个别名进行“代码辅助”。

如果Trait函数可以确定它被称为的名称,那么能够多次别名同一个函数可能会产生一些有趣的行为。但似乎我们无法确定特质函数中的“别名”函数。

这是我的测试代码:

<?php
trait TestTrait
{
    public function test() { print __CLASS__ . ', ' . __TRAIT__ . ', ' . __METHOD__ . ', ' . __FUNCTION__  . "\n"; }
}
class TestClass
{
    use TestTrait { test as test1; test as test2; }
}
$c = new TestClass();
$c->test1();
$c->test2();

输出:

TestClass, TestTrait, TestTrait::test, test
TestClass, TestTrait, TestTrait::test, test

为特征函数添加一个新的 ALIAS 常量可能会很好,以确定它们被调用的别名。

事实上我已经为此创建了PHP功能请求:

https://bugs.php.net/bug.php?id=63629