Na podstawie frameworka phpiggy, na którym (między innymi) się wzorowaliśmy pisząc nasz projekt MVC, pokazujemy jak działa wstrzykiwanie zależności przez konstruktor. Do dzieła.

Na początku interfejs middleware:

interface MiddlewareInterface
{
  public function process(callable $next);
}

Przykładowe middleware dla stron „tylko dla gości”:

class GuestOnlyMiddleware implements MiddlewareInterface
{
  public function process(callable $next)
  {
    if (!empty($_SESSION['user'])) {
      redirectTo('/');
    }

    $next();
  }
}

Równie dobrze można napisać middleware dla stron „tylko dla dorosłych” (sprawdzając cookie czy pełnoletność została potwierdzona) i tak dalej.

Nas interesuje wstrzykiwanie zależności przez konstruktor i używanie ich wewnątrz klasy:

class TemplateDataMiddleware implements MiddlewareInterface
{
  public function __construct(private TemplateEngine $view)
  {
  }

  public function process(callable $next)
  {
    $this->view->addGlobal('title', 'Expense Tracking App');

    $next();
  }
}

Sama klasa wygląda tak:

class TemplateEngine
{
  private array $globalTemplateData = [];

  //(...)

  public function addGlobal(string $key, mixed $value)
  {
    $this->globalTemplateData[$key] = $value;
  }
}

Mamy tu globalTemplateData i funkcję addGlobal, której użyliśmy.

Potem globalne zmienne są wypakowywane razem z lokalnymi, tj. tylko dla konkretnego widoku:

class TemplateEngine
{
  private array $globalTemplateData = [];

  //(...)

  public function render(string $template, array $data = [])
  {
    extract($data, EXTR_SKIP);
    extract($this->globalTemplateData, EXTR_SKIP);

    ob_start();

    include $this->resolve($template);

    $output = ob_get_contents();

    ob_end_clean();

    return $output;
  }

 //(...)
}

Najpierw mamy extract przekazanych do rendera, później globalnych, ale bez kolizji (render może globalną nadpisać).

Dalej dość prosty mechanizm bez żadnych własnych składni, co takie super nie jest, ale ok.

Sama klasa TemplateEngine też korzysta z promocji konstruktora:

class TemplateEngine
{
  private array $globalTemplateData = [];

  public function __construct(private string $basePath)
  {
  }

  //(...)

  public function resolve(string $path)
  {
    return "{$this->basePath}/{$path}";
  }

  //(...)
}

Polega to na tym, że nie musimy dwa razy pisać „private string basePath” ani do this przypisywać, a działa jak normalne property, które zapisujemy konstruktorem.