Source for file Dwoo.php
Documentation is available at Dwoo.php
define('DWOO_DIRECTORY', dirname(__FILE__
) .
DIRECTORY_SEPARATOR);
* main dwoo class, allows communication between the compiler, template and data classes
* php 5.2.0 or above (might work below, it's a rough estimate)
* SPL and PCRE extensions (for php versions prior to 5.3.0)
* mbstring extension for some string manipulation plugins (especially if you intend to use UTF-8)
* hash extension (for Dwoo_Template_String - minor performance boost)
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from the use of this software.
* @author Jordi Boggiano <j.boggiano@seld.be>
* @copyright Copyright (c) 2008, Jordi Boggiano
* @license http://dwoo.org/LICENSE Modified BSD License
* unique number of this dwoo release
* this can be used by templates classes to check whether the compiled template
* has been compiled before this release or not, so that old templates are
* recompiled automatically when Dwoo is updated
* constants that represents all plugin types
* these are bitwise-operation-safe values to allow multiple types
const COMPILABLE_PLUGIN =
16;
const CUSTOM_PLUGIN =
32;
const SMARTY_MODIFIER =
64;
const SMARTY_BLOCK =
128;
const SMARTY_FUNCTION =
256;
const PROXY_PLUGIN =
512;
const TEMPLATE_PLUGIN =
1024;
* character set of the template, used by string manipulation plugins
* it must be lowercase, but setCharset() will take care of that
* global variables that are accessible through $dwoo.* in the templates
* default values include:
* $dwoo.version - current version number
* $dwoo.ad - a Powered by Dwoo link pointing to dwoo.org
* $dwoo.now - the current time
* $dwoo.template - the current template filename
* $dwoo.charset - the character set used by the template
* on top of that, foreach and other plugins can store special values in there,
* see their documentation for more details.
* directory where the compiled templates are stored
* defaults to DWOO_COMPILEDIR (= dwoo_dir/compiled by default)
* directory where the cached templates are stored
* defaults to DWOO_CACHEDIR (= dwoo_dir/cache by default)
* defines how long (in seconds) the cached files must remain valid
* can be overriden on a per-template basis
* >0 = duration in seconds
* @var Dwoo_Security_Policy
* stores the custom plugins callbacks
* stores the filter callbacks
* stores the resource types and associated
* classes / compiler classes
'class' =>
'Dwoo_Template_File',
'class' =>
'Dwoo_Template_String',
* the dwoo loader object used to load plugins by this dwoo instance
* currently rendered template, set to null when not-rendering
* stores the instances of the class plugins during template runtime
* stores the data during template runtime
* stores the current scope during template runtime
* this should ideally not be accessed directly from outside template code
* stores the scope tree during template runtime
* stores the block plugins stack during template runtime
* stores the current block plugin at the top of the stack during template runtime
* stores the output buffer during template runtime
* constructor, sets the cache and compile dir to the default values if not provided
* @param string $compileDir path to the compiled directory, defaults to lib/compiled
* @param string $cacheDir path to the cache directory, defaults to lib/cache
public function __construct($compileDir =
null, $cacheDir =
null)
if ($compileDir !==
null) {
if ($cacheDir !==
null) {
* resets some runtime variables to allow a cloned object to be used to render sub-templates
* outputs the template instead of returning it, this is basically a shortcut for get(*, *, *, true)
* @param mixed $tpl template, can either be a Dwoo_ITemplate object (i.e. Dwoo_Template_File), a valid path to a template, or
* a template as a string it is recommended to provide a Dwoo_ITemplate as it will probably make things faster,
* especially if you render a template multiple times
* @param mixed $data the data to use, can either be a Dwoo_IDataProvider object (i.e. Dwoo_Data) or an associative array. if you're
* rendering the template from cache, it can be left null
* @param Dwoo_ICompiler $compiler the compiler that must be used to compile the template, if left empty a default
* Dwoo_Compiler will be used.
* @return string nothing or the template output if $output is true
public function output($tpl, $data =
array(), Dwoo_ICompiler $compiler =
null)
return $this->get($tpl, $data, $compiler, true);
* returns the given template rendered using the provided data and optional compiler
* @param mixed $tpl template, can either be a Dwoo_ITemplate object (i.e. Dwoo_Template_File), a valid path to a template, or
* a template as a string it is recommended to provide a Dwoo_ITemplate as it will probably make things faster,
* especially if you render a template multiple times
* @param mixed $data the data to use, can either be a Dwoo_IDataProvider object (i.e. Dwoo_Data) or an associative array. if you're
* rendering the template from cache, it can be left null
* @param Dwoo_ICompiler $compiler the compiler that must be used to compile the template, if left empty a default
* Dwoo_Compiler will be used.
* @param bool $output flag that defines whether the function returns the output of the template (false, default) or echoes it directly (true)
* @return string nothing or the template output if $output is true
public function get($_tpl, $data =
array(), $_compiler =
null, $_output =
false)
// a render call came from within a template, so we need a new dwoo instance in order to avoid breaking this one
return $proxy->get($_tpl, $data, $_compiler, $_output);
// auto-create template if required
throw
new Dwoo_Exception('Dwoo->get/Dwoo->output\'s first argument must be a Dwoo_ITemplate (i.e. Dwoo_Template_File) or a valid path to a template file', E_USER_NOTICE);
// save the current template, enters render mode at the same time
// if another rendering is requested it will be proxied to a new Dwoo instance
$this->data =
$data->getData();
throw
new Dwoo_Exception('Dwoo->get/Dwoo->output\'s data argument must be a Dwoo_IDataProvider object (i.e. Dwoo_Data) or an associative array', E_USER_NOTICE);
$this->globals['template'] =
$_tpl->getName();
// try to get cached template
$file =
$_tpl->getCachedTemplate($this);
$doCache =
$file ===
true;
if ($cacheLoaded ===
true) {
// cache is present, run it
$out =
include $_tpl->getCompiledTemplate($this, $_compiler);
// template returned false so it needs to be recompiled
$_tpl->forceCompilation();
$out =
include $_tpl->getCompiledTemplate($this, $_compiler);
$out =
preg_replace('/(<%|%>|<\?php|<\?|\?>)/', '<?php /*'.
$dynamicId.
'*/ echo \'$1\'; ?>', $out);
foreach ($this->filters as $filter) {
$file =
$_tpl->cache($this, $out);
// run it from the cache to be sure dynamics are rendered
// no need to build cache
* re-initializes the globals array before each template run
* this method is only callede once when the Dwoo object is created
'version' =>
self::VERSION,
'ad' =>
'<a href="http://dwoo.org/">Powered by Dwoo</a>',
'now' =>
$_SERVER['REQUEST_TIME'],
* re-initializes the runtime variables before each template run
* override this method to inject data in the globals array if needed, this
* method is called before each template execution
* @param Dwoo_ITemplate $tpl the template that is going to be rendered
* --------- settings functions ---------
* adds a custom plugin that is not in one of the plugin directories
* @param string $name the plugin name to be used in the templates
* @param callback $callback the plugin callback, either a function name,
* a class name or an array containing an object
* or class name and a method name
* @param bool $compilable if set to true, the plugin is assumed to be compilable
public function addPlugin($name, $callback, $compilable =
false)
$compilable =
$compilable ?
self::COMPILABLE_PLUGIN :
0;
$this->plugins[$name] =
array('type'=>
self::BLOCK_PLUGIN |
$compilable, 'callback'=>
$callback, 'class'=>
(is_object($callback[0]) ?
get_class($callback[0]) :
$callback[0]));
$this->plugins[$name] =
array('type'=>
self::CLASS_PLUGIN |
$compilable, 'callback'=>
$callback, 'class'=>
(is_object($callback[0]) ?
get_class($callback[0]) :
$callback[0]), 'function'=>
$callback[1]);
$this->plugins[$name] =
array('type'=>
self::BLOCK_PLUGIN |
$compilable, 'callback'=>
$callback, 'class'=>
$callback);
$this->plugins[$name] =
array('type'=>
self::CLASS_PLUGIN |
$compilable, 'callback'=>
$callback, 'class'=>
$callback, 'function'=>
'process');
$this->plugins[$name] =
array('type'=>
self::FUNC_PLUGIN |
$compilable, 'callback'=>
$callback);
throw
new Dwoo_Exception('Callback could not be processed correctly, please check that the function/class you used exists');
* removes a custom plugin
* @param string $name the plugin name
if (isset
($this->plugins[$name])) {
* adds a filter to this Dwoo instance, it will be used to filter the output of all the templates rendered by this instance
* @param mixed $callback a callback or a filter name if it is autoloaded from a plugin directory
* @param bool $autoload if true, the first parameter must be a filter name from one of the plugin directories
public function addFilter($callback, $autoload =
false)
$class =
'Dwoo_Filter_'.
$callback;
if (strstr($callback, 'Dwoo_Filter_')) {
throw
new Dwoo_Exception('Wrong filter name : '.
$callback.
', the "Dwoo_Filter_" prefix should not be used, please only use "'.
str_replace('Dwoo_Filter_', '', $callback).
'"');
throw
new Dwoo_Exception('Wrong filter name : '.
$callback.
', when using autoload the filter must be in one of your plugin dir as "name.php" containg a class or function named "Dwoo_Filter_name"');
$callback =
array(new $class($this), 'process');
throw
new Dwoo_Exception('Wrong filter name : '.
$callback.
', when using autoload the filter must be in one of your plugin dir as "name.php" containg a class or function named "Dwoo_Filter_name"');
* @param mixed $callback callback or filter name if it was autoloaded
$class =
'Dwoo_Filter_' .
$callback;
foreach ($this->filters as $index=>
$filter) {
if (is_array($filter) &&
$filter[0] instanceof
$class) {
* adds a resource or overrides a default one
* @param string $name the resource name
* @param string $class the resource class (which must implement Dwoo_ITemplate)
* @param callback $compilerFactory the compiler factory callback, a function that must return a compiler instance used to compile this resource, if none is provided. by default it will produce a Dwoo_Compiler object
public function addResource($name, $class, $compilerFactory =
null)
throw
new Dwoo_Exception('Resource names must be at least two-character long to avoid conflicts with Windows paths');
if (in_array('Dwoo_ITemplate', $interfaces) ===
false) {
throw
new Dwoo_Exception('Resource class must implement Dwoo_ITemplate');
$this->resources[$name] =
array('class'=>
$class, 'compiler'=>
$compilerFactory);
* removes a custom resource
* @param string $name the resource name
$this->resources['file'] =
array('class'=>
'Dwoo_Template_File', 'compiler'=>
null);
* --------- getters and setters ---------
* sets the loader object to use to load plugins
* @param Dwoo_ILoader $loader loader object
public function setLoader(Dwoo_ILoader $loader)
* returns the current loader object or a default one if none is currently found
* returns the custom plugins loaded
* used by the Dwoo_ITemplate classes to pass the custom plugins to their Dwoo_ICompiler instance
* returns the cache directory with a trailing DIRECTORY_SEPARATOR
* sets the cache directory and automatically appends a DIRECTORY_SEPARATOR
* @param string $dir the cache directory
throw
new Dwoo_Exception('The cache directory must be writable, chmod "'.
$this->cacheDir.
'" to make it writable');
* returns the compile directory with a trailing DIRECTORY_SEPARATOR
* sets the compile directory and automatically appends a DIRECTORY_SEPARATOR
* @param string $dir the compile directory
* returns the default cache time that is used with templates that do not have a cache time set
* @return int the duration in seconds
* sets the default cache time to use with templates that do not have a cache time set
* @param int $seconds the duration in seconds
* returns the character set used by the string manipulation plugins
* the charset is automatically lowercased
* sets the character set used by the string manipulation plugins
* the charset will be automatically lowercased
* @param string $charset the character set
* returns the current template being rendered, when applicable, or null
* @return Dwoo_ITemplate|null
* sets the default compiler factory function for the given resource name
* a compiler factory must return a Dwoo_ICompiler object pre-configured to fit your needs
* @param string $resourceName the resource name (i.e. file, string)
* @param callback $compilerFactory the compiler factory callback
$this->resources[$resourceName]['compiler'] =
$compilerFactory;
* returns the default compiler factory function for the given resource name
* @param string $resourceName the resource name
* @return callback the compiler factory callback
return $this->resources[$resourceName]['compiler'];
* sets the security policy object to enforce some php security settings
* use this if untrusted persons can modify templates
* @param Dwoo_Security_Policy $policy the security policy object
* returns the current security policy object or null by default
* @return Dwoo_Security_Policy|nullthe security policy object if any
* sets the object that must be used as a plugin proxy when plugin can't be found
* @param Dwoo_IPluginProxy $pluginProxy the proxy object
* returns the current plugin proxy object or null by default
* @param Dwoo_IPluginProxy|nullthe proxy object if any
* --------- util functions ---------
* [util function] checks whether the given template is cached or not
* @param Dwoo_ITemplate $tpl the template object
public function isCached(Dwoo_ITemplate $tpl)
return is_string($tpl->getCachedTemplate($this));
* [util function] clears the cached templates if they are older than the given time
* @param int $olderThan minimum time (in seconds) required for a cached template to be cleared
* @return int the amount of templates cleared
$cacheDirs =
new RecursiveDirectoryIterator($this->getCacheDir());
$cache =
new RecursiveIteratorIterator($cacheDirs);
$expired =
time() -
$olderThan;
foreach ($cache as $file) {
if ($cache->isDot() ||
$cache->isDir() ||
substr($file, -
5) !==
'.html') {
if ($cache->getCTime() <
$expired) {
$count +=
unlink((string)
$file) ?
1 :
0;
* [util function] fetches a template object of the given resource
* @param string $resourceName the resource name (i.e. file, string)
* @param string $resourceId the resource identifier (i.e. file path)
* @param int $cacheTime the cache time setting for this resource
* @param string $cacheId the unique cache identifier
* @param string $compileId the unique compiler identifier
public function templateFactory($resourceName, $resourceId, $cacheTime =
null, $cacheId =
null, $compileId =
null, Dwoo_ITemplate $parentTemplate =
null)
if (isset
($this->resources[$resourceName])) {
// TODO could be changed to $this->resources[$resourceName]['class']::templateFactory(..) in 5.3 maybe
return call_user_func(array($this->resources[$resourceName]['class'], 'templateFactory'), $this, $resourceId, $cacheTime, $cacheId, $compileId, $parentTemplate);
* [util function] checks if the input is an array or an iterator object, optionally it can also check if it's empty
* @param mixed $value the variable to check
* @param bool $checkIsEmpty if true, the function will also check if the array is empty,
* and return true only if it's not empty
* @return bool true if it's an array (and not empty) or false if it's not an array (or if it's empty)
public function isArray($value, $checkIsEmpty=
false)
if ($checkIsEmpty ===
false) {
return count($value) >
0;
} elseif ($value instanceof
Iterator) {
if ($checkIsEmpty ===
false) {
} elseif ($value instanceof
Countable) {
return count($value) >
0;
} elseif ($value instanceof
ArrayAccess) {
if ($checkIsEmpty ===
false) {
} elseif ($value instanceof
Countable) {
return count($value) >
0;
return $value->offsetExists(0);
* [util function] triggers a dwoo error
* @param string $message the error message
* @param int $level the error level, one of the PHP's E_* constants
public function triggerError($message, $level=
E_USER_NOTICE)
trigger_error('Dwoo error (in '.
$tplIdentifier.
') : '.
$message, $level);
* --------- runtime functions ---------
* [runtime function] adds a block to the block stack
* @param string $blockName the block name (without Dwoo_Plugin_ prefix)
* @param array $args the arguments to be passed to the block's init() function
* @return Dwoo_Block_Plugin the newly created block
public function addStack($blockName, array $args=
array())
if (isset
($this->plugins[$blockName])) {
$class =
$this->plugins[$blockName]['class'];
$class =
'Dwoo_Plugin_'.
$blockName;
$block =
new $class($this);
$block->init($args[0], $args[1]);
$block->init($args[0], $args[1], $args[2]);
$block->init($args[0], $args[1], $args[2], $args[3]);
* [runtime function] removes the plugin at the top of the block stack
* calls the block buffer() function, followed by a call to end()
* and finally a call to process()
$this->curBlock->end($args[0], $args[1], $args[2], $args[3]);
* [runtime function] returns the parent block of the given block
* @param Dwoo_Block_Plugin $block
* @return Dwoo_Block_Plugin or false if the given block isn't in the stack
if ($index !==
false &&
$index >
0) {
return $this->stack[$index-
1];
* [runtime function] finds the closest block of the given type, starting at the top of the stack
* @param string $type the type of plugin you want to find
* @return Dwoo_Block_Plugin or false if no plugin of such type is in the stack
if (isset
($this->plugins[$type])) {
$type =
$this->plugins[$type]['class'];
$type =
'Dwoo_Plugin_'.
str_replace('Dwoo_Plugin_', '', $type);
while (($key =
array_pop($keys)) !==
false) {
if ($this->stack[$key] instanceof
$type) {
return $this->stack[$key];
* [runtime function] returns a Dwoo_Plugin of the given class
* this is so a single instance of every class plugin is created at each template run,
* allowing class plugins to have "per-template-run" static variables
* @param string $class the class name
* @return mixed an object of the given class
* [runtime function] calls the process() method of the given class-plugin name
* @param string $plugName the class plugin name (without Dwoo_Plugin_ prefix)
* @param array $params an array of parameters to send to the process() method
* @return string the process() return value
public function classCall($plugName, array $params =
array())
$class =
'Dwoo_Plugin_'.
$plugName;
return $plugin->process();
return $plugin->process($params[0]);
return $plugin->process($params[0], $params[1]);
return $plugin->process($params[0], $params[1], $params[2]);
return $plugin->process($params[0], $params[1], $params[2], $params[3]);
* [runtime function] calls a php function
* @param string $callback the function to call
* @param array $params an array of parameters to send to the function
* @return mixed the return value of the called function
public function arrayMap($callback, array $params)
if ($params[0] ===
$this) {
if ((is_array($params[0]) ||
($params[0] instanceof
Iterator &&
$params[0] instanceof
ArrayAccess))) {
$out[] =
$callback($this, $items[$i]);
$out[] =
$callback($this, $items[$i], $params[2]);
$out[] =
$callback($this, $items[$i], $params[2], $params[3]);
$out[] =
$callback($items[$i]);
$out[] =
$callback($items[$i], $params[1]);
$out[] =
$callback($items[$i], $params[1], $params[2]);
$out[] =
$callback($items[$i], $params[1], $params[2], $params[3]);
* [runtime function] reads a variable into the given data array
* @param string $varstr the variable string, using dwoo variable syntax (i.e. "var.subvar[subsubvar]->property")
* @param mixed $data the data array or object to read from
* @param bool $safeRead if true, the function will check whether the index exists to prevent any notices from being output
public function readVarInto($varstr, $data, $safeRead =
false)
while (list
($k, $sep) =
each($m[1])) {
if ($sep ===
'.' ||
$sep ===
'[' ||
$sep ===
'') {
if ((is_array($data) ||
$data instanceof
ArrayAccess) &&
($safeRead ===
false || isset
($data[$m[2][$k]]))) {
$data =
$data[$m[2][$k]];
if (is_object($data) &&
($safeRead ===
false || isset
($data->$m[2][$k]))) {
$data =
$data->$m[2][$k];
* [runtime function] reads a variable into the parent scope
* @param int $parentLevels the amount of parent levels to go from the current scope
* @param string $varstr the variable string, using dwoo variable syntax (i.e. "var.subvar[subsubvar]->property")
while ($parentLevels--!==
0) {
* [runtime function] reads a variable into the current scope
* @param string $varstr the variable string, using dwoo variable syntax (i.e. "var.subvar[subsubvar]->property")
if (strstr($varstr, '.') ===
false &&
strstr($varstr, '[') ===
false &&
strstr($varstr, '->') ===
false) {
if ($varstr ===
'dwoo') {
} elseif ($varstr ===
'__' ||
$varstr ===
'_root' ) {
} elseif ($varstr ===
'_' ||
$varstr ===
'_parent') {
if (isset
($cur[$varstr])) {
if (substr($varstr, 0, 1) ===
'.') {
$varstr =
'dwoo'.
$varstr;
} elseif ($i ===
'__' ||
$i ===
'_root') {
} elseif ($i ===
'_' ||
$i ===
'_parent') {
while (list
($k, $sep) =
each($m[1])) {
if ($sep ===
'.' ||
$sep ===
'[' ||
$sep ===
'') {
if ((is_array($cur) ||
$cur instanceof
ArrayAccess) && isset
($cur[$m[2][$k]])) {
} elseif ($sep ===
'->') {
* [runtime function] assign the value to the given variable
* @param mixed $value the value to assign
* @param string $scope the variable string, using dwoo variable syntax (i.e. "var.subvar[subsubvar]->property")
* @return bool true if assigned correctly or false if a problem occured while parsing the var string
if (strstr($scope, '.') ===
false &&
strstr($scope, '->') ===
false) {
$this->scope[$scope] =
$value;
// TODO handle _root/_parent scopes ?
while (list
($k, $sep) =
each($m[1])) {
if ($sep ===
'.' ||
$sep ===
'[' ||
$sep ===
'') {
} elseif ($sep ===
'->') {
if ($last[0] ===
'.' ||
$last[0] ===
'[' ||
$last[0] ===
'') {
} elseif ($last[0] ===
'->') {
* [runtime function] sets the scope to the given scope string or array
* @param mixed $scope a string i.e. "level1.level2" or an array i.e. array("level1", "level2")
* @param bool $absolute if true, the scope is set from the top level scope and not from the current scope
* @return array the current scope tree
public function setScope($scope, $absolute =
false)
if ($bit ===
'_' ||
$bit ===
'_parent') {
} elseif ($bit ===
'__' ||
$bit ===
'_root') {
} elseif (isset
($this->scope[$bit])) {
* [runtime function] returns the entire data array
* [runtime function] returns a reference to the current scope
* Redirects all calls to unexisting to plugin proxy.
* @param string Method name
* @param array List of arguments
public function __call($method, $args) {
Documentation generated on Sat, 18 Jul 2009 21:04:54 +0200 by phpDocumentor 1.4.0