Capsule.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. <?php
  2. /**
  3. * Capsule is a simple "template" engine that essentially provides an isolated context
  4. * for PHP scripts.
  5. *
  6. * There is no special templating language, and therefore no limitations to what
  7. * can be accomplished within templates. The main purpose of Capsule is to separate
  8. * the business logic from display / output logic.
  9. *
  10. * @author Hans Lellelid <hans@xmpl.org>
  11. * @version $Revision: 905 $
  12. * @package phing.lib
  13. */
  14. class Capsule {
  15. /**
  16. * Look for templates here (if relative path provided).
  17. * @var string
  18. */
  19. protected $templatePath;
  20. /**
  21. * Where should output files be written?
  22. * (This is named inconsistently to be compatible w/ Texen.)
  23. * @var string
  24. */
  25. protected $outputDirectory;
  26. /**
  27. * The variables that can be used by the templates.
  28. * @var array Hash of variables.
  29. */
  30. public $vars = array();
  31. /**
  32. * Has template been initialized.
  33. */
  34. protected $initialized = false;
  35. /**
  36. * Stores the pre-parse() include_path.
  37. * @var string
  38. */
  39. private $old_include_path;
  40. function __construct() {
  41. }
  42. /**
  43. * Clears one or several or all variables.
  44. * @param mixed $which String name of var, or array of names.
  45. * @return void
  46. */
  47. function clear($which = null) {
  48. if ($which === null) {
  49. $this->vars = array();
  50. } elseif (is_array($which)) {
  51. foreach($which as $var) {
  52. unset($this->vars[$var]);
  53. }
  54. } else {
  55. unset($this->vars[$which]);
  56. }
  57. }
  58. /**
  59. * Set the basepath to use for template lookups.
  60. * @param string $v
  61. */
  62. function setTemplatePath($v) {
  63. $this->templatePath = rtrim($v, DIRECTORY_SEPARATOR.'/');
  64. }
  65. /**
  66. * Get the basepath to use for template lookups.
  67. * @return string
  68. */
  69. function getTemplatePath() {
  70. return $this->templatePath;
  71. }
  72. /**
  73. * Set a basepath to use for output file creation.
  74. * @param string $v
  75. */
  76. function setOutputDirectory($v) {
  77. $this->outputDirectory = rtrim($v, DIRECTORY_SEPARATOR.'/');
  78. }
  79. /**
  80. * Get basepath to use for output file creation.
  81. * @return string
  82. */
  83. function getOutputDirectory() {
  84. return $this->outputDirectory;
  85. }
  86. /**
  87. * Low overhead (no output buffering) method to simply dump template
  88. * to buffer.
  89. *
  90. * @param string $__template
  91. * @return void
  92. * @throws Exception - if template cannot be found
  93. */
  94. function display($__template) {
  95. // Prepend "private" variable names with $__ in this function
  96. // to keep namespace conflict potential to a minimum.
  97. // Alias this class to $generator.
  98. $generator = $this;
  99. if (isset($this->vars['this'])) {
  100. throw new Exception("Assigning a variable named \$this to a context conflicts with class namespace.");
  101. }
  102. // extract variables into local namespace
  103. extract($this->vars);
  104. // prepend template path to include path,
  105. // so that include "path/relative/to/templates"; can be used within templates
  106. $__old_inc_path = ini_get('include_path');
  107. ini_set('include_path', $this->templatePath . PATH_SEPARATOR . $__old_inc_path);
  108. @ini_set('track_errors', true);
  109. include $__template;
  110. @ini_restore('track_errors');
  111. // restore the include path
  112. ini_set('include_path', $__old_inc_path);
  113. if (!empty($php_errormsg)) {
  114. throw new Exception("Unable to parse template " . $__template . ": " . $php_errormsg);
  115. }
  116. }
  117. /**
  118. * Fetches the results of a tempalte parse and either returns
  119. * the string or writes results to a specified output file.
  120. *
  121. * @param string $template The template filename (relative to templatePath or absolute).
  122. * @param string $outputFile If specified, contents of template will also be written to this file.
  123. * @param boolean $append Should output be appended to source file?
  124. * @return string The "parsed" template output.
  125. * @throws Exception - if template not found.
  126. */
  127. function parse($template, $outputFile = null, $append = false) {
  128. // main work done right here:
  129. // hopefully this works recursively ... fingers crossed.
  130. ob_start();
  131. try {
  132. $this->display($template);
  133. } catch (Exception $e) {
  134. ob_end_flush(); // flush the output on error (so we can see up to what point it parsed everything)
  135. throw $e;
  136. }
  137. $output = ob_get_contents();
  138. ob_end_clean();
  139. if ($outputFile !== null) {
  140. $outputFile = $this->resolvePath($outputFile, $this->outputDirectory);
  141. $flags = null;
  142. if ($append) $flags = FILE_APPEND;
  143. if (!file_put_contents($outputFile, $output, $flags) && $output != "") {
  144. throw new Exception("Unable to write output to " . $outputFile);
  145. }
  146. }
  147. return $output;
  148. }
  149. /**
  150. * This returns a "best guess" path for the given file.
  151. *
  152. * @param string $file File name or possibly absolute path.
  153. * @param string $basepath The basepath that should be prepended if $file is not absolute.
  154. * @return string "Best guess" path for this file.
  155. */
  156. protected function resolvePath($file, $basepath) {
  157. if ( !($file{0} == DIRECTORY_SEPARATOR || $file{0} == '/')
  158. // also account for C:\ style path
  159. && !($file{1} == ':' && ($file{2} == DIRECTORY_SEPARATOR || $file{2} == '/'))) {
  160. if ($basepath != null) {
  161. $file = $basepath . DIRECTORY_SEPARATOR . $file;
  162. }
  163. }
  164. return $file;
  165. }
  166. /**
  167. * Gets value of specified var or NULL if var has not been put().
  168. * @param string $name Variable name to retrieve.
  169. * @return mixed
  170. */
  171. function get($name) {
  172. if (!isset($this->vars[$name])) return null;
  173. return $this->vars[$name];
  174. }
  175. /**
  176. * Merges in passed hash to vars array.
  177. *
  178. * Given an array like:
  179. *
  180. * array( 'myvar' => 'Hello',
  181. * 'myvar2' => 'Hello')
  182. *
  183. * Resulting template will have access to $myvar and $myvar2.
  184. *
  185. * @param array $vars
  186. * @param boolean $recursiveMerge Should matching keys be recursively merged?
  187. * @return void
  188. */
  189. function putAll($vars, $recursiveMerge = false) {
  190. if ($recursiveMerge) {
  191. $this->vars = array_merge_recursive($this->vars, $vars);
  192. } else {
  193. $this->vars = array_merge($this->vars, $vars);
  194. }
  195. }
  196. /**
  197. * Adds a variable to the context.
  198. *
  199. * Resulting template will have access to ${$name$} variable.
  200. *
  201. * @param string $name
  202. * @param mixed $value
  203. */
  204. function put($name, $value) {
  205. $this->vars[$name] = $value;
  206. }
  207. /**
  208. * Put a variable into the context, assigning it by reference.
  209. * This means that if the template modifies the variable, then it
  210. * will also be modified in the context.
  211. *
  212. * @param $name
  213. * @param &$value
  214. */
  215. function putRef($name, &$value) {
  216. $this->vars[$name] = &$value;
  217. }
  218. /**
  219. * Makes a copy of the value and puts it into the context.
  220. * This is primarily to force copying (cloning) of objects, rather
  221. * than the default behavior which is to assign them by reference.
  222. * @param string $name
  223. * @param mixed $value
  224. */
  225. function putCopy($name, $value) {
  226. if (is_object($value)) {
  227. $value = clone $value;
  228. }
  229. $this->vars[$name] = $value;
  230. }
  231. }