php Как реализовать "конструктор" в трейте -- для значений которые нужно инициллизировать один раз (trait)

Предположим, что у был класс, которому для печати данных разными методами в своем конструкторе приходилось (чтобы не создавать объект каждый раз) одному из своих полей (->outputWriter) присваивать объект конкретного класса (\Codeception\Lib\Console\Output), занимающегося печатью, после чего данное поле использовалось методами:


class IFFAcceptanceTester extends \AcceptanceTester
{
    public function __construct(\Codeception\Scenario $scenario) {
        parent::__construct($scenario);
        
        $this->outputWriter = new \Codeception\Lib\Console\Output([]); 
    }
    
    /**
     * Хранит класс, с помощью которого можно писать в консоль
     * @var \Codeception\Lib\Console\Output
     */
    protected $outputWriter = null; 
    
    /**
     * Специальный маркер для перевод строки один раз за время работы теста
     * 
     * @var boolean 
     */
    protected $newLineAlreadyExists = false;
    
    /**
     * Log additinal info into console
     * Вывод дополнительной технической информации о процессе работы теста в консоль.
     * 
     * Поддерживает тэги symfony/console @link https://symfony.com/doc/current/console/...
     * 
     * @param type $str
     */
    public function log($str)
    {
        $this->checkNewLineNeed();
        $message ="⚑ $str";
        
        // echo ("$message \n");
        $this->outputWriter->writeln("$message");
    }
    
    /**
     * Обёртка над print_r() для тестировщика $I
     * 
     * @param type $value
     * @param string $comment
     */
    public function pre($value, $comment = '')
    {
        $this->checkNewLineNeed();
        
        if ($comment) {
            $comment .= ':';
        }
        $message =" $comment " . print_r($value, true);
        $this->outputWriter->writeln("$message");
    }
    
    /**
     * Вызывается один раз для перевода строки после вывода имени теста
     */
    protected function checkNewLineNeed()
    {
        if (!$this->newLineAlreadyExists) {
            echo "\n";
            $this->newLineAlreadyExists = true;
        }
    }
}

Теперь мы хотим вынести те же методы ->log() и ->pre() в трейт, а с ними и то, на что они опираются -- ведь это позволит использовать логгирование, не только в классах тестера, но и в других независимых от него модулях системы.

Для этого делаем следующее:

  1. Для инициаллизации $this->outputWriter заводим специальный метод -- назовем его, что не удивительно, outputWriter(), который просто проверяет инициллизировано ли уже значение
  2. Заменяем все обращения
    $this->outputWriter

    заменям на вызов

    $this->outputWriter()

    (уже метода)

В результате получаем:

trait IFFLoggerTrait
{
    /**
     * Хранит класс, непосредственно занимающийся печать.
     * @var object 
     */
    protected $outputWriter = null; 
    
    protected function outputWriter() {

        if (!$this->outputWriter) { // если ещё не инициллизировано
            $this->outputWriter = new \Codeception\Lib\Console\Output([]); 
        }
        
       return $this->outputWriter;
    }

    /**
     * Специальный маркер для перевод строки один раз за время работы теста
     * 
     * @var boolean 
     */
    protected $newLineAlreadyExists = false;
    
    /**
     * Log additinal info into console
     * Вывод дополнительной технической информации о процессе работы теста в консоль.
     * 
     * Поддерживает тэги symfony/console @link https://symfony.com/doc/current/console/...
     * 
     * @param type $str
     */
    public function log($str)
    {
        $this->checkNewLineNeed();
        $message ="⚑ $str";
        
        // echo ("$message \n");
        $this->outputWriter()->writeln("$message");
    }
    
    /**
     * Обёртка над print_r() для тестировщика $I
     * 
     * @param type $value
     * @param string $comment
     */
    public function pre($value, $comment = '')
    {
        $this->checkNewLineNeed();
        
        if ($comment) {
            $comment .= ':';
        }
        $message ="$comment " . print_r($value, true);
        $this->outputWriter()->writeln("$message");
    }
    
    /**
     * Вызывается один раз для перевода строки после вывода имени теста
     */
    protected function checkNewLineNeed()
    {
        if (!$this->newLineAlreadyExists) {
            echo "\n";
            $this->newLineAlreadyExists = true;
        }
    }
}