PageRenderTime 87ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/Mad/View/Base.php

https://github.com/smoke/maintainable-framework
PHP | 313 lines | 116 code | 31 blank | 166 comment | 18 complexity | 3f0999cc47a76d738b006253cca1db81 MD5 | raw file
  1. <?php
  2. /**
  3. * Mad_View_Base is the "V" in MVC and provides encapsulation for
  4. * presentation logic. It allows for templates to be written in
  5. * mostly HTML with lightweight embedded PHP helpers.
  6. *
  7. * @category Mad
  8. * @package Mad_View
  9. * @copyright (c) 2007-2009 Maintainable Software, LLC
  10. * @license http://opensource.org/licenses/bsd-license.php BSD
  11. */
  12. /**
  13. * Mad_View_Base is the "V" in MVC and provides encapsulation for
  14. * presentation logic. It allows for templates to be written in
  15. * mostly HTML with lightweight embedded PHP helpers.
  16. *
  17. * @category Mad
  18. * @package Mad_View
  19. * @copyright (c) 2007-2009 Maintainable Software, LLC
  20. * @license http://opensource.org/licenses/bsd-license.php BSD
  21. */
  22. class Mad_View_Base
  23. {
  24. public static $defaultFormBuilder = 'Mad_View_Helper_Form_Builder';
  25. /**
  26. * Template path stack
  27. * @var array
  28. */
  29. private $_paths = array();
  30. /**
  31. * Stack of helper objects methods with associated objects
  32. * @var array
  33. */
  34. private $_helpers = array();
  35. /**
  36. * The controller to delegate component rendering to
  37. * @var object
  38. */
  39. public $controller = null;
  40. /**
  41. * Constructor. Sets default paths. If we want to use component rendering,
  42. * we must have a controller to delegate the request to
  43. *
  44. * @param object $controller
  45. */
  46. public function __construct($controller = null)
  47. {
  48. $this->addPath('app/views/');
  49. $this->controller = $controller;
  50. }
  51. /**
  52. * Accesses a helper object from within a template.
  53. *
  54. * @param string $name The helper name.
  55. * @param array $args The parameters for the helper.
  56. * @return string The result of the helper output.
  57. * @throws Mad_View_Exception
  58. */
  59. public function __call($name, $args)
  60. {
  61. if (!isset($this->_helpers[$name])) {
  62. throw new Mad_View_Exception("The helper method \"$name\" does not exist");
  63. }
  64. // call the helper method
  65. return call_user_func_array(array($this->_helpers[$name], $name), $args);
  66. }
  67. /**
  68. * Undefined variables return null.
  69. *
  70. * @return null
  71. */
  72. public function __get($offset)
  73. {
  74. return null;
  75. }
  76. /*##########################################################################
  77. # Public
  78. ##########################################################################*/
  79. /**
  80. * Add to the list of template filepaths in LIFO order. Relative
  81. * paths are automatically prepended with MAD_ROOT.
  82. *
  83. * <code>
  84. * <?php
  85. * ...
  86. * $view->addPath('app/views/layout/');
  87. * $view->addPath('app/views/shared/');
  88. * $view->addPath('app/views/FooBar/');
  89. * ...
  90. * ?>
  91. * </code>
  92. *
  93. * This would look for the template file in the order:
  94. * 1. {MAD_ROOT}/app/views/FooBar/template.html
  95. * 2. {MAD_ROOT}/app/views/shared/template.html
  96. * 3. {MAD_ROOT}/app/views/layout/template.html
  97. *
  98. * @see Mad_Controller_Base::_initViewPaths();
  99. * @param string $path
  100. * @param boolean $relative Is $path relative to MAD_ROOT?
  101. */
  102. public function addPath($path, $relative = true)
  103. {
  104. if ($relative) {
  105. $path = MAD_ROOT .'/'. $path;
  106. }
  107. if (substr($path, -1) != '/') {
  108. $path .= '/';
  109. }
  110. array_unshift($this->_paths, $path);
  111. }
  112. /**
  113. * Get the list of paths to the template files
  114. * @return array
  115. */
  116. public function getPaths()
  117. {
  118. return $this->_paths;
  119. }
  120. /**
  121. * Adds all of the built-in Mad_View_Helpers to this instance
  122. *
  123. * @todo We'll come up with a lazy-load strategy in the future.
  124. */
  125. public function addBuiltinHelpers()
  126. {
  127. $dir = dirname(__FILE__) . '/Helper';
  128. foreach (new DirectoryIterator($dir) as $f) {
  129. if ($f->isFile()) {
  130. $class = "Mad_View_Helper_"
  131. . str_replace('.php', '', $f->getFilename());
  132. if ($class != 'Mad_View_Helper_Base') {
  133. $this->addHelper(new $class($this));
  134. }
  135. }
  136. }
  137. }
  138. /**
  139. * Add a helper to this view. This will make all the methods available
  140. * in the helper class accessible from the view template.
  141. * Performs reflection to get list of public methods available as helpers.
  142. * One thing to watch out for is that helper method names do not collide
  143. *
  144. * <code>
  145. * <?php
  146. * ...
  147. * $view->addHelper(new HelperObject);
  148. * ...
  149. * ?>
  150. *
  151. * // now we can call methods defined in HelperObject from our template
  152. * <div>
  153. * <?= $this->helperObjectMethod($data) ?>
  154. * </div>
  155. *
  156. * </code>
  157. *
  158. * @param object $helper
  159. * @throws Mad_View_Exception
  160. */
  161. public function addHelper(Mad_View_Helper_Base $helpers)
  162. {
  163. foreach (get_class_methods($helpers) as $method) {
  164. if (substr($method, 0, 1) != '_') {
  165. $this->_helpers[$method] = $helpers;
  166. }
  167. }
  168. }
  169. /*##########################################################################
  170. # Rendering Methods
  171. ##########################################################################*/
  172. /**
  173. * Processes a view template and returns the output. Add .html extension
  174. * as default if no extension has been given.
  175. *
  176. * <code>
  177. * <?php
  178. * ...
  179. * $result = $view->render('index');
  180. * ...
  181. * ?>
  182. * </code>
  183. *
  184. * @param string $name The template name to process.
  185. * @param array $locals
  186. * @return string The template output.
  187. */
  188. public function render($name, $locals = array())
  189. {
  190. // render partial
  191. if (is_array($name) && $partial = $name['partial']) {
  192. unset($name['partial']);
  193. return $this->renderPartial($partial, $name);
  194. }
  195. // append missing html
  196. if (!strstr($name, '.')) { $name .= '.html'; }
  197. return $this->_template($name, $locals);
  198. }
  199. /**
  200. * Render a partial template. Partial template filenames are named with
  201. * a leading underscore, although this underscore is not used when
  202. * specifying the name of the partial.
  203. *
  204. * we would reference the file /views/shared/_sidebarInfo.html
  205. * in our template using:
  206. *
  207. * <code>
  208. * <div>
  209. * <?= $this->renderPartial('sidebarInfo'); ?>
  210. * </div>
  211. * </code>
  212. *
  213. * @param string $name
  214. * @param array $options
  215. * @return string The template output
  216. */
  217. public function renderPartial($name, $options=array())
  218. {
  219. // pop name off of the path
  220. $parts = strstr($name, '/') ? explode('/', $name) : array($name);
  221. $name = array_pop($parts);
  222. $path = implode('/', $parts)."/";
  223. // check if they passed in a collection before validating keys
  224. $useCollection = array_key_exists('collection', $options);
  225. $valid = array('object', 'locals' => array(), 'collection' => array());
  226. $options = Mad_Support_Base::assertValidKeys($options, $valid);
  227. $locals = array($name => null);
  228. // set the object variable
  229. if ($options['object']) {
  230. $locals[$name] = $options['object'];
  231. }
  232. // set local variables to be used in the partial
  233. foreach ($options['locals'] as $key => $val) {
  234. $locals[$key] = $val;
  235. }
  236. // collection
  237. if ($useCollection) {
  238. $rendered = '';
  239. if ($options['collection'] instanceof Mad_Model_Collection ||
  240. $options['collection'] instanceof Mad_Model_PaginatedCollection ||
  241. is_array($options['collection'])) {
  242. $sz = count($options['collection']);
  243. for ($i = 0; $i < $sz; $i++) {
  244. $locals["{$name}Counter"] = $i;
  245. $locals[$name] = $options['collection'][$i];
  246. $rendered .= $this->render("{$path}_{$name}", $locals);
  247. }
  248. }
  249. // single render
  250. } else {
  251. $rendered = $this->render("{$path}_{$name}", $locals);
  252. }
  253. return $rendered;
  254. }
  255. /*##########################################################################
  256. # Private Methods
  257. ##########################################################################*/
  258. /**
  259. * Finds a view template from the available directories.
  260. *
  261. * @param string $name The base name of the template.
  262. * @param array $locals The array of local variables to make available
  263. * @throws Mad_View_Exception
  264. */
  265. private function _template($name, $locals)
  266. {
  267. // set local variables
  268. foreach ($locals as $key => $value) {
  269. ${$key} = $value;
  270. }
  271. foreach ($this->_paths as $dir) {
  272. $path = $dir.$name;
  273. if (is_readable($path)) {
  274. ob_start();
  275. include "madview://$path";
  276. $buffer = ob_get_clean();
  277. return $buffer;
  278. }
  279. }
  280. throw new Mad_View_Exception("template '$name' not found");
  281. }
  282. }