Почему частные члены черты PHP доступны из класса PHP?

Я задавался вопросом, почему можно получить доступ к частным свойствам и методам черты 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 02.02.2023 15:31

@apokryfos В других языках могут быть механизмы «горизонтального повторного использования», которые позволяют скрывать / инкапсулировать функциональность в единицу многократного использования; «сплющивание» — это дизайнерское решение, а не неизбежное следствие горизонтального повторного использования.

IMSoP 02.02.2023 15:50
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
2
2
53
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Распространенное объяснение трейтов состоит в том, что они являются «копированием и вставкой с помощью компилятора»: когда вы включаете трейт в конкретный класс, содержимое этого трейта вставляется в определение класса, как если бы вы написали его вручную.

Между членами, вставленными чертой, и членами, написанными непосредственно в определении класса, не поддерживается никакого различия; и между классом и трейтом, из которого были скопированы члены, не поддерживается никакой связи. В некоторых случаях есть дополнительные ограничения, проверяемые компилятором, или переименование, которое выполняется как часть оператора 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, которое представило эту функцию, вносит ясность, особенно последнее предложение:

Сглаживание свойство

Как уже упоминалось, множественное наследование и миксины — это сложные механизмы. Черты - это альтернатива, которая была разработана, чтобы не накладывать дополнительную семантику на классы. Трейты — это только сущности буквального кода, записанного в ваших исходных файлах. Нет понятия о трейтах во время выполнения. Они используются для группировки методов и повторного использования кода и полностью объединяются в классы, составленные из них. Это почти как поддерживаемый язык и отказоустойчивый механизм копирования и вставки для создания классов.

Другие вопросы по теме