<?php namespace Syna; class Factory { protected $sharedData = []; /** @var HelperLocator */ protected $helperLocator; /** @var ViewLocator */ protected $viewLocator; /** @var ViewLocator[] */ protected $namedLocators = []; /** * @param ViewLocator $viewLocator * @param HelperLocator $helperLocator * @param ViewLocator $layoutLocator */ public function __construct( ViewLocator $viewLocator, ?HelperLocator $helperLocator = null, ?ViewLocator $layoutLocator = null ) { $this->viewLocator = $viewLocator; $this->helperLocator = $helperLocator ?? new HelperLocator(); !$layoutLocator || $this->namedLocators['layout'] = $layoutLocator; } /** * Add a named ViewLocator $locator to this factory * * @param string $name * @param ViewLocator $locator * @return Factory */ public function addLocator(string $name, ViewLocator $locator): self { $this->namedLocators[$name] = $locator; return $this; } /** * Get a named ViewLocator or the default ViewLocator * * @param string $name * @return ViewLocator */ public function getLocator(?string $name = null): ?ViewLocator { if (!$name) { return $this->viewLocator; } return $this->namedLocators[$name] ?? null; } /** * Get the HelperLocator * * @return HelperLocator */ public function getHelperLocator(): HelperLocator { return $this->helperLocator; } /** * Add shared data * * @param array $data * @return Factory */ public function addSharedData(array $data): self { $this->sharedData = array_merge($this->sharedData, $data); return $this; } /** * Get all shared Data * * @return array */ public function getSharedData(): array { return $this->sharedData; } /** * Create a view for $name with $data * * $name can be prefixed with a locator name followed by two colons (e. g. 'mail::activation') uses the locator * named mail and searches for 'activation'. * * @param string $name * @param array $data * @return View * @throws \Exception|\LogicException */ public function view(string $name, array $data = []): View { $viewLocator = $this->viewLocator; if (strpos($name, '::', 1) !== false) { list($locatorName, $name) = explode('::', $name); if (!isset($this->namedLocators[$locatorName])) { throw new \LogicException('No locator for ' . $locatorName . ' defined'); } $viewLocator = $this->namedLocators[$locatorName]; } if (!$viewLocator->has($name)) { throw new \Exception('View ' . $name . ' not found'); } return new View($this, $viewLocator->getPath($name), $data); } /** * Creates a view for $name with $data and renders it * * If $layout is given the view will be wrapped in $layout using the layout ViewLocator. You have to define a layout * ViewLocator first. * * @param string $name * @param array $data * @param string|null $layout * @return string */ public function render(string $name, array $data = [], string $layout = null): string { $view = $this->view($name, $data); $content = $view->render(); if ($layout && isset($this->namedLocators['layout'])) { $layout = $this->view('layout::' . $layout); $layout->setSections(...array_merge($view->getSections(), ['content' => $content])); $content = $layout->render(); } return $content; } /** * Execute $function with $arguments * * If the HelperLocator has $function this helper will be preferred but a 'strtoupper' is a valid callable and will * be executed if no helper is defined for this name. * * @param View $view * @param string|callable $function * @param mixed ...$arguments * @return mixed */ public function helper(View $view, $function, ...$arguments) { if ($this->helperLocator->has($function)) { $helper = $this->helperLocator->getHelper($function); $helper->setView($view); /** @noinspection PhpMethodParametersCountMismatchInspection */ return $helper(...$arguments); } elseif (is_callable($function)) { return call_user_func($function, ...$arguments); } throw new \LogicException('$function has to be callable or a registered view helper'); } }