'FOF30\\View\\Engine\\BladeEngine', '.php' => 'FOF30\\View\\Engine\\PhpEngine', ]; /** * All of the finished, captured sections. * * @var array */ protected $sections = []; /** * The stack of in-progress sections. * * @var array */ protected $sectionStack = []; /** * The number of active rendering operations. * * @var int */ protected $renderCount = 0; /** * Aliases of view templates. For example: * * array('userProfile' => 'site://com_foobar/users/profile') * * allows you to do something like $this->loadAnyTemplate('userProfile') to display the frontend view template * site://com_foobar/users/profile. You can also alias one view template with another, e.g. * 'site://com_something/users/profile' => 'admin://com_foobar/clients/record' * * @var array */ protected $viewTemplateAliases = []; /** * Constructor. * * The $config array can contain the following overrides: * name string The name of the view (defaults to the view class name) * template_path string The path of the layout directory * layout string The layout for displaying the view * viewFinder ViewTemplateFinder The object used to locate view templates in the filesystem * viewEngineMap array Maps view template extensions to view engine classes * * @param Container $container The container we belong to * @param array $config The configuration overrides for the view * * @return View */ public function __construct(Container $container, array $config = []) { $this->container = $container; $this->config = $config; // Get the view name if (isset($this->config['name'])) { $this->name = $this->config['name']; } $this->name = $this->getName(); // Set the default template search path if (array_key_exists('template_path', $this->config)) { // User-defined dirs $this->setTemplatePath($this->config['template_path']); } else { $this->setTemplatePath($this->container->thisPath . '/View/' . ucfirst($this->name) . '/tmpl'); } // Set the layout if (array_key_exists('layout', $this->config)) { $this->setLayout($this->config['layout']); } // Apply the viewEngineMap if (isset($config['viewEngineMap'])) { // If the overrides are a string convert it to an array first. if (!is_array($config['viewEngineMap'])) { $temp = explode(',', $config['viewEngineMap']); $config['viewEngineMap'] = []; foreach ($temp as $assignment) { $parts = explode('=>', $assignment, 2); if (count($parts) != 2) { continue; } $parts = array_map(function ($x) { return trim($x); }, $parts); $config['viewEngineMap'][$parts[0]] = $parts[1]; } } /** * We want to always have a sane fallback to plain .php template files at the end of the view engine stack. * For this to happen to we need to remove it from the current stack, disallow overriding it in the * viewEngineMap overrides, merge the overrides and then add the fallback at the very end of the stack. * * In previous versions we didn't do that which had two side effects: * 1. It was no longer a fallback, it was in the middle of the stack * 2. Any new template engines using a .something.php extension wouldn't work, see * https://github.com/akeeba/fof/issues/694 */ // Do not allow overriding the fallback .php handler if (isset($config['viewEngineMap']['.php'])) { unset ($config['viewEngineMap']['.php']); } // Temporarily remove the fallback .php handler $phpHandler = $this->viewEngineMap['.php'] ?? PhpEngine::class; unset ($this->viewEngineMap['.php']); // Add the overrides $this->viewEngineMap = array_merge($this->viewEngineMap, $config['viewEngineMap']); // Push the fallback .php handler to the end of the view engine map stack $this->viewEngineMap = array_merge($this->viewEngineMap, [ '.php' => $phpHandler ]); } // Set the ViewFinder $this->viewFinder = $this->container->factory->viewFinder($this); if (isset($config['viewFinder']) && !empty($config['viewFinder']) && is_object($config['viewFinder']) && ($config['viewFinder'] instanceof ViewTemplateFinder)) { $this->viewFinder = $config['viewFinder']; } // Apply the registered view template extensions to the view finder $this->viewFinder->setExtensions(array_keys($this->viewEngineMap)); // Apply the base URL $this->baseurl = $this->container->platform->URIbase(); } /** * Magic get method. Handles magic properties: * $this->input mapped to $this->container->input * * @param string $name The property to fetch * * @return mixed|null */ public function __get($name) { // Handle $this->input if ($name == 'input') { return $this->container->input; } // Property not found; raise error $trace = debug_backtrace(); trigger_error( 'Undefined property via __get(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_NOTICE); return null; } /** * Method to get the view name * * The model name by default parsed using the classname, or it can be set * by passing a $config['name'] in the class constructor * * @return string The name of the model * * @throws Exception */ public function getName() { if (empty($this->name)) { $r = null; if (!preg_match('/(.*)\\\\View\\\\(.*)\\\\(.*)/i', get_class($this), $r)) { throw new CannotGetName; } $this->name = $r[2]; } return $this->name; } /** * Escapes a value for output in a view script. * * @param mixed $var The output to escape. * * @return mixed The escaped value. */ public function escape($var) { return htmlspecialchars($var, ENT_COMPAT, 'UTF-8'); } /** * Method to get data from a registered model or a property of the view * * @param string $property The name of the method to call on the Model or the property to get * @param string $default The default value [optional] * @param string $modelName The name of the Model to reference [optional] * * @return mixed The return value of the method */ public function get($property, $default = null, $modelName = null) { // If $model is null we use the default model if (is_null($modelName)) { $model = $this->defaultModel; } else { $model = $modelName; } // First check to make sure the model requested exists if (isset($this->modelInstances[$model])) { // Model exists, let's build the method name $method = 'get' . ucfirst($property); // Does the method exist? if (method_exists($this->modelInstances[$model], $method)) { // The method exists, let's call it and return what we get $result = $this->modelInstances[$model]->$method(); return $result; } else { $result = $this->modelInstances[$model]->$property(); if (is_null($result)) { return $default; } return $result; } } // If the model doesn't exist, try to fetch a View property else { if (@isset($this->$property)) { return $this->$property; } else { return $default; } } } /** * Returns a named Model object * * @param string $name The Model name. If null we'll use the modelName * variable or, if it's empty, the same name as * the Controller * * @return Model The instance of the Model known to this Controller */ public function getModel($name = null) { if (!empty($name)) { $modelName = $name; } elseif (!empty($this->defaultModel)) { $modelName = $this->defaultModel; } else { $modelName = $this->name; } if (!array_key_exists($modelName, $this->modelInstances)) { throw new ModelNotFound($modelName, $this->name); } return $this->modelInstances[$modelName]; } /** * Pushes the default Model to the View * * @param Model $model The model to push */ public function setDefaultModel(Model &$model) { $name = $model->getName(); $this->setDefaultModelName($name); $this->setModel($this->defaultModel, $model); } /** * Set the name of the Model to be used by this View * * @param string $modelName The name of the Model * * @return void */ public function setDefaultModelName($modelName) { $this->defaultModel = $modelName; } /** * Pushes a named model to the View * * @param string $modelName The name of the Model * @param Model $model The actual Model object to push * * @return void */ public function setModel($modelName, Model &$model) { $this->modelInstances[$modelName] = $model; } /** * Overrides the default method to execute and display a template script. * Instead of loadTemplate is uses loadAnyTemplate. * * @param string $tpl The name of the template file to parse * * @return boolean True on success * * @throws Exception When the layout file is not found */ public function display($tpl = null) { $eventName = 'onBefore' . ucfirst($this->doTask); $this->triggerEvent($eventName, [$tpl]); $preRenderResult = ''; if ($this->doPreRender) { @ob_start(); $this->preRender(); $preRenderResult = @ob_get_contents(); @ob_end_clean(); } $templateResult = $this->loadTemplate($tpl); $eventName = 'onAfter' . ucfirst($this->doTask); $this->triggerEvent($eventName, [$tpl]); if (is_object($templateResult) && ($templateResult instanceof Exception)) { throw $templateResult; } echo $preRenderResult . $templateResult; if ($this->doPostRender) { $this->postRender(); } return true; } /** * Get the layout. * * @return string The layout name */ public function getLayout() { return $this->layout; } /** * Sets the layout name to use * * @param string $layout The layout name or a string in format