Как сделать переменную приватной для черты?
Я хотел бы повторно использовать функциональность несколько раз в одном классе. Эта функция зависит от частной переменной:
trait Address {
private $address;
public function getAddress() {
return $this->address;
}
public function setAddress($address) {
$this->address = $address;
}
}
Единственный способ, которым я нашел использовать признак дважды, выглядит следующим образом:
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
Есть ли решение действительно использовать признак дважды, не разделяя его частные переменные?
Ответы
Ответ 1
Объявление признака с помощью use
не создаст экземпляр этой черты. Черты - это просто код, который копируется и вставляется в класс использования. 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 …
}
}
}
Но это всего лишь банда червей.
Другими словами, вы не можете делать то, что вы пытаетесь сделать с чертами. Либо используйте две разные черты. Или используйте хорошую старую агрегацию и добавьте конкретные прокси-методы.
Ответ 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
Ответ 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 {}
}
Ответ 4
Несколько лет спустя я прокомментировал комментарий в ошибке 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
Это можно сделать! (Благодарю Дэйва Фаррелла, чей ответ вдохновил его на это.) Аргументы в пользу debug_backtrace - это попытка минимизировать использование памяти, я не уверен, насколько это влияет на производительность.