skip to Main Content

I wondered why it is possible to access to the private properties and methods of a PHP Trait in a class that uses that Trait.

Here is my PHP Trait: BulkMessageMembers.php:

<?php

namespace AppTraits;

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;
}

And my PHP class (This is only a portion of code.) SMSJsonBulkMessageService.php that uses my Trait:

<?php

namespace AppServices;

use AppInterfacesSMSGateway;
use AppModelsBalance;
use AppModelsMessage;
use AppModelsMessageSetting;
use AppModelsUserMessageSetting;
use AppTraitsBulkMessageMembers;
use GuzzleHttpExceptionConnectException;
use IlluminateSupportCollection;

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
   {
       //
   }
}

Can someone explain to me why from my PHP class I access the private properties of a Trait? I know trait is not a class per se, but its main interest is to reduce some limitations of single inheritance by enabling a developer to reuse sets of methods freely in several independent classes living in different class hierarchies.

Need clarification please.

2

Answers


  1. A common explanation of traits is that they are "compiler-assisted copy-and-paste": when you incorporate a trait into a concrete class, the contents of that trait are inserted into the class definition as though you had written them by hand.

    No distinction is maintained between members inserted by the trait, and members written directly in the class definition; and no relationship is maintained between the class and the trait the members were copied from. In some cases, there are additional constraints checked by the compiler, or renaming that is performed as part of the use statement, but in the simple case, it is exactly as though you had copied the code from one file to the other.

    In your case, the code that is compiled is essentially this:

    <?php
    
    namespace AppServices;
    
    use AppInterfacesSMSGateway;
    use AppModelsBalance;
    use AppModelsMessage;
    use AppModelsMessageSetting;
    use AppModelsUserMessageSetting;
    use AppTraitsBulkMessageMembers;
    use GuzzleHttpExceptionConnectException;
    use IlluminateSupportCollection;
    
    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
       {
           //
       }
    }
    

    Looking at that code, it should be no surprise that the prepare method has access to $this->phone_numbers.

    Login or Signup to reply.
  2. Beyond @IMSoP’s great answer, I think quoting the RFC that introduced the feature brings clarity, specifically the last sentence:

    The Flattening Property

    As already mentioned, multiple inheritance and Mixins are complex mechanisms. Traits are an alternative which have been designed to impose no additional semantics on classes. Traits are only entities of the literal code written in your source files. There is no notion about Traits at runtime. They are used to group methods and reuse code and are totally flattened into the classes composed from them. It is almost like a language supported and failsafe copy’n’paste mechanism to build classes.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search