1 | <?php |
||
33 | class Hamle { |
||
34 | /** |
||
35 | * @var Setup instance of hamleSetup Object |
||
36 | */ |
||
37 | public $setup; |
||
38 | /** |
||
39 | * @var Hamle Instance of the 'current' hamle Engine |
||
40 | */ |
||
41 | static protected $me; |
||
42 | /** |
||
43 | * @var Parse Parser Instance |
||
44 | */ |
||
45 | public $parse; |
||
46 | /** |
||
47 | * @var string Filename for Cache file |
||
48 | */ |
||
49 | protected $cacheFile; |
||
50 | /** |
||
51 | * @var bool Enable cacheing of templates |
||
52 | */ |
||
53 | protected $cache = true; |
||
54 | /** |
||
55 | * @var array Array of Files required $files[0] is the template file |
||
56 | * The rest of the files are Snippets |
||
57 | */ |
||
58 | protected $snipFiles; |
||
59 | /** |
||
60 | * @var int Timestamp of latest modification to snipppet file |
||
61 | */ |
||
62 | protected $snipMod = 0; |
||
63 | |||
64 | public $baseModel; |
||
65 | |||
66 | const REL_CHILD = 0x01; /* Child Relation */ |
||
67 | const REL_PARENT = 0x02; /* Parent Relation */ |
||
68 | const REL_ANY = 0x03; /* Unspecified or any relation */ |
||
69 | |||
70 | const SORT_NATURAL = 0x00; /* Sort in what ever order is 'default' */ |
||
71 | const SORT_ASCENDING = 0x02; /* Sort Ascending */ |
||
72 | const SORT_DESCENDING = 0x03; /* Sort Decending */ |
||
73 | const SORT_RANDOM = 0x04; /* Sort Randomly */ |
||
74 | /** |
||
75 | * Create new HAMLE Parser |
||
76 | * |
||
77 | * @param Model $baseModel |
||
78 | * @param Setup $setup |
||
79 | * @throws Exception\Unsupported |
||
80 | * @throws Exception\NotFound |
||
81 | */ |
||
82 | function __construct($baseModel, $setup = NULL) { |
||
83 | self::$me = $this; |
||
84 | if(!$setup) |
||
85 | $setup = new Setup(); |
||
86 | $this->parse = new Parse(); |
||
87 | if(!$setup instanceOf Setup) |
||
88 | throw new Exception\Unsupported("Unsupported Setup Helper was passed, it must extends hamleSetup"); |
||
89 | if(!$baseModel instanceOf Model) |
||
90 | throw new Exception\Unsupported("Unsupported Model(".get_class($baseModel).") Type was passed, it must implement hamleModel"); |
||
91 | $this->setup = $setup; |
||
92 | $this->baseModel = $baseModel; |
||
93 | $this->initSnipFiles(); |
||
94 | } |
||
95 | |||
96 | function initSnipFiles() { |
||
97 | if($this->snipMod == 0) { |
||
98 | $this->snipFiles = $this->setup->snippetFiles(); |
||
99 | foreach($this->snipFiles as $f) { |
||
100 | if (!file_exists($f)) throw new Exception\NotFound("Unable to find Snippet File ($f)"); |
||
101 | $this->snipFiles = max($this->snipFiles, filemtime($f)); |
||
102 | } |
||
103 | } |
||
104 | } |
||
105 | |||
106 | /** |
||
107 | * Parse a HAMLE Template File |
||
108 | * @param string $hamleFile Template File Name (will have path gathered from hamleSetup->templatePath |
||
109 | * @throws Exception\NotFound If tempalte file cannot be found |
||
110 | * @return Hamle Returns instance for chaining commands |
||
111 | */ |
||
112 | function load($hamleFile, \Closure $parseFunc = null) { |
||
113 | $template = $this->setup->templatePath($hamleFile); |
||
114 | if(!file_exists($template)) |
||
115 | throw new Exception\NotFound("Unable to find HAMLE Template ($template)"); |
||
116 | $this->cacheFile = $this->setup->cachePath( |
||
117 | str_replace("/","-",$hamleFile).".php"); |
||
118 | $this->setup->debugLog("Set cache file path to ({$this->cacheFile})"); |
||
119 | $cacheFileAge = is_file($this->cacheFile)?filemtime($this->cacheFile):0; |
||
120 | $cacheDirty = !$this->cache || |
||
121 | $cacheFileAge < $this->snipMod || $cacheFileAge < filemtime($template); |
||
122 | if($cacheDirty) { |
||
123 | $this->setup->debugLog("Parsing File ($template to {$this->cacheFile})"); |
||
124 | $this->parse($parseFunc?"":file_get_contents($template), $parseFunc); |
||
125 | } else |
||
126 | $this->setup->debugLog("Using Cached file ({$this->cacheFile})"); |
||
127 | return $this; |
||
128 | } |
||
129 | /** |
||
130 | * Parse a HAMLE tempalte from a string |
||
131 | * _WARNING_ Template Sting will *NOT* be cached, it will be parsed every time |
||
132 | * |
||
133 | * @internal Not for general use, use string($h) instead |
||
134 | * @param string $hamleCode Hamle Template as string |
||
135 | * @throws Exception\ParseError if unable to write to the cache file |
||
136 | */ |
||
137 | function parse($hamleCode, \Closure $parseFunc = null) { |
||
138 | if(!$this->cacheFile) |
||
139 | $this->cacheFile = $this->setup->cachePath("string.hamle.php"); |
||
140 | if($parseFunc) |
||
141 | $parseFunc($this->parse); |
||
142 | else |
||
143 | $this->parse->str($hamleCode); |
||
144 | $this->setup->debugLog("Loading Snippet Files"); |
||
145 | foreach($this->snipFiles as $snip) |
||
146 | $this->parse->parseSnip(file_get_contents($snip)); |
||
147 | $this->setup->debugLog("Applying Snippet Files"); |
||
148 | $this->parse->applySnip(); |
||
149 | $this->setup->debugLog("Executing Parse Filters"); |
||
150 | foreach($this->setup->getFilters() as $filter) |
||
151 | $this->parse->parseFilter($filter); |
||
152 | $this->setup->debugLog("Updating Cache File ({$this->cacheFile})"); |
||
153 | if(FALSE === file_put_contents($this->cacheFile, $this->parse->output())) |
||
154 | throw new Exception\ParseError( |
||
155 | "Unable to write to cache file ({$this->cacheFile})"); |
||
156 | } |
||
157 | |||
158 | /** |
||
159 | * Parse a HAMLE String, and cache output |
||
160 | * @param $hamleString string Hamle |
||
161 | */ |
||
162 | function string($hamleString) { |
||
163 | $md5 = md5($hamleString); |
||
164 | $stringId = substr($md5,0,12).substr($md5,24,8); |
||
165 | $this->cacheFile = $this->setup->cachePath("string.$stringId.hamle.php"); |
||
166 | if(!is_file($this->cacheFile)) |
||
167 | $this->parse($hamleString); |
||
168 | } |
||
169 | |||
170 | /** |
||
171 | * Produce HTML Output from hamle Template file |
||
172 | * @return string HTML Output as String |
||
173 | * @throws Exception |
||
174 | */ |
||
175 | function output() { |
||
176 | try { |
||
177 | ob_start(); |
||
178 | Run::addInstance($this); |
||
179 | $baseModel = $this->baseModel; |
||
180 | $this->baseModel = null; |
||
181 | $currentModel = $baseModel == Scope::getTopScope(); |
||
182 | if(!$currentModel && $baseModel) Scope::add($baseModel); |
||
183 | require $this->cacheFile; |
||
184 | if(!$currentModel && $baseModel) Scope::done(); |
||
185 | $this->baseModel = $baseModel; |
||
186 | $out = ob_get_contents(); |
||
187 | ob_end_clean(); |
||
188 | } catch (\Exception $e) { |
||
189 | ob_end_clean(); |
||
190 | throw $e; |
||
191 | } |
||
192 | Run::popInstance(); |
||
193 | return $out; |
||
194 | } |
||
195 | |||
196 | /** |
||
197 | * Get the current line number |
||
198 | * @return int The line number being passed by the parser |
||
199 | */ |
||
200 | static function getLineNo() { |
||
201 | if(!isset(self::$me)) |
||
202 | return 0; |
||
203 | return self::$me->parse->getLineNo(); |
||
204 | } |
||
205 | |||
206 | /** |
||
207 | * Disable the caching of hamle templates |
||
208 | */ |
||
209 | function disableCache() { |
||
210 | $this->cache = false; |
||
211 | } |
||
212 | |||
213 | } |
||
214 | |||
215 |