PageRenderTime 64ms CodeModel.GetById 35ms RepoModel.GetById 0ms app.codeStats 0ms

/view/engines/hydrogen/HydrogenEngine.php

https://github.com/ThaDeanesta/Hydrogen
PHP | 268 lines | 85 code | 15 blank | 168 comment | 9 complexity | 0a6f6eac2f938f3e20ad3a97a19a7633 MD5 | raw file
  1. <?php
  2. /*
  3. * Copyright (c) 2009 - 2011, Frosted Design
  4. * All rights reserved.
  5. */
  6. namespace hydrogen\view\engines\hydrogen;
  7. use hydrogen\config\Config;
  8. use hydrogen\view\TemplateEngine;
  9. use hydrogen\view\engines\hydrogen\Parser;
  10. use hydrogen\view\engines\hydrogen\exceptions\NoSuchFilterException;
  11. use hydrogen\view\engines\hydrogen\exceptions\NoSuchTagException;
  12. /**
  13. * The Hydrogen Templating Engine is a robust template parser based on the
  14. * highly-acclaimed Django template language, with some key changes for
  15. * increased performance and more flexibility for template creators. See
  16. * linked documentation for more information.
  17. *
  18. * @link http://www.webdevrefinery.com/forums/topic/6404-hydrogen-templates-for-front-end-developers
  19. * Frontend Development Documentation
  20. */
  21. class HydrogenEngine implements TemplateEngine {
  22. protected static $filterClass = array();
  23. protected static $filterPath = array();
  24. protected static $filterNamespace = array(
  25. '\hydrogen\view\engines\hydrogen\filters\\'
  26. );
  27. protected static $tagClass = array();
  28. protected static $tagPath = array();
  29. protected static $tagNamespace = array(
  30. '\hydrogen\view\engines\hydrogen\tags\\'
  31. );
  32. /**
  33. * Adds an external filter to the Hydrogen template engine. Once added,
  34. * the filter can be used in any Hydrogen template file for the duration
  35. * of that request.
  36. *
  37. * @param string $filterName The name of the filter to add. The name is
  38. * what will be typed in the template files themselves.
  39. * @param string $className The class name (with namespace) of this
  40. * filter. The filter must implement the interface
  41. * {@link \hydrogen\view\engines\hydrogen\Filter}.
  42. * @param string $path The path to the PHP to require_once before using
  43. * the given class, either absolute or relative to the base_url
  44. * defined in the hydrogen.autoconfig.php file. This argument is
  45. * OPTIONAL: if omitted, Hydrogen will assume the class will be
  46. * automatically loaded when accessed.
  47. */
  48. public static function addFilter($filterName, $className, $path=false) {
  49. $filterName = strtolower($filterName);
  50. static::$filterClass[$filterName] = static::formatNamespace($className,
  51. false);
  52. if ($path)
  53. static::$filterPath[$filterName] = Config::getAbsolutePath($path);
  54. }
  55. /**
  56. * Adds an external tag to the Hydrogen template engine. Once added,
  57. * the tag can be used in any Hydrogen template file for the duration
  58. * of that request.
  59. *
  60. * @param string $tagName The name of the tag to add. The name is
  61. * what will be typed in the template files themselves.
  62. * @param string $className The class name (with namespace) of this
  63. * tag. The tag must extend the class
  64. * {@link \hydrogen\view\engines\hydrogen\Tag}.
  65. * @param string $path The path to the PHP to require_once before using
  66. * the given class, either absolute or relative to the base_url
  67. * defined in the hydrogen.autoconfig.php file. This argument is
  68. * OPTIONAL: if omitted, Hydrogen will assume the class will be
  69. * automatically loaded when accessed.
  70. */
  71. public static function addTag($tagName, $className, $path=false) {
  72. $tagName = strtolower($tagName);
  73. static::$tagClass[$tagName] = static::formatNamespace($className,
  74. false);
  75. if ($path)
  76. static::$tagPath[$tagName] = Config::getAbsolutePath($path);
  77. }
  78. /**
  79. * Adds a namespace from which filters will be automatically loaded if
  80. * they're not found in the default hydrogen namespace or any namespaces
  81. * previously added using this function. Only the namespace is to be
  82. * provided; Hydrogen will then attempt to load any unknown filters by
  83. * adding [filtername]Filter to the end of the namespace.
  84. *
  85. * For example, if the filter {{someVar|swedishchef}} is used in a
  86. * template, and this function was called with the namespace
  87. * '\myapp\filters', Hydrogen would attempt to load the class
  88. * '\myapp\filters\SwedishchefFilter' if it hasn't already been defined
  89. * with {@link addFilter()}. Note the capitalization: for autoloaded
  90. * filter names, only the first character of the filter name must be
  91. * capitalized, followed by 'Filter' with a capital F.
  92. *
  93. * The autoloading of this class is up to the programmer, though the file
  94. * hydrogen.inc.php can be used as a basis for good autoloading practices
  95. * for your own class files. If your code does not use namespaces, a
  96. * backslash can be provided for the $namespace argument.
  97. *
  98. * @param string $namespace The namespace to which filter classes should
  99. * be added to attempt to autoload them.
  100. */
  101. public static function addFilterNamespace($namespace) {
  102. static::$filterNamespace[] = static::formatNamespace($namespace);
  103. }
  104. /**
  105. * Adds a namespace from which tags will be automatically loaded if
  106. * they're not found in the default hydrogen namespace or any namespaces
  107. * previously added using this function. Only the namespace is to be
  108. * provided; Hydrogen will then attempt to load any unknown tags by
  109. * adding [tagname]Tag to the end of the namespace.
  110. *
  111. * For example, if the tag {% piglatin %} is used in a
  112. * template, and this function was called with the namespace
  113. * '\myapp\tags', Hydrogen would attempt to load the class
  114. * '\myapp\tags\PiglatinFilter' if it hasn't already been defined
  115. * with {@link addTag()}. Note the capitalization: for autoloaded
  116. * tag names, only the first character of the tag name must be
  117. * capitalized, followed by 'Tag' with a capital T.
  118. *
  119. * The autoloading of this class is up to the programmer, though the file
  120. * hydrogen.inc.php can be used as a basis for good autoloading practices
  121. * for your own class files. If your code does not use namespaces, a
  122. * backslash can be provided for the $namespace argument.
  123. *
  124. * @param string $namespace The namespace to which tag classes should
  125. * be added to attempt to autoload them.
  126. */
  127. public static function addTagNamespace($namespace) {
  128. static::$tagNamespace[] = static::formatNamespace($namespace);
  129. }
  130. /**
  131. * Gets the full class name (with namespace) for the given filter. The
  132. * class will either be pre-loaded by this function, or is expected to be
  133. * autoloaded when it is first accessed. The returned class name can be
  134. * used with no further error checking.
  135. *
  136. * @param string $filterName The filter for which to obtain the full
  137. * namespace + class name.
  138. * @param string $origin The template originating the request for this
  139. * filter. It is optional, but if provided, will be used to create
  140. * a more helpful error message should the requested filter not be
  141. * found.
  142. * @return string the full namespace + class name, with leading backslash,
  143. * for the provided filter name.
  144. * @throws NoSuchFilterException when the requested filter was not found.
  145. */
  146. public static function getFilterClass($filterName, $origin=false) {
  147. return static::getModuleClass($filterName, 'Filter',
  148. static::$filterClass, static::$filterPath,
  149. static::$filterNamespace, $origin);
  150. }
  151. /**
  152. * Gets the full class name (with namespace) for the given tag. The
  153. * class will either be pre-loaded by this function, or is expected to be
  154. * autoloaded when it is first accessed. The returned class name can be
  155. * used with no further error checking.
  156. *
  157. * @param string $tagName The tag for which to obtain the full
  158. * namespace + class name.
  159. * @param string $origin The template originating the request for this
  160. * tag. It is optional, but if provided, will be used to create
  161. * a more helpful error message should the requested tag not be
  162. * found.
  163. * @return string the full namespace + class name, with leading backslash,
  164. * for the provided tag name.
  165. * @throws NoSuchTagException when the requested tag was not found.
  166. */
  167. public static function getTagClass($tagName, $origin=false) {
  168. return static::getModuleClass($tagName, 'Tag',
  169. static::$tagClass, static::$tagPath,
  170. static::$tagNamespace, $origin);
  171. }
  172. /**
  173. * Generates the raw PHP for the given template, using the given loader to
  174. * load the template files. Works as specified by
  175. * {@link \hydrogen\view\TemplateEngine}.
  176. *
  177. * @param string $templateName The name of the template to load.
  178. * @param \hydrogen\view\Loader $loader The loader with which to load
  179. * template files.
  180. * @return string the full, raw PHP that can be executed in a
  181. * {@link \hydrogen\view\ViewSandbox}.
  182. */
  183. public static function getPHP($templateName, $loader) {
  184. $parser = new Parser($templateName, $loader);
  185. $nodes = $parser->parse();
  186. return $nodes->render();
  187. }
  188. /**
  189. * Adds a leading backslash to the provided namespace if it's missing one,
  190. * and optionally adds as trailing backslash as well.
  191. *
  192. * @param string $namespace The namespace to start with.
  193. * @param boolean $endSlash true to add a trailing slash to the namespace
  194. * if it doesn't already have one, false otherwise. This is an
  195. * optional argument. Defaults to true.
  196. * @return string the formatted namespace.
  197. */
  198. protected static function formatNamespace($namespace, $endSlash=true) {
  199. if ($namespace[0] !== '\\')
  200. $namespace = '\\' . $namespace;
  201. if ($endSlash && $namespace[strlen($namespace) - 1] !== '\\')
  202. $namespace .= '\\';
  203. return $namespace;
  204. }
  205. /**
  206. * Gets the full class name for a certain module (tag or filter) using the
  207. * provided arrays of data. This is an abstraction for
  208. * {@link getFilterClass} and {@link getTagClass}.
  209. *
  210. * @param string $modName The name of the filter or tag module for which
  211. * to search.
  212. * @param string $modType The type of module to be loaded. Should be
  213. * either 'Tag' or 'Filter', case sensitive. There is no error
  214. * checking for this value.
  215. * @param array $modClasses An associative array of lowercase module
  216. * names to full namespace + class names.
  217. * @param array $modPaths An associative array of lowercase module names
  218. * to absolute paths to the PHP file that contains them.
  219. * @param array $modNamespaces An array of namespaces in which to search
  220. * for $modName+$modType if it was not found in $modClasses.
  221. * @param string $origin The template name requesting this module. This
  222. * value is optional, but helps to produce more helpful error messages
  223. * should the module not be found.
  224. * @throws NoSuchFilterException if the $modType is 'Filter' and the
  225. * filter was not found.
  226. * @throws NoSuchTagException if the $modType is 'Tag' and the tag was not
  227. * found.
  228. */
  229. protected static function getModuleClass($modName, $modType, &$modClasses,
  230. &$modPaths, &$modNamespaces, $origin=false) {
  231. $lowName = strtolower($modName);
  232. if (isset($modClasses[$lowName])) {
  233. if (isset($modPaths[$lowName]))
  234. require_once($modPaths[$lowName]);
  235. return $modClasses[$lowName];
  236. }
  237. $properName = ucfirst($lowName) . $modType;
  238. foreach ($modNamespaces as $namespace) {
  239. $class = $namespace . $properName;
  240. if (@class_exists($class)) {
  241. $modClasses[$lowName] = $class;
  242. return $class;
  243. }
  244. }
  245. $error = $modType . ' "' . $modName . '" does not exist' .
  246. ($origin ? ' in template "' . $origin . '".' : '.');
  247. if ($modType === 'Filter')
  248. throw new NoSuchFilterException($error);
  249. else
  250. throw new NoSuchTagException($error);
  251. }
  252. }
  253. ?>