namespace Ice\Mvc;
use Ice\Arr;
use Ice\Exception;
use Ice\Mvc\View\Engine\Php;
use Ice\Mvc\View\ViewInterface;
/**
* View is a class for working with the "view" portion of the model-view-controller pattern.
*
* @package Ice/View
* @category Component
* @author Ice Team
* @copyright (c) 2014-2025 Ice Team
* @license http://iceframework.org/license
*/
class View extends Arr implements ViewInterface
{
protected engines { set };
protected content { set, get };
protected mainView = "index" { set, get };
protected layoutsDir = "layouts/" { set, get };
protected partialsDir = "partials/" { set, get };
protected viewsDir { set, get };
protected file { set, get };
protected silent = false { set };
/**
* View constructor. Set the file and vars.
*
* @param string file
* @param array data
*/
public function __construct(file = null, array data = [])
{
if file != null {
let this->file = file;
}
parent::__construct(data);
}
/**
* Get registered engines.
*/
public function getEngines()
{
var ext, engine;
if !this->engines {
let this->engines[".phtml"] = new Php(this);
} else {
for ext, engine in this->engines {
if typeof engine == "object" {
if engine instanceof \Closure {
let this->engines[ext] = call_user_func_array(engine, [this]);
}
} else {
if typeof engine == "string" {
let this->engines[ext] = create_instance_params(engine, [this]);
} else {
throw new Exception(sprintf("Invalid template engine registration for '%s' extension", ext));
}
}
}
}
return this->engines;
}
/**
* Try to render the view with vars for engines.
*
* @param string file
* @param array data
* @return string
*/
public function render(file = null, array data = [])
{
var ext, engine, engines, path, dir, dirs, exists, content;
let exists = false,
content = null;
if file !== null {
let this->file = file;
}
if empty this->file {
throw new Exception("You must set the file to use within your view before rendering");
}
let engines = this->getEngines();
if typeof this->viewsDir == "array" {
let dirs = this->viewsDir;
} else {
let dirs = [this->viewsDir];
}
let ext = pathinfo(this->file, PATHINFO_EXTENSION);
if !empty ext {
if fetch engine, engines["." . ext] {
for dir in dirs {
let path = dir . this->file;
if file_exists(path) {
let exists = true;
this->merge(data);
let content = engine->render(path, this->all());
break;
}
}
}
} else {
for ext, engine in engines {
for dir in dirs {
let path = dir . this->file . ext;
if file_exists(path) {
let exists = true;
this->merge(data);
let content = engine->render(path, this->all());
break;
}
}
// no need to lookup and parse the other view
if exists {
break;
}
}
}
if !this->silent && !exists {
throw new Exception(sprintf("The requested view %s could not be found", path));
}
return content;
}
/**
* Load the view.
*
* @param string file Name of file without extension from the views dir
* @param array data Vars to send
* @return string
*/
public function load(string! file, array data = [])
{
return this->render(file, data);
}
/**
* Load the partial view.
*
* @param string file Name of file without extension from the partials dir
* @param array data Vars to send
* @return string
*/
public function partial(string! file, array data = [])
{
return this->render(this->partialsDir . file, data);
}
/**
* Load the layout view.
*
* @param string file Name of file without extension from the layouts dir
* @param array data Vars to send
* @return string
*/
public function layout(var file = null, array data = [])
{
if !file {
let file = this->mainView;
}
return this->render(this->layoutsDir . file, data);
}
/**
* Set var to the view.
*
* @param string name
* @param mixed value
* @return object View
*/
public function setVar(string! name, value)
{
this->set(name, value);
return this;
}
/**
* Set multiple vars to the view.
*
* @param array vars
* @return object View
*/
public function setVars(array! vars)
{
this->merge(vars);
return this;
}
/**
* Alias of the `setMainView` method.
*
* @param array vars
* @return object View
*/
public function setLayout(string layout)
{
this->setMainView(layout);
return this;
}
/**
* Magic toStrint, get the rendered view.
*/
public function __toString()
{
return this->render();
}
}