1<?php 2/** 3 * PHPTAL templating engine 4 * 5 * PHP Version 5 6 * 7 * @category HTML 8 * @package PHPTAL 9 * @author Laurent Bedubourg <lbedubourg@motion-twin.com> 10 * @author Kornel Lesiński <kornel@aardvarkmedia.co.uk> 11 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License 12 * @version SVN: $Id$ 13 * @link http://phptal.org/ 14 */ 15 16/** 17 * Exception that is related to location within a template. 18 * You can check srcFile and srcLine to find source of the error. 19 * 20 * @package PHPTAL 21 * @subpackage Exception 22 */ 23class PHPTAL_TemplateException extends PHPTAL_Exception 24{ 25 public $srcFile; 26 public $srcLine; 27 private $is_src_accurate; 28 29 public function __construct($msg, $srcFile='', $srcLine=0) 30 { 31 parent::__construct($msg); 32 33 if ($srcFile && $srcLine) { 34 $this->srcFile = $srcFile; 35 $this->srcLine = $srcLine; 36 $this->is_src_accurate = true; 37 } else { 38 $this->is_src_accurate = $this->setTemplateSource(); 39 } 40 41 if ($this->is_src_accurate) { 42 $this->file = $this->srcFile; 43 $this->line = (int)$this->srcLine; 44 } 45 } 46 47 public function __toString() 48 { 49 if (!$this->srcFile || $this->is_src_accurate) return parent::__toString(); 50 return "From {$this->srcFile} around line {$this->srcLine}\n".parent::__toString(); 51 } 52 53 /** 54 * Set new TAL source file/line if it isn't known already 55 */ 56 public function hintSrcPosition($srcFile, $srcLine) 57 { 58 if ($srcFile && $srcLine) { 59 if (!$this->is_src_accurate) { 60 $this->srcFile = $srcFile; 61 $this->srcLine = $srcLine; 62 $this->is_src_accurate = true; 63 } else if ($this->srcLine <= 1 && $this->srcFile === $srcFile) { 64 $this->srcLine = $srcLine; 65 } 66 } 67 68 if ($this->is_src_accurate) { 69 $this->file = $this->srcFile; 70 $this->line = (int)$this->srcLine; 71 } 72 } 73 74 private function isTemplatePath($path) 75 { 76 return preg_match('/[\\\\\/]tpl_[0-9a-f]{8}_[^\\\\]+$/', $path); 77 } 78 79 private function findFileAndLine() 80 { 81 if ($this->isTemplatePath($this->file)) { 82 return array($this->file, $this->line); 83 } 84 85 $eval_line = 0; 86 $eval_path = NULL; 87 88 // searches backtrace to find template file 89 foreach($this->getTrace() as $tr) { 90 if (!isset($tr['file'],$tr['line'])) continue; 91 92 if ($this->isTemplatePath($tr['file'])) { 93 return array($tr['file'], $tr['line']); 94 } 95 96 // PHPTAL.php uses eval() on first run to catch fatal errors. This makes template path invisible. 97 // However, function name matches template path and eval() is visible in backtrace. 98 if (false !== strpos($tr['file'], 'eval()')) { 99 $eval_line = $tr['line']; 100 } 101 else if ($eval_line && isset($tr['function'],$tr['args'],$tr['args'][0]) && 102 $this->isTemplatePath("/".$tr['function'].".php") && $tr['args'][0] instanceof PHPTAL) { 103 return array($tr['args'][0]->getCodePath(), $eval_line); 104 } 105 } 106 107 return array(NULL,NULL); 108 } 109 110 /** 111 * sets srcLine and srcFile to template path and source line 112 * by checking error backtrace and scanning PHP code file 113 * 114 * @return bool true if found accurate data 115 */ 116 private function setTemplateSource() 117 { 118 // not accurate, but better than null 119 $this->srcFile = $this->file; 120 $this->srcLine = $this->line; 121 122 list($file,$line) = $this->findFileAndLine(); 123 124 if (NULL === $file) { 125 return false; 126 } 127 128 // this is not accurate yet, hopefully will be overwritten later 129 $this->srcFile = $file; 130 $this->srcLine = $line; 131 132 $lines = @file($file); 133 if (!$lines) { 134 return false; 135 } 136 137 $found_line=false; 138 $found_file=false; 139 140 // scan lines backwards looking for "from line" comments 141 $end = min(count($lines), $line)-1; 142 for($i=$end; $i >= 0; $i--) { 143 if (preg_match('/tag "[^"]*" from line (\d+)/', $lines[$i], $m)) { 144 $this->srcLine = intval($m[1]); 145 $found_line=true; 146 break; 147 } 148 } 149 150 foreach(preg_grep('/Generated by PHPTAL from/',$lines) as $line) { 151 if (preg_match('/Generated by PHPTAL from (.*) \(/', $line, $m)) { 152 $this->srcFile = $m[1]; 153 $found_file=true; 154 break; 155 } 156 } 157 158 return $found_line && $found_file; 159 } 160} 161