389 lines
10 KiB
PHP
389 lines
10 KiB
PHP
<?php
|
|
/**
|
|
* @category Horde
|
|
* @package View
|
|
*/
|
|
|
|
/**
|
|
* Abstract base class for Horde_View to get private constructs out of
|
|
* template scope.
|
|
*
|
|
* @category Horde
|
|
* @package View
|
|
*/
|
|
abstract class Horde_View_Base
|
|
{
|
|
/**
|
|
* @var string
|
|
*/
|
|
public static $defaultFormBuilder = 'Horde_View_Helper_Form_Builder';
|
|
|
|
/**
|
|
* Path stack for templates.
|
|
*
|
|
* @var array
|
|
*/
|
|
private $_templatePath = array('./');
|
|
|
|
/**
|
|
* Template to execute.
|
|
*
|
|
* Stored in a private variable to keep it out of the public view scope.
|
|
*
|
|
* @var string
|
|
*/
|
|
private $_file = null;
|
|
|
|
/**
|
|
* Cache of helper objects.
|
|
*
|
|
* @var array
|
|
*/
|
|
private $_helpers = array();
|
|
|
|
/**
|
|
* Encoding to use in escaping mechanisms.
|
|
*
|
|
* @var string
|
|
*/
|
|
private $_encoding = 'UTF-8';
|
|
|
|
/**
|
|
* Should we throw an error if helper methods collide?
|
|
*
|
|
* @var boolean
|
|
*/
|
|
private $_throwOnHelperCollision = false;
|
|
|
|
/**
|
|
* Protected properties.
|
|
*
|
|
* @var array
|
|
*/
|
|
private $_protectedProperties;
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param array $config Configuration key-value pairs.
|
|
*/
|
|
public function __construct($config = array())
|
|
{
|
|
// Encoding.
|
|
if (!empty($config['encoding'])) {
|
|
$this->setEncoding($config['encoding']);
|
|
}
|
|
|
|
// User-defined template path.
|
|
if (!empty($config['templatePath'])) {
|
|
$this->addTemplatePath($config['templatePath']);
|
|
}
|
|
|
|
$this->_protectedProperties = get_class_vars(__CLASS__);
|
|
}
|
|
|
|
/**
|
|
* Undefined variables return null.
|
|
*
|
|
* @return null
|
|
*/
|
|
public function __get($name)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Accesses a helper object from within a template.
|
|
*
|
|
* @param string $method The helper method.
|
|
* @param array $args The parameters for the helper.
|
|
*
|
|
* @return string The result of the helper method.
|
|
* @throws Horde_View_Exception
|
|
*/
|
|
public function __call($method, $args)
|
|
{
|
|
if (isset($this->_helpers[$method])) {
|
|
return call_user_func_array(array($this->_helpers[$method], $method), $args);
|
|
}
|
|
|
|
throw new Horde_View_Exception('Helper for ' . $method . ' not found.');
|
|
}
|
|
|
|
/**
|
|
* Adds to the stack of template paths in LIFO order.
|
|
*
|
|
* @param string|array The directory (-ies) to add.
|
|
*/
|
|
public function addTemplatePath($path)
|
|
{
|
|
foreach ((array)$path as $dir) {
|
|
// Attempt to strip any possible separator and append a
|
|
// directory separator.
|
|
$dir = rtrim($dir, '\\/' . DIRECTORY_SEPARATOR) . '/';
|
|
|
|
// Add to the top of the stack.
|
|
array_unshift($this->_templatePath, $dir);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resets the stack of template paths.
|
|
*
|
|
* To clear all paths, use Horde_View::setTemplatePath(null).
|
|
*
|
|
* @param string|array The directory (-ies) to set as the path.
|
|
*/
|
|
public function setTemplatePath($path)
|
|
{
|
|
$this->_templatePath = array();
|
|
$this->addTemplatePath($path);
|
|
}
|
|
|
|
/**
|
|
* Returns the template paths.
|
|
*
|
|
* @return array The stack of current template paths.
|
|
*/
|
|
public function getTemplatePaths()
|
|
{
|
|
return $this->_templatePath;
|
|
}
|
|
|
|
/**
|
|
* Adds to the stack of helpers in LIFO order.
|
|
*
|
|
* If the $helper parameter is a string instead of a Helper instance, then
|
|
* it will be treated as a class name. Names without "_" and that do not
|
|
* have "Helper" in them will be prefixed with Horde_View_Helper_; other
|
|
* names will be treated as literal class names. Examples:
|
|
*
|
|
* <code>
|
|
* // Adds a new Horde_View_Helper_Tag to the view:
|
|
* $v->addHelper('Tag');
|
|
* // Adds a new AppHelper object to the view if it exists, otherwise
|
|
* // throws an exception:
|
|
* $v->addHelper('AppHelper');
|
|
* </code>
|
|
*
|
|
* @param Horde_View_Helper|string $helper The helper instance to add.
|
|
*
|
|
* @return Horde_View_Helper Returns the helper object that was added.
|
|
* @throws Horde_View_Exception
|
|
*/
|
|
public function addHelper($helper)
|
|
{
|
|
if (is_string($helper)) {
|
|
if (strpos($helper, '_') === false &&
|
|
strpos($helper, 'Helper') === false) {
|
|
$class = 'Horde_View_Helper_' . $helper;
|
|
} else {
|
|
$class = $helper;
|
|
}
|
|
if (!class_exists($class)) {
|
|
throw new Horde_View_Exception('Helper class ' . $helper . ' not found');
|
|
}
|
|
$helper = new $class($this);
|
|
}
|
|
|
|
foreach (get_class_methods($helper) as $method) {
|
|
if (isset($this->_helpers[$method])) {
|
|
$msg = 'Helper method ' . get_class($this->_helpers[$method])
|
|
. '#' . $method . ' overridden by ' . get_class($helper)
|
|
. '#' . $method;
|
|
if ($this->_throwOnHelperCollision) {
|
|
throw new Horde_View_Exception($msg);
|
|
}
|
|
if ($this->logger) {
|
|
$this->logger->warn($msg);
|
|
}
|
|
}
|
|
$this->_helpers[$method] = $helper;
|
|
}
|
|
|
|
return $helper;
|
|
}
|
|
|
|
/**
|
|
* Assigns multiple variables to the view.
|
|
*
|
|
* The array keys are used as names, each assigned their corresponding
|
|
* array value.
|
|
*
|
|
* @param array $array The array of key/value pairs to assign.
|
|
*/
|
|
public function assign($array)
|
|
{
|
|
foreach ($array as $key => $val) {
|
|
if (isset($this->_protectedProperties[$key])) {
|
|
throw new Horde_View_Exception('Cannot overwrite internal variables in assign()');
|
|
}
|
|
$this->$key = $val;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Processes a template and returns the output.
|
|
*
|
|
* @param string $name The template to process.
|
|
*
|
|
* @return string The template output.
|
|
*/
|
|
public function render($name, $locals = array())
|
|
{
|
|
// Render partial.
|
|
if (is_array($name) && $partial = $name['partial']) {
|
|
unset($name['partial']);
|
|
return $this->renderPartial($partial, $name);
|
|
}
|
|
|
|
// Find the template file name.
|
|
$this->_file = $this->_template($name);
|
|
|
|
// Remove $name from local scope.
|
|
unset($name);
|
|
|
|
ob_start();
|
|
$this->_run($this->_file, $locals);
|
|
return ob_get_clean();
|
|
}
|
|
|
|
/**
|
|
* Renders a partial template.
|
|
*
|
|
* Partial template filenames are named with a leading underscore, although
|
|
* this underscore is not used when specifying the name of the partial.
|
|
*
|
|
* We would reference the file /views/shared/_sidebarInfo.html in our
|
|
* template using:
|
|
*
|
|
* <code>
|
|
* <div>
|
|
* <?php echo $this->renderPartial('sidebarInfo') ?>
|
|
* </div>
|
|
* </code>
|
|
*
|
|
* @param string $name
|
|
* @param array $options
|
|
*
|
|
* @return string The template output.
|
|
*/
|
|
public function renderPartial($name, $options = array())
|
|
{
|
|
// Pop name off of the path.
|
|
$parts = strstr($name, '/') ? explode('/', $name) : array($name);
|
|
$name = array_pop($parts);
|
|
$path = count($parts)
|
|
? implode('/', $parts) . '/'
|
|
: '';
|
|
|
|
// Check if they passed in a collection before validating keys.
|
|
$useCollection = array_key_exists('collection', $options);
|
|
|
|
$valid = array('object' => null,
|
|
'locals' => array(),
|
|
'collection' => array());
|
|
$options = array_merge($valid, $options);
|
|
$locals = array($name => null);
|
|
|
|
// Set the object variable.
|
|
if ($options['object']) {
|
|
$locals[$name] = $options['object'];
|
|
}
|
|
|
|
// Set local variables to be used in the partial.
|
|
if (isset($options['locals']) &&
|
|
(is_array($options['locals']) ||
|
|
$options['locals'] instanceof Traversable)) {
|
|
foreach ($options['locals'] as $key => $val) {
|
|
$locals[$key] = $val;
|
|
}
|
|
}
|
|
|
|
if ($useCollection) {
|
|
// Collection.
|
|
$rendered = '';
|
|
if (is_array($options['collection'])) {
|
|
foreach (array_values($options['collection']) as $i => $item) {
|
|
$locals["{$name}Counter"] = $i;
|
|
$locals[$name] = $item;
|
|
$rendered .= $this->render("{$path}_{$name}", $locals);
|
|
}
|
|
}
|
|
} else {
|
|
// Single render.
|
|
$rendered = $this->render("{$path}_{$name}", $locals);
|
|
}
|
|
|
|
return $rendered;
|
|
}
|
|
|
|
/**
|
|
* Sets the output encoding.
|
|
*
|
|
* @param string $encoding A character set name.
|
|
*/
|
|
public function setEncoding($encoding)
|
|
{
|
|
$this->_encoding = $encoding;
|
|
}
|
|
|
|
/**
|
|
* Returns the current output encoding.
|
|
*
|
|
* @return string The current character set.
|
|
*/
|
|
public function getEncoding()
|
|
{
|
|
return $this->_encoding;
|
|
}
|
|
|
|
/**
|
|
* Controls the behavior when a helper method is overridden by another
|
|
* helper.
|
|
*
|
|
* @param boolean $throw Throw an exception when helper methods collide?
|
|
*/
|
|
public function throwOnHelperCollision($throw = true)
|
|
{
|
|
$this->_throwOnHelperCollision = $throw;
|
|
}
|
|
|
|
/**
|
|
* Finds a template from the available directories.
|
|
*
|
|
* @param $name string The base name of the template.
|
|
*
|
|
* @return string The full path to the first matching template.
|
|
*/
|
|
protected function _template($name)
|
|
{
|
|
// Append missing .html.
|
|
if (!strstr($name, '.')) {
|
|
$name .= '.html.php';
|
|
}
|
|
|
|
if (!count($this->_templatePath)) {
|
|
throw new Horde_View_Exception('No template directory set; unable to locate ' . $name);
|
|
}
|
|
|
|
foreach ($this->_templatePath as $dir) {
|
|
if (is_readable($dir . $name)) {
|
|
return $dir . $name;
|
|
}
|
|
}
|
|
|
|
throw new Horde_View_Exception("\"$name\" not found in template path (\"" . implode(':', $this->_templatePath) . '")');
|
|
}
|
|
|
|
/**
|
|
* Includes the template in a scope with only public variables.
|
|
*
|
|
* @param string The template to execute. Not declared in the function
|
|
* signature so it stays out of the view's public scope.
|
|
* @param array Any local variables to declare.
|
|
*/
|
|
abstract protected function _run();
|
|
|
|
}
|