1 <?php
2 // vim: set expandtab tabstop=4 shiftwidth=4:
3 // This code that was derived from the original PHPLIB Template class
4 // is copyright by Kristian Koehntopp, NetUSE AG and was released
5 // under the LGPL.
6 //
7 // Authors: Kristian Koehntopp <kris@koehntopp.de> (original from PHPLIB)
8 // Bjoern Schotte <bjoern@rent-a-phpwizard.de> (PEARification)
9 // Martin Jansen <mj@php.net> (PEAR conformance)
10 //
11 // $Id: PHPLIB.php,v 1.14 2003/06/11 06:03:32 bjoern Exp $
12 //
14 //require_once "PEAR.php";
16 /**
17 * Converted PHPLIB Template class
18 *
19 * For those who want to use PHPLIB's fine template class,
20 * here's a PEAR conforming class with the original PHPLIB
21 * template code from phplib-stable CVS. Original author
22 * was Kristian Koehntopp <kris@koehntopp.de>
23 *
24 * @author Bjoern Schotte <bjoern@rent-a-phpwizard.de>
25 * @author Martin Jansen <mj@php.net> (PEAR conformance)
26 * @version 1.0
27 */
28 class Template_PHPLIB
29 {
30 /**
31 * If set, echo assignments
32 * @var bool
33 */
34 var $debug = false;
36 /**
37 * $file[handle] = "filename";
38 * @var array
39 */
40 var $file = array();
42 /**
43 * fallback paths that should be defined in a child class
44 * @var array
45 */
46 var $file_fallbacks = array();
48 /**
49 * Relative filenames are relative to this pathname
50 * @var string
51 */
52 var $root = "";
54 /*
55 * $_varKeys[key] = "key"
56 * @var array
57 */
58 var $_varKeys = array();
60 /**
61 * $_varVals[key] = "value";
62 * @var array
63 */
64 var $_varVals = array();
66 /**
67 * "remove" => remove undefined variables
68 * "comment" => replace undefined variables with comments
69 * "keep" => keep undefined variables
70 * @var string
71 */
72 var $unknowns = "remove";
74 /**
75 * "yes" => halt, "report" => report error, continue, "no" => ignore error quietly
76 * @var string
77 */
78 var $haltOnError = "report";
80 /**
81 * The last error message is retained here
82 * @var string
83 * @see halt
84 */
85 var $_lastError = "";
88 /**
89 * Constructor
90 *
91 * @access public
92 * @param string template root directory
93 * @param string how to handle unknown variables
94 * @param array fallback paths
95 */
96 function Template_PHPLIB($root = ".", $unknowns = "remove", $fallback="")
97 {
98 $this->setRoot($root);
99 $this->setUnknowns($unknowns);
100 if (is_array($fallback)) $this->file_fallbacks = $fallback;
101 }
103 /**
104 * Sets the template directory
105 *
106 * @access public
107 * @param string new template directory
108 * @return bool
109 */
110 function setRoot($root)
111 {
112 if (!is_dir($root)) {
113 $this->halt("setRoot: $root is not a directory.");
114 return false;
115 }
117 $this->root = $root;
119 return true;
120 }
122 /**
123 * What to do with unknown variables
124 *
125 * three possible values:
126 *
127 * - "remove" will remove unknown variables
128 * (don't use this if you define CSS in your page)
129 * - "comment" will replace undefined variables with comments
130 * - "keep" will keep undefined variables as-is
131 *
132 * @access public
133 * @param string unknowns
134 */
135 function setUnknowns($unknowns = "keep")
136 {
137 $this->unknowns = $unknowns;
138 }
140 /**
141 * Set appropriate template files
142 *
143 * With this method you set the template files you want to use.
144 * Either you supply an associative array with key/value pairs
145 * where the key is the handle for the filname and the value
146 * is the filename itself, or you define $handle as the file name
147 * handle and $filename as the filename if you want to define only
148 * one template.
149 *
150 * @access public
151 * @param mixed handle for a filename or array with handle/name value pairs
152 * @param string name of template file
153 * @return bool
154 */
155 function setFile($handle, $filename = "")
156 {
157 if (!is_array($handle)) {
159 if ($filename == "") {
160 $this->halt("setFile: For handle $handle filename is empty.");
161 return false;
162 }
164 $this->file[$handle] = $this->_filename($filename);
166 } else {
168 reset($handle);
169 while (list($h, $f) = each($handle)) {
170 $this->file[$h] = $this->_filename($f);
171 }
172 }
173 }
175 /**
176 * Set a block in the appropriate template handle
177 *
178 * By setting a block like that:
179 *
180 * <!-- BEGIN blockname -->
181 * html code
182 * <!-- END blockname -->
183 *
184 * you can easily do repeating HTML code, i.e. output
185 * database data nice formatted into a HTML table where
186 * each DB row is placed into a HTML table row which is
187 * defined in this block.
188 * It extracts the template $handle from $parent and places
189 * variable {$name} instead.
190 *
191 * @access public
192 * @param string parent handle
193 * @param string block name handle
194 * @param string variable substitution name
195 */
196 function setBlock($parent, $handle, $name = "")
197 {
198 if (!$this->_loadFile($parent)) {
199 $this->halt("setBlock: unable to load $parent.");
200 return false;
201 }
203 if ($name == "") {
204 $name = $handle;
205 }
207 $str = $this->getVar($parent);
208 $reg = "/[ \t]*<!--\s+BEGIN $handle\s+-->\s*?\n?(\s*.*?\n?)\s*<!--\s+END $handle\s+-->\s*?\n?/sm";
209 preg_match_all($reg, $str, $m);
210 $str = preg_replace($reg, "{" . "$name}", $str);
212 if (isset($m[1][0])) $this->setVar($handle, $m[1][0]);
213 $this->setVar($parent, $str);
214 }
216 /**
217 * Set corresponding substitutions for placeholders
218 *
219 * @access public
220 * @param string name of a variable that is to be defined or an array of variables with value substitution as key/value pairs
221 * @param string value of that variable
222 * @param boolean if true, the value is appended to the variable's existing value
223 */
224 function setVar($varname, $value = "", $append = false)
225 {
226 if (!is_array($varname)) {
228 if (!empty($varname))
229 if ($this->debug) print "scalar: set *$varname* to *$value*<br>\n";
231 $this->_varKeys[$varname] = $this->_varname($varname);
232 ($append) ? $this->_varVals[$varname] .= $value : $this->_varVals[$varname] = $value;
234 } else {
235 reset($varname);
237 while (list($k, $v) = each($varname)) {
238 if (!empty($k))
239 if ($this->debug) print "array: set *$k* to *$v*<br>\n";
241 $this->_varKeys[$k] = $this->_varname($k);
242 ($append) ? $this->_varVals[$k] .= $v : $this->_varVals[$k] = $v;
243 }
244 }
245 }
247 /**
248 * Substitute variables in handle $handle
249 *
250 * @access public
251 * @param string name of handle
252 * @return mixed string substituted content of handle
253 */
254 function subst($handle)
255 {
256 if (!$this->_loadFile($handle)) {
257 $this->halt("subst: unable to load $handle.");
258 return false;
259 }
261 return @str_replace($this->_varKeys, $this->_varVals, $this->getVar($handle));
262 }
264 /**
265 * Same as subst but printing the result
266 *
267 * @access public
268 * @brother subst
269 * @param string handle of template
270 * @return bool always false
271 */
272 function pSubst($handle)
273 {
274 print $this->subst($handle);
275 return false;
276 }
278 /**
279 * Parse handle into target
280 *
281 * Parses handle $handle into $target, eventually
282 * appending handle at $target if $append is defined
283 * as TRUE.
284 *
285 * @access public
286 * @param string target handle to parse into
287 * @param string which handle should be parsed
288 * @param boolean append it to $target or not?
289 * @return string parsed handle
290 */
291 function parse($target, $handle, $append = false)
292 {
293 if (!is_array($handle)) {
294 $str = $this->subst($handle);
296 ($append) ? $this->setVar($target, $this->getVar($target) . $str) : $this->setVar($target, $str);
297 } else {
298 reset($handle);
300 while (list(, $h) = each($handle)) {
301 $str = $this->subst($h);
302 $this->setVar($target, $str);
303 }
304 }
306 return $str;
307 }
309 /**
310 * Same as parse, but printing it.
311 *
312 * @access public
313 * @brother parse
314 * @param string target to parse into
315 * @param string handle which should be parsed
316 * @param should $handle be appended to $target?
317 * @return bool
318 */
319 function pParse($target, $handle, $append = false)
320 {
321 print $this->finish($this->parse($target, $handle, $append));
322 return false;
323 }
325 /**
326 * Return all defined variables and their values
327 *
328 * @access public
329 * @return array with all defined variables and their values
330 */
331 function getVars()
332 {
333 reset($this->_varKeys);
335 while (list($k, ) = each($this->_varKeys)) {
336 $result[$k] = $this->getVar($k);
337 }
339 return $result;
340 }
342 /**
343 * Return one or more specific variable(s) with their values.
344 *
345 * @access public
346 * @param mixed array with variable names or one variable name as a string
347 * @return mixed array of variable names with their values or value of one specific variable
348 */
349 function getVar($varname)
350 {
351 if (!is_array($varname)) {
352 if (isset($this->_varVals[$varname])) {
353 return $this->_varVals[$varname];
354 } else {
355 return "";
356 }
357 } else {
358 reset($varname);
360 while (list($k, ) = each($varname)) {
361 $result[$k] = (isset($this->_varVals[$k])) ? $this->_varVals[$k] : "";
362 }
364 return $result;
365 }
366 }
368 /**
369 * Get undefined values of a handle
370 *
371 * @access public
372 * @param string handle name
373 * @return mixed false if an error occured or the undefined values
374 */
375 function getUndefined($handle)
376 {
377 if (!$this->_loadFile($handle)) {
378 $this->halt("getUndefined: unable to load $handle.");
379 return false;
380 }
382 preg_match_all("/{([^ \t\r\n}]+)}/", $this->getVar($handle), $m);
383 $m = $m[1];
384 if (!is_array($m)) {
385 return false;
386 }
388 reset($m);
389 while (list(, $v) = each($m)) {
390 if (!isset($this->_varKeys[$v])) {
391 $result[$v] = $v;
392 }
393 }
395 if (isset($result) && count($result)) {
396 return $result;
397 } else {
398 return false;
399 }
400 }
402 /**
403 * Finish string
404 *
405 * @access public
406 * @param string string to finish
407 * @return finished, i.e. substituted string
408 */
409 function finish($str)
410 {
411 switch ($this->unknowns) {
412 case "remove":
413 $str = preg_replace('/{[^ \t\r\n}]+}/', "", $str);
414 break;
416 case "comment":
417 $str = preg_replace('/{([^ \t\r\n}]+)}/', "<!-- Template $handle: Variable \\1 undefined -->", $str);
418 break;
419 }
421 return $str;
422 }
424 /**
425 * Print variable to the browser
426 *
427 * @access public
428 * @param string name of variable to print
429 */
430 function p($varname)
431 {
432 print $this->finish($this->getVar($varname));
433 }
435 /**
436 * Get finished variable
437 *
438 * @access public public
439 * @param string variable to get
440 * @return string string with finished variable
441 */
442 function get($varname)
443 {
444 return $this->finish($this->getVar($varname));
445 }
447 /**
448 * Complete filename
449 *
450 * Complete filename, i.e. testing it for slashes
451 *
452 * @access private
453 * @param string filename to be completed
454 * @return string completed filename
455 */
456 function _filename($filename)
457 {
458 // if (substr($filename, 0, 1) != "/") {
459 // $filename = $this->root."/".$filename;
460 // }
462 if (file_exists($filename)) return $filename;
463 if (is_array($this->file_fallbacks) && count($this->file_fallbacks) > 0) {
464 reset($this->file_fallbacks);
465 while (list(,$v) = each($this->file_fallbacks)) {
466 if (file_exists($v.basename($filename))) return $v.basename($filename);
467 }
468 $this->halt(sprintf("filename: file %s does not exist in the fallback paths %s.",$filename,implode(",",$this->file_fallbacks)));
469 return false;
470 } else {
471 $this->halt(sprintf("filename: file %s does not exist.",$filename));
472 return false;
473 }
475 return $filename;
476 }
478 /**
479 * Protect a replacement variable
480 *
481 * @access private
482 * @param string name of replacement variable
483 * @return string replaced variable
484 */
485 function _varname($varname)
486 {
487 return "{".$varname."}";
488 }
490 /**
491 * load file defined by handle if it is not loaded yet
492 *
493 * @access private
494 * @param string handle
495 * @return bool FALSE if error, true if all is ok
496 */
497 function _loadFile($handle)
498 {
499 if (isset($this->_varKeys[$handle]) and !empty($this->_varVals[$handle])) {
500 return true;
501 }
503 if (!isset($this->file[$handle])) {
504 $this->halt("loadfile: $handle is not a valid handle.");
505 return false;
506 }
508 $filename = $this->file[$handle];
509 if (function_exists("file_get_contents")) {
510 $str = file_get_contents($filename);
511 } else {
512 if (!$fp = @fopen($filename,"r")) {
513 $this->halt("loadfile: couldn't open $filename");
514 return false;
515 }
517 $str = fread($fp,filesize($filename));
518 fclose($fp);
519 }
521 if ($str=='') {
522 $this->halt("loadfile: While loading $handle, $filename does not exist or is empty.");
523 return false;
524 }
526 $this->setVar($handle, $str);
528 return true;
529 }
531 /**
532 * Error function. Halt template system with message to show
533 *
534 * @access public
535 * @param string message to show
536 * @return bool
537 */
538 function halt($msg)
539 {
540 $this->_lastError = $msg;
542 if ($this->haltOnError != "no") {
543 // return $this->haltMsg($msg);
544 $this->haltMsg($msg);
545 }
547 if ($this->haltOnError == "yes") {
548 die("<b>Halted.</b>");
549 }
551 return false;
552 }
554 /**
555 * printf error message to show
556 *
557 * @access public
558 * @param string message to show
559 * @return object PEAR error object
560 */
561 function haltMsg($msg)
562 {
563 // PEAR::raiseError(sprintf("<b>Template Error:</b> %s<br>\n", $msg));
564 printf("<b>Template Error:</b> %s<br>\n", $msg);
565 }
566 }
567 ?>