17

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

trait Address {
    private $address;

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

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

我发现使用 trait 两次的唯一方法是:

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

4 回答 4

21

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

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

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

您可以将方法设为私有,然后__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 …
        }
    }
}

但这只是一罐蠕虫。

换句话说,你不能理智地做你试图用特质做的事情。要么使用两个不同的特征。或者使用好的旧聚合并添加具体的代理方法。

于 2012-11-27T11:29:13.330 回答
3

我参加聚会可能有点晚了,但是您尝试创建的行为不应该由特征所涵盖,而是由简单的对象组合所涵盖。

<?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 {}
}
于 2017-11-14T10:22:22.007 回答
2

我确认你可以多次使用同一个函数的别名,这让我很惊讶。尽管 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

于 2012-11-27T18:54:35.380 回答
2

几年后,我跟进了错误 63629中的评论并产生了以下内容:

<?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";

哪个输出

High Street, Luton
Business Name, London

可以办到!(感谢 Dave Farrell,他的回答启发了这一点。)debug_backtrace的论点是为了尽量减少内存使用,我不确定这对性能有多大影响。

于 2019-07-29T12:43:47.583 回答