forked from Wavyzz/dolibarr
1337 lines
29 KiB
PHP
1337 lines
29 KiB
PHP
<?php
|
|
|
|
/**
|
|
*
|
|
* Provides an object-oriented template system for PHP5.
|
|
*
|
|
* @package Savant3
|
|
*
|
|
* @author Paul M. Jones <pmjones@ciaweb.net>
|
|
*
|
|
* @license http://www.gnu.org/copyleft/lesser.html LGPL
|
|
*
|
|
* @version $Id: Savant3.php,v 1.42 2006/01/01 18:31:00 pmjones Exp $
|
|
*
|
|
*/
|
|
|
|
|
|
/**
|
|
* Always have these classes available.
|
|
*/
|
|
include_once dirname(__FILE__) . '/Savant3/Filter.php';
|
|
include_once dirname(__FILE__) . '/Savant3/Plugin.php';
|
|
|
|
|
|
/**
|
|
*
|
|
* Provides an object-oriented template system for PHP5.
|
|
*
|
|
* Savant3 helps you separate business logic from presentation logic
|
|
* using PHP as the template language. By default, Savant3 does not
|
|
* compile templates. However, you may pass an optional compiler object
|
|
* to compile template source to include-able PHP code. It is E_STRICT
|
|
* compliant for PHP5.
|
|
*
|
|
* Please see the documentation at {@link http://phpsavant.com/}, and be
|
|
* sure to donate! :-)
|
|
*
|
|
* @author Paul M. Jones <pmjones@ciaweb.net>
|
|
*
|
|
* @package Savant3
|
|
*
|
|
* @version @package_version@
|
|
*
|
|
*/
|
|
|
|
class Savant3 {
|
|
|
|
|
|
/**
|
|
*
|
|
* Array of configuration parameters.
|
|
*
|
|
* @access protected
|
|
*
|
|
* @var array
|
|
*
|
|
*/
|
|
|
|
protected $__config = array(
|
|
'template_path' => array(),
|
|
'resource_path' => array(),
|
|
'error_text' => "\n\ntemplate error, examine fetch() result\n\n",
|
|
'exceptions' => false,
|
|
'autoload' => false,
|
|
'compiler' => null,
|
|
'filters' => array(),
|
|
'plugins' => array(),
|
|
'template' => null,
|
|
'plugin_conf' => array(),
|
|
'extract' => false,
|
|
'fetch' => null,
|
|
'escape' => array('htmlspecialchars'),
|
|
);
|
|
|
|
|
|
// -----------------------------------------------------------------
|
|
//
|
|
// Constructor and magic methods
|
|
//
|
|
// -----------------------------------------------------------------
|
|
|
|
|
|
/**
|
|
*
|
|
* Constructor.
|
|
*
|
|
* @access public
|
|
*
|
|
* @param array $config An associative array of configuration keys for
|
|
* the Savant3 object. Any, or none, of the keys may be set.
|
|
*
|
|
* @return object Savant3 A Savant3 instance.
|
|
*
|
|
*/
|
|
|
|
public function __construct($config = null)
|
|
{
|
|
// force the config to an array
|
|
settype($config, 'array');
|
|
|
|
// set the default template search path
|
|
if (isset($config['template_path'])) {
|
|
// user-defined dirs
|
|
$this->setPath('template', $config['template_path']);
|
|
} else {
|
|
// no directories set, use the
|
|
// default directory only
|
|
$this->setPath('template', null);
|
|
}
|
|
|
|
// set the default resource search path
|
|
if (isset($config['resource_path'])) {
|
|
// user-defined dirs
|
|
$this->setPath('resource', $config['resource_path']);
|
|
} else {
|
|
// no directories set, use the
|
|
// default directory only
|
|
$this->setPath('resource', null);
|
|
}
|
|
|
|
// set the error reporting text
|
|
if (isset($config['error_text'])) {
|
|
$this->setErrorText($config['error_text']);
|
|
}
|
|
|
|
// set the autoload flag
|
|
if (isset($config['autoload'])) {
|
|
$this->setAutoload($config['autoload']);
|
|
}
|
|
|
|
// set the extraction flag
|
|
if (isset($config['extract'])) {
|
|
$this->setExtract($config['extract']);
|
|
}
|
|
|
|
// set the exceptions flag
|
|
if (isset($config['exceptions'])) {
|
|
$this->setExceptions($config['exceptions']);
|
|
}
|
|
|
|
// set the template to use for output
|
|
if (isset($config['template'])) {
|
|
$this->setTemplate($config['template']);
|
|
}
|
|
|
|
// set the output escaping callbacks
|
|
if (isset($config['escape'])) {
|
|
$this->setEscape($config['escape']);
|
|
}
|
|
|
|
// set the default plugin configs
|
|
if (isset($config['plugin_conf']) && is_array($config['plugin_conf'])) {
|
|
foreach ($config['plugin_conf'] as $name => $opts) {
|
|
$this->setPluginConf($name, $opts);
|
|
}
|
|
}
|
|
|
|
// set the default filter callbacks
|
|
if (isset($config['filters'])) {
|
|
$this->addFilters($config['filters']);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Executes a main plugin method with arbitrary parameters.
|
|
*
|
|
* @access public
|
|
*
|
|
* @param string $func The plugin method name.
|
|
*
|
|
* @param array $args The parameters passed to the method.
|
|
*
|
|
* @return mixed The plugin output, or a Savant3_Error with an
|
|
* ERR_PLUGIN code if it can't find the plugin.
|
|
*
|
|
*/
|
|
|
|
public function __call($func, $args)
|
|
{
|
|
$plugin = $this->plugin($func);
|
|
|
|
if ($this->isError($plugin)) {
|
|
return $plugin;
|
|
}
|
|
|
|
// try to avoid the very-slow call_user_func_array()
|
|
// for plugins with very few parameters. thanks to
|
|
// Andreas Korthaus for profiling the code to find
|
|
// the slowdown.
|
|
switch (count($args)) {
|
|
|
|
case 0:
|
|
return $plugin->$func();
|
|
|
|
case 1:
|
|
return $plugin->$func($args[0]);
|
|
break;
|
|
|
|
case 2:
|
|
return $plugin->$func($args[0], $args[1]);
|
|
break;
|
|
|
|
case 3:
|
|
return $plugin->$func($args[0], $args[1], $args[2]);
|
|
break;
|
|
|
|
default:
|
|
return call_user_func_array(array($plugin, $func), $args);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Magic method to echo this object as template output.
|
|
*
|
|
* Note that if there is an error, this will output a simple
|
|
* error text string and will not return an error object. Use
|
|
* fetch() to get an error object when errors occur.
|
|
*
|
|
* @access public
|
|
*
|
|
* @return string The template output.
|
|
*
|
|
*/
|
|
|
|
public function __toString()
|
|
{
|
|
return $this->getOutput();
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Reports the API version for this class.
|
|
*
|
|
* @access public
|
|
*
|
|
* @return string A PHP-standard version number.
|
|
*
|
|
*/
|
|
|
|
public function apiVersion()
|
|
{
|
|
return '@package_version@';
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Returns an internal plugin object; creates it as needed.
|
|
*
|
|
* @access public
|
|
*
|
|
* @param string $name The plugin name. If this plugin has not
|
|
* been created yet, this method creates it automatically.
|
|
*
|
|
* @return mixed The plugin object, or a Savant3_Error with an
|
|
* ERR_PLUGIN code if it can't find the plugin.
|
|
*
|
|
*/
|
|
|
|
public function plugin($name)
|
|
{
|
|
// shorthand reference
|
|
$plugins =& $this->__config['plugins'];
|
|
$autoload = $this->__config['autoload'];
|
|
|
|
// is the plugin method object already instantiated?
|
|
if (! array_key_exists($name, $plugins)) {
|
|
|
|
// not already instantiated, so load it up.
|
|
// set up the class name.
|
|
$class = "Savant3_Plugin_$name";
|
|
|
|
// has the class been loaded?
|
|
if (! class_exists($class, $autoload)) {
|
|
|
|
// class is not loaded, set up the file name.
|
|
$file = "$class.php";
|
|
|
|
// make sure the class file is available from the resource path.
|
|
$result = $this->findFile('resource', $file);
|
|
if (! $result) {
|
|
// not available, this is an error
|
|
return $this->error(
|
|
'ERR_PLUGIN',
|
|
array('method' => $name)
|
|
);
|
|
} else {
|
|
// available, load the class file
|
|
include_once $result;
|
|
}
|
|
}
|
|
|
|
// get the default configuration for the plugin.
|
|
$plugin_conf =& $this->__config['plugin_conf'];
|
|
if (! empty($plugin_conf[$name])) {
|
|
$opts = $plugin_conf[$name];
|
|
} else {
|
|
$opts = array();
|
|
}
|
|
|
|
// add the Savant reference
|
|
$opts['Savant'] = $this;
|
|
|
|
// instantiate the plugin with its options.
|
|
$plugins[$name] = new $class($opts);
|
|
}
|
|
|
|
// return the plugin object
|
|
return $plugins[$name];
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------
|
|
//
|
|
// Public configuration management (getters and setters).
|
|
//
|
|
// -----------------------------------------------------------------
|
|
|
|
|
|
/**
|
|
*
|
|
* Returns a copy of the Savant3 configuration parameters.
|
|
*
|
|
* @access public
|
|
*
|
|
* @param string $key The specific configuration key to return. If null,
|
|
* returns the entire configuration array.
|
|
*
|
|
* @return mixed A copy of the $this->__config array.
|
|
*
|
|
*/
|
|
|
|
public function getConfig($key = null)
|
|
{
|
|
if (is_null($key)) {
|
|
// no key requested, return the entire config array
|
|
return $this->__config;
|
|
} elseif (empty($this->__config[$key])) {
|
|
// no such key
|
|
return null;
|
|
} else {
|
|
// return the requested key
|
|
return $this->__config[$key];
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Sets __autoload() usage on or off.
|
|
*
|
|
* @access public
|
|
*
|
|
* @param bool $flag True to use __autoload(), false to not use it.
|
|
*
|
|
* @return void
|
|
*
|
|
*/
|
|
|
|
public function setAutoload($flag)
|
|
{
|
|
$this->__config['autoload'] = (bool) $flag;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Sets a custom compiler/pre-processor callback for template sources.
|
|
*
|
|
* By default, Savant3 does not use a compiler; use this to set your
|
|
* own custom compiler (pre-processor) for template sources.
|
|
*
|
|
* @access public
|
|
*
|
|
* @param mixed $compiler A compiler callback value suitable for the
|
|
* first parameter of call_user_func(). Set to null/false/empty to
|
|
* use PHP itself as the template markup (i.e., no compiling).
|
|
*
|
|
* @return void
|
|
*
|
|
*/
|
|
|
|
public function setCompiler($compiler)
|
|
{
|
|
$this->__config['compiler'] = $compiler;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Sets the custom error text for __toString().
|
|
*
|
|
* @access public
|
|
*
|
|
* @param string $text The error text when a template is echoed.
|
|
*
|
|
* @return void
|
|
*
|
|
*/
|
|
|
|
public function setErrorText($text)
|
|
{
|
|
$this->__config['error_text'] = $text;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Sets whether or not exceptions will be thrown.
|
|
*
|
|
* @access public
|
|
*
|
|
* @param bool $flag True to turn on exception throwing, false
|
|
* to turn it off.
|
|
*
|
|
* @return void
|
|
*
|
|
*/
|
|
|
|
public function setExceptions($flag)
|
|
{
|
|
$this->__config['exceptions'] = (bool) $flag;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Sets whether or not variables will be extracted.
|
|
*
|
|
* @access public
|
|
*
|
|
* @param bool $flag True to turn on variable extraction, false
|
|
* to turn it off.
|
|
*
|
|
* @return void
|
|
*
|
|
*/
|
|
|
|
public function setExtract($flag)
|
|
{
|
|
$this->__config['extract'] = (bool) $flag;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Sets config array for a plugin.
|
|
*
|
|
* @access public
|
|
*
|
|
* @param string $plugin The plugin to configure.
|
|
*
|
|
* @param array $config The configuration array for the plugin.
|
|
*
|
|
* @return void
|
|
*
|
|
*/
|
|
|
|
public function setPluginConf($plugin, $config = null)
|
|
{
|
|
$this->__config['plugin_conf'][$plugin] = $config;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Sets the template name to use.
|
|
*
|
|
* @access public
|
|
*
|
|
* @param string $template The template name.
|
|
*
|
|
* @return void
|
|
*
|
|
*/
|
|
|
|
public function setTemplate($template)
|
|
{
|
|
$this->__config['template'] = $template;
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------
|
|
//
|
|
// Output escaping and management.
|
|
//
|
|
// -----------------------------------------------------------------
|
|
|
|
|
|
/**
|
|
*
|
|
* Clears then sets the callbacks to use when calling $this->escape().
|
|
*
|
|
* Each parameter passed to this function is treated as a separate
|
|
* callback. For example:
|
|
*
|
|
* <code>
|
|
* $savant->setEscape(
|
|
* 'stripslashes',
|
|
* 'htmlspecialchars',
|
|
* array('StaticClass', 'method'),
|
|
* array($object, $method)
|
|
* );
|
|
* </code>
|
|
*
|
|
* @access public
|
|
*
|
|
* @return void
|
|
*
|
|
*/
|
|
|
|
public function setEscape()
|
|
{
|
|
$this->__config['escape'] = (array) @func_get_args();
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Adds to the callbacks used when calling $this->escape().
|
|
*
|
|
* Each parameter passed to this function is treated as a separate
|
|
* callback. For example:
|
|
*
|
|
* <code>
|
|
* $savant->addEscape(
|
|
* 'stripslashes',
|
|
* 'htmlspecialchars',
|
|
* array('StaticClass', 'method'),
|
|
* array($object, $method)
|
|
* );
|
|
* </code>
|
|
*
|
|
* @access public
|
|
*
|
|
* @return void
|
|
*
|
|
*/
|
|
|
|
public function addEscape()
|
|
{
|
|
$args = (array) @func_get_args();
|
|
$this->__config['escape'] = array_merge(
|
|
$this->__config['escape'], $args
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Gets the array of output-escaping callbacks.
|
|
*
|
|
* @access public
|
|
*
|
|
* @return array The array of output-escaping callbacks.
|
|
*
|
|
*/
|
|
|
|
public function getEscape()
|
|
{
|
|
return $this->__config['escape'];
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Applies escaping to a value.
|
|
*
|
|
* You can override the predefined escaping callbacks by passing
|
|
* added parameters as replacement callbacks.
|
|
*
|
|
* <code>
|
|
* // use predefined callbacks
|
|
* $result = $savant->escape($value);
|
|
*
|
|
* // use replacement callbacks
|
|
* $result = $savant->escape(
|
|
* $value,
|
|
* 'stripslashes',
|
|
* 'htmlspecialchars',
|
|
* array('StaticClass', 'method'),
|
|
* array($object, $method)
|
|
* );
|
|
* </code>
|
|
*
|
|
*
|
|
* Unfortunately, a call to "echo htmlspecialchars()" is twice
|
|
* as fast as a call to "echo $this->escape()" under the default
|
|
* escaping (which is htmlspecialchars). The benchmark showed
|
|
* 0.007 seconds for htmlspecialchars(), and 0.014 seconds for
|
|
* $this->escape(), on 300 calls each.
|
|
*
|
|
* @access public
|
|
*
|
|
* @param mixed $value The value to be escaped.
|
|
*
|
|
* @return mixed
|
|
*
|
|
*/
|
|
|
|
public function escape($value)
|
|
{
|
|
// were custom callbacks passed?
|
|
if (func_num_args() == 1) {
|
|
|
|
// no, only a value was passed.
|
|
// loop through the predefined callbacks.
|
|
foreach ($this->__config['escape'] as $func) {
|
|
// this if() shaves 0.001sec off of 300 calls.
|
|
if (is_string($func)) {
|
|
$value = $func($value);
|
|
} else {
|
|
$value = call_user_func($func, $value);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
// yes, use the custom callbacks
|
|
$callbacks = func_get_args();
|
|
|
|
// drop $value
|
|
array_shift($callbacks);
|
|
|
|
// loop through custom callbacks.
|
|
foreach ($callbacks as $func) {
|
|
// this if() shaves 0.001sec off of 300 calls.
|
|
if (is_string($func)) {
|
|
$value = $func($value);
|
|
} else {
|
|
$value = call_user_func($func, $value);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Prints a value after escaping it for output.
|
|
*
|
|
* You can override the predefined escaping callbacks by passing
|
|
* added parameters as replacement callbacks.
|
|
*
|
|
* <code>
|
|
* // use predefined callbacks
|
|
* $this->eprint($value);
|
|
*
|
|
* // use replacement callbacks
|
|
* $this->eprint(
|
|
* $value,
|
|
* 'stripslashes',
|
|
* 'htmlspecialchars',
|
|
* array('StaticClass', 'method'),
|
|
* array($object, $method)
|
|
* );
|
|
* </code>
|
|
*
|
|
* @access public
|
|
*
|
|
* @param mixed $value The value to be escaped and printed.
|
|
*
|
|
* @return void
|
|
*
|
|
*/
|
|
|
|
public function eprint($value)
|
|
{
|
|
// avoid the very slow call_user_func_array() when there
|
|
// are no custom escaping callbacks. thanks to
|
|
// Andreas Korthaus for profiling the code to find
|
|
// the slowdown.
|
|
$num = func_num_args();
|
|
if ($num == 1) {
|
|
echo $this->escape($value);
|
|
} else {
|
|
$args = func_get_args();
|
|
echo call_user_func_array(
|
|
array($this, 'escape'),
|
|
$args
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------
|
|
//
|
|
// File management
|
|
//
|
|
// -----------------------------------------------------------------
|
|
|
|
|
|
/**
|
|
*
|
|
* Sets an entire array of search paths for templates or resources.
|
|
*
|
|
* @access public
|
|
*
|
|
* @param string $type The type of path to set, typically 'template'
|
|
* or 'resource'.
|
|
*
|
|
* @param string|array $path The new set of search paths. If null or
|
|
* false, resets to the current directory only.
|
|
*
|
|
* @return void
|
|
*
|
|
*/
|
|
|
|
public function setPath($type, $path)
|
|
{
|
|
// clear out the prior search dirs
|
|
$this->__config[$type . '_path'] = array();
|
|
|
|
// always add the fallback directories as last resort
|
|
switch (strtolower($type)) {
|
|
case 'template':
|
|
// the current directory
|
|
$this->addPath($type, '.');
|
|
break;
|
|
case 'resource':
|
|
// the Savant3 distribution resources
|
|
$this->addPath($type, dirname(__FILE__) . '/Savant3/resources/');
|
|
break;
|
|
}
|
|
|
|
// actually add the user-specified directories
|
|
$this->addPath($type, $path);
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Adds to the search path for templates and resources.
|
|
*
|
|
* @access public
|
|
*
|
|
* @param string|array $path The directory or stream to search.
|
|
*
|
|
* @return void
|
|
*
|
|
*/
|
|
|
|
public function addPath($type, $path)
|
|
{
|
|
// convert from path string to array of directories
|
|
if (is_string($path) && ! strpos($path, '://')) {
|
|
|
|
// the path config is a string, and it's not a stream
|
|
// identifier (the "://" piece). add it as a path string.
|
|
$path = explode(PATH_SEPARATOR, $path);
|
|
|
|
// typically in path strings, the first one is expected
|
|
// to be searched first. however, Savant3 uses a stack,
|
|
// so the first would be last. reverse the path string
|
|
// so that it behaves as expected with path strings.
|
|
$path = array_reverse($path);
|
|
|
|
} else {
|
|
|
|
// just force to array
|
|
settype($path, 'array');
|
|
|
|
}
|
|
|
|
// loop through the path directories
|
|
foreach ($path as $dir) {
|
|
|
|
// no surrounding spaces allowed!
|
|
$dir = trim($dir);
|
|
|
|
// add trailing separators as needed
|
|
if (strpos($dir, '://') && substr($dir, -1) != '/') {
|
|
// stream
|
|
$dir .= '/';
|
|
} elseif (substr($dir, -1) != DIRECTORY_SEPARATOR) {
|
|
// directory
|
|
$dir .= DIRECTORY_SEPARATOR;
|
|
}
|
|
|
|
// add to the top of the search dirs
|
|
array_unshift(
|
|
$this->__config[$type . '_path'],
|
|
$dir
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Searches the directory paths for a given file.
|
|
*
|
|
* @param array $type The type of path to search (template or resource).
|
|
*
|
|
* @param string $file The file name to look for.
|
|
*
|
|
* @return string|bool The full path and file name for the target file,
|
|
* or boolean false if the file is not found in any of the paths.
|
|
*
|
|
*/
|
|
|
|
protected function findFile($type, $file)
|
|
{
|
|
// get the set of paths
|
|
$set = $this->__config[$type . '_path'];
|
|
|
|
// start looping through the path set
|
|
foreach ($set as $path) {
|
|
|
|
// get the path to the file
|
|
$fullname = $path . $file;
|
|
|
|
// is the path based on a stream?
|
|
if (strpos($path, '://') === false) {
|
|
// not a stream, so do a realpath() to avoid
|
|
// directory traversal attempts on the local file
|
|
// system. Suggested by Ian Eure, initially
|
|
// rejected, but then adopted when the secure
|
|
// compiler was added.
|
|
$path = realpath($path); // needed for substr() later
|
|
$fullname = realpath($fullname);
|
|
}
|
|
|
|
// the substr() check added by Ian Eure to make sure
|
|
// that the realpath() results in a directory registered
|
|
// with Savant so that non-registered directores are not
|
|
// accessible via directory traversal attempts.
|
|
if (file_exists($fullname) && is_readable($fullname) &&
|
|
substr($fullname, 0, strlen($path)) == $path) {
|
|
return $fullname;
|
|
}
|
|
}
|
|
|
|
// could not find the file in the set of paths
|
|
return false;
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------
|
|
//
|
|
// Variable and reference assignment
|
|
//
|
|
// -----------------------------------------------------------------
|
|
|
|
|
|
/**
|
|
*
|
|
* Sets variables for the template (by copy).
|
|
*
|
|
* This method is overloaded; you can assign all the properties of
|
|
* an object, an associative array, or a single value by name.
|
|
*
|
|
* You are not allowed to assign any variable named '__config' as
|
|
* it would conflict with internal configuration tracking.
|
|
*
|
|
* In the following examples, the template will have two variables
|
|
* assigned to it; the variables will be known inside the template as
|
|
* "$this->var1" and "$this->var2".
|
|
*
|
|
* <code>
|
|
* $Savant3 = new Savant3();
|
|
*
|
|
* // assign by object
|
|
* $obj = new stdClass;
|
|
* $obj->var1 = 'something';
|
|
* $obj->var2 = 'else';
|
|
* $Savant3->assign($obj);
|
|
*
|
|
* // assign by associative array
|
|
* $ary = array('var1' => 'something', 'var2' => 'else');
|
|
* $Savant3->assign($ary);
|
|
*
|
|
* // assign by name and value
|
|
* $Savant3->assign('var1', 'something');
|
|
* $Savant3->assign('var2', 'else');
|
|
*
|
|
* // assign directly
|
|
* $Savant3->var1 = 'something';
|
|
* $Savant3->var2 = 'else';
|
|
* </code>
|
|
*
|
|
* @access public
|
|
*
|
|
* @return bool True on success, false on failure.
|
|
*
|
|
*/
|
|
|
|
public function assign()
|
|
{
|
|
// get the arguments; there may be 1 or 2.
|
|
$arg0 = @func_get_arg(0);
|
|
$arg1 = @func_get_arg(1);
|
|
|
|
// assign from object
|
|
if (is_object($arg0)) {
|
|
// assign public properties
|
|
foreach (get_object_vars($arg0) as $key => $val) {
|
|
// can't assign to __config
|
|
if ($key != '__config') {
|
|
$this->$key = $val;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// assign from associative array
|
|
if (is_array($arg0)) {
|
|
foreach ($arg0 as $key => $val) {
|
|
// can't assign to __config
|
|
if ($key != '__config') {
|
|
$this->$key = $val;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// assign by name and value (can't assign to __config).
|
|
if (is_string($arg0) && func_num_args() > 1 && $arg0 != '__config') {
|
|
$this->$arg0 = $arg1;
|
|
return true;
|
|
}
|
|
|
|
// $arg0 was not object, array, or string.
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Sets variables for the template (by reference).
|
|
*
|
|
* You are not allowed to assign any variable named '__config' as
|
|
* it would conflict with internal configuration tracking.
|
|
*
|
|
* <code>
|
|
* $Savant3 = new Savant3();
|
|
*
|
|
* // assign by name and value
|
|
* $Savant3->assignRef('var1', $ref);
|
|
*
|
|
* // assign directly
|
|
* $Savant3->ref =& $var1;
|
|
* </code>
|
|
*
|
|
* @access public
|
|
*
|
|
* @return bool True on success, false on failure.
|
|
*
|
|
*/
|
|
|
|
public function assignRef($key, &$val)
|
|
{
|
|
// assign by name and reference (can't assign to __config).
|
|
if ($key != '__config') {
|
|
$this->$key =& $val;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------
|
|
//
|
|
// Template processing
|
|
//
|
|
// -----------------------------------------------------------------
|
|
|
|
|
|
/**
|
|
*
|
|
* Displays a template directly (equivalent to <code>echo $tpl</code>).
|
|
*
|
|
* @access public
|
|
*
|
|
* @param string $tpl The template source to compile and display.
|
|
*
|
|
* @return void
|
|
*
|
|
*/
|
|
|
|
public function display($tpl = null)
|
|
{
|
|
echo $this->getOutput($tpl);
|
|
}
|
|
|
|
/**
|
|
* Returns output, including error_text if an error occurs.
|
|
*
|
|
* @param $tpl The template to process; if null, uses the
|
|
* default template set with setTemplate().
|
|
*
|
|
* @return string The template output.
|
|
*/
|
|
public function getOutput($tpl = null)
|
|
{
|
|
$output = $this->fetch($tpl);
|
|
if ($this->isError($output)) {
|
|
$text = $this->__config['error_text'];
|
|
return $this->escape($text);
|
|
} else {
|
|
return $output;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Compiles, executes, and filters a template source.
|
|
*
|
|
* @access public
|
|
*
|
|
* @param string $tpl The template to process; if null, uses the
|
|
* default template set with setTemplate().
|
|
*
|
|
* @return mixed The template output string, or a Savant3_Error.
|
|
*
|
|
*/
|
|
|
|
public function fetch($tpl = null)
|
|
{
|
|
// make sure we have a template source to work with
|
|
if (is_null($tpl)) {
|
|
$tpl = $this->__config['template'];
|
|
}
|
|
|
|
// get a path to the compiled template script
|
|
$result = $this->template($tpl);
|
|
|
|
// did we get a path?
|
|
if (! $result || $this->isError($result)) {
|
|
|
|
// no. return the error result.
|
|
return $result;
|
|
|
|
} else {
|
|
|
|
// yes. execute the template script. move the script-path
|
|
// out of the local scope, then clean up the local scope to
|
|
// avoid variable name conflicts.
|
|
$this->__config['fetch'] = $result;
|
|
unset($result);
|
|
unset($tpl);
|
|
|
|
// are we doing extraction?
|
|
if ($this->__config['extract']) {
|
|
// pull variables into the local scope.
|
|
extract(get_object_vars($this), EXTR_REFS);
|
|
}
|
|
|
|
// buffer output so we can return it instead of displaying.
|
|
ob_start();
|
|
|
|
// are we using filters?
|
|
if ($this->__config['filters']) {
|
|
// use a second buffer to apply filters. we used to set
|
|
// the ob_start() filter callback, but that would
|
|
// silence errors in the filters. Hendy Irawan provided
|
|
// the next three lines as a "verbose" fix.
|
|
ob_start();
|
|
include $this->__config['fetch'];
|
|
echo $this->applyFilters(ob_get_clean());
|
|
} else {
|
|
// no filters being used.
|
|
include $this->__config['fetch'];
|
|
}
|
|
|
|
// reset the fetch script value, get the buffer, and return.
|
|
$this->__config['fetch'] = null;
|
|
return ob_get_clean();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Compiles a template and returns path to compiled script.
|
|
*
|
|
* By default, Savant does not compile templates, it uses PHP as the
|
|
* markup language, so the "compiled" template is the same as the source
|
|
* template.
|
|
*
|
|
* Used inside a template script like so:
|
|
*
|
|
* <code>
|
|
* include $this->template($tpl);
|
|
* </code>
|
|
*
|
|
* @access protected
|
|
*
|
|
* @param string $tpl The template source name to look for.
|
|
*
|
|
* @return string The full path to the compiled template script.
|
|
*
|
|
* @throws object An error object with a 'ERR_TEMPLATE' code.
|
|
*
|
|
*/
|
|
|
|
protected function template($tpl = null)
|
|
{
|
|
// set to default template if none specified.
|
|
if (is_null($tpl)) {
|
|
$tpl = $this->__config['template'];
|
|
}
|
|
|
|
// find the template source.
|
|
$file = $this->findFile('template', $tpl);
|
|
if (! $file) {
|
|
return $this->error(
|
|
'ERR_TEMPLATE',
|
|
array('template' => $tpl)
|
|
);
|
|
}
|
|
|
|
// are we compiling source into a script?
|
|
if ($this->__config['compiler']) {
|
|
// compile the template source and get the path to the
|
|
// compiled script (will be returned instead of the
|
|
// source path)
|
|
$result = call_user_func(
|
|
array($this->__config['compiler'], 'compile'),
|
|
$file
|
|
);
|
|
} else {
|
|
// no compiling requested, use the source path
|
|
$result = $file;
|
|
}
|
|
|
|
// is there a script from the compiler?
|
|
if (! $result || $this->isError($result)) {
|
|
// return an error, along with any error info
|
|
// generated by the compiler.
|
|
return $this->error(
|
|
'ERR_COMPILER',
|
|
array(
|
|
'template' => $tpl,
|
|
'compiler' => $result
|
|
)
|
|
);
|
|
} else {
|
|
// no errors, the result is a path to a script
|
|
return $result;
|
|
}
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------
|
|
//
|
|
// Filter management and processing
|
|
//
|
|
// -----------------------------------------------------------------
|
|
|
|
|
|
/**
|
|
*
|
|
* Resets the filter stack to the provided list of callbacks.
|
|
*
|
|
* @access protected
|
|
*
|
|
* @param array An array of filter callbacks.
|
|
*
|
|
* @return void
|
|
*
|
|
*/
|
|
|
|
public function setFilters()
|
|
{
|
|
$this->__config['filters'] = (array) @func_get_args();
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Adds filter callbacks to the stack of filters.
|
|
*
|
|
* @access protected
|
|
*
|
|
* @param array An array of filter callbacks.
|
|
*
|
|
* @return void
|
|
*
|
|
*/
|
|
|
|
public function addFilters()
|
|
{
|
|
// add the new filters to the static config variable
|
|
// via the reference
|
|
foreach ((array) @func_get_args() as $callback) {
|
|
$this->__config['filters'][] = $callback;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Runs all filter callbacks on buffered output.
|
|
*
|
|
* @access protected
|
|
*
|
|
* @param string The template output.
|
|
*
|
|
* @return void
|
|
*
|
|
*/
|
|
|
|
protected function applyFilters($buffer)
|
|
{
|
|
$autoload = $this->__config['autoload'];
|
|
foreach ($this->__config['filters'] as $callback) {
|
|
|
|
// if the callback is a static Savant3_Filter method,
|
|
// and not already loaded, try to auto-load it.
|
|
if (is_array($callback) &&
|
|
is_string($callback[0]) &&
|
|
substr($callback[0], 0, 15) == 'Savant3_Filter_' &&
|
|
! class_exists($callback[0], $autoload)) {
|
|
|
|
// load the Savant3_Filter_*.php resource
|
|
$file = $callback[0] . '.php';
|
|
$result = $this->findFile('resource', $file);
|
|
if ($result) {
|
|
include_once $result;
|
|
}
|
|
}
|
|
|
|
// can't pass a third $this param, it chokes the OB system.
|
|
$buffer = call_user_func($callback, $buffer);
|
|
}
|
|
|
|
return $buffer;
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------
|
|
//
|
|
// Error handling
|
|
//
|
|
// -----------------------------------------------------------------
|
|
|
|
|
|
/**
|
|
*
|
|
* Returns an error object or throws an exception.
|
|
*
|
|
* @access public
|
|
*
|
|
* @param string $code A Savant3 'ERR_*' string.
|
|
*
|
|
* @param array $info An array of error-specific information.
|
|
*
|
|
* @param int $level The error severity level, default is
|
|
* E_USER_ERROR (the most severe possible).
|
|
*
|
|
* @param bool $trace Whether or not to include a backtrace, default
|
|
* true.
|
|
*
|
|
* @return object Savant3_Error
|
|
*
|
|
*/
|
|
|
|
public function error($code, $info = array(), $level = E_USER_ERROR,
|
|
$trace = true)
|
|
{
|
|
$autoload = $this->__config['autoload'];
|
|
|
|
// are we throwing exceptions?
|
|
if ($this->__config['exceptions']) {
|
|
if (! class_exists('Savant3_Exception', $autoload)) {
|
|
include_once dirname(__FILE__) . '/Savant3/Exception.php';
|
|
}
|
|
throw new Savant3_Exception($code);
|
|
}
|
|
|
|
|
|
// the error config array
|
|
$config = array(
|
|
'code' => $code,
|
|
'info' => (array) $info,
|
|
'level' => $level,
|
|
'trace' => $trace
|
|
);
|
|
|
|
// make sure the Savant3 error class is available
|
|
if (! class_exists('Savant3_Error', $autoload)) {
|
|
include_once dirname(__FILE__) . '/Savant3/Error.php';
|
|
}
|
|
|
|
// return it
|
|
$err = new Savant3_Error($config);
|
|
return $err;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Tests if an object is of the Savant3_Error class.
|
|
*
|
|
* @access public
|
|
*
|
|
* @param object $obj The object to be tested.
|
|
*
|
|
* @return boolean True if $obj is an error object of the type
|
|
* Savant3_Error, or is a subclass that Savant3_Error. False if not.
|
|
*
|
|
*/
|
|
|
|
public function isError($obj)
|
|
{
|
|
$autoload = $this->__config['autoload'];
|
|
|
|
// is it even an object?
|
|
if (! is_object($obj)) {
|
|
// not an object, so can't be a Savant3_Error
|
|
return false;
|
|
} else {
|
|
// make sure the Savant3 error class is available for
|
|
// comparison
|
|
if (! class_exists('Savant3_Error', $autoload)) {
|
|
include_once dirname(__FILE__) . '/Savant3/Error.php';
|
|
}
|
|
// now compare the parentage
|
|
$is = $obj instanceof Savant3_Error;
|
|
$sub = is_subclass_of($obj, 'Savant3_Error');
|
|
return ($is || $sub);
|
|
}
|
|
}
|
|
}
|
|
?>
|