Я задавался вопросом, почему можно получить доступ к частным свойствам и методам черты PHP в классе, который использует эту черту.
Вот моя черта PHP: BulkMessageMembers.php:
<?php
namespace App\Traits;
trait BulkMessageMembers
{
private string $endpoint;
private string $text;
private array $phone_numbers;
private string $server_response;
public bool $sms_status = false;
public int $user_id;
}
И мой PHP-класс (это только часть кода) SMSJsonBulkMessageService.php, который использует мой Trait:
<?php
namespace App\Services;
use App\Interfaces\SMSGateway;
use App\Models\Balance;
use App\Models\Message;
use App\Models\MessageSetting;
use App\Models\UserMessageSetting;
use App\Traits\BulkMessageMembers;
use GuzzleHttp\Exception\ConnectException;
use Illuminate\Support\Collection;
class SMSJsonBulkMessageService implements SMSGateway
{
use BulkMessageMembers;
public function __construct(
private readonly UserMessageSetting $preference,
private readonly Collection $messages, private readonly MessageSetting $setting
)
{
$this->prepare();
}
private function prepare(): void
{
$this->endpoint = $this->preference->messageProvider->bulk_endpoint;
$this->messages->each(function ($message) {
$this->phone_numbers[] = sprintf('243%d', $message->phone_number);
});
$this->text = $this->messages->first()->description;
}
/**
* @throws ConnectException
* @return void
*/
public function send(): void
{
//
}
public function update(): void
{
//
}
}
Может ли кто-нибудь объяснить мне, почему из моего класса PHP я получаю доступ к частным свойствам черты? Я знаю, что trait не является классом как таковым, но его основной интерес состоит в том, чтобы уменьшить некоторые ограничения одиночного наследования, позволяя разработчику свободно повторно использовать наборы методов в нескольких независимых классах, живущих в разных иерархиях классов.
Пожалуйста, нужно разъяснение.
@apokryfos В других языках могут быть механизмы «горизонтального повторного использования», которые позволяют скрывать / инкапсулировать функциональность в единицу многократного использования; «сплющивание» — это дизайнерское решение, а не неизбежное следствие горизонтального повторного использования.






Распространенное объяснение трейтов состоит в том, что они являются «копированием и вставкой с помощью компилятора»: когда вы включаете трейт в конкретный класс, содержимое этого трейта вставляется в определение класса, как если бы вы написали его вручную.
Между членами, вставленными чертой, и членами, написанными непосредственно в определении класса, не поддерживается никакого различия; и между классом и трейтом, из которого были скопированы члены, не поддерживается никакой связи. В некоторых случаях есть дополнительные ограничения, проверяемые компилятором, или переименование, которое выполняется как часть оператора use, но в простом случае это точно так же, как если бы вы скопировали код из одного файла в другой.
В вашем случае код, который скомпилирован, по существу таков:
<?php
namespace App\Services;
use App\Interfaces\SMSGateway;
use App\Models\Balance;
use App\Models\Message;
use App\Models\MessageSetting;
use App\Models\UserMessageSetting;
use App\Traits\BulkMessageMembers;
use GuzzleHttp\Exception\ConnectException;
use Illuminate\Support\Collection;
class SMSJsonBulkMessageService implements SMSGateway
{
private string $endpoint;
private string $text;
private array $phone_numbers;
private string $server_response;
public bool $sms_status = false;
public int $user_id;
public function __construct(
private readonly UserMessageSetting $preference,
private readonly Collection $messages, private readonly MessageSetting $setting
)
{
$this->prepare();
}
private function prepare(): void
{
$this->endpoint = $this->preference->messageProvider->bulk_endpoint;
$this->messages->each(function ($message) {
$this->phone_numbers[] = sprintf('243%d', $message->phone_number);
});
$this->text = $this->messages->first()->description;
}
/**
* @throws ConnectException
* @return void
*/
public function send(): void
{
//
}
public function update(): void
{
//
}
}
Глядя на этот код, неудивительно, что метод prepare имеет доступ к $this->phone_numbers.
Помимо отличного ответа @IMSoP, я думаю, что цитирование RFC, которое представило эту функцию, вносит ясность, особенно последнее предложение:
Сглаживание свойство
Как уже упоминалось, множественное наследование и миксины — это сложные механизмы. Черты - это альтернатива, которая была разработана, чтобы не накладывать дополнительную семантику на классы. Трейты — это только сущности буквального кода, записанного в ваших исходных файлах. Нет понятия о трейтах во время выполнения. Они используются для группировки методов и повторного использования кода и полностью объединяются в классы, составленные из них. Это почти как поддерживаемый язык и отказоустойчивый механизм копирования и вставки для создания классов.
Мой вопрос: почему вы не ожидаете, что они будут доступны? Трейты не являются классами, и использование трейтов не является наследованием, поэтому поведение не должно быть привязано к этим конструкциям.