habarimag-old /system/classes/hiengine.php

Language PHP Lines 479
MD5 Hash d304b500286d0c69509acfd80b970fde Estimated Cost $7,285 (why?)
Repository https://github.com/HabariMag/habarimag-old.git View Raw File View Project SPDX
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
<?php
/**
 * @package Habari
 *
 */

/**
 *
 * Habari HiEngine class
 *
 * The HiEngine is a subclass of the RawPHPEngine class
 * which is intended for those theme designers who want to use
 * simple {hi:*} tags four output instead of PHP
 *
 * To use this engine, specify "hiengine" in the theme.xml of a theme.
 *
 * This engine behaves exactly like RawPHPEngine after the template tags are
 * processed, so if an existing RawPHPEngine template is switched to HiEngine,
 * it should still run without issue.
 */
class HiEngine extends RawPHPEngine {
	/**
	 *
	 * Constructor for HiEngine
	 *
	 * Sets up the stream protocol handler
	 */
	public function __construct()
	{
		$streams = stream_get_wrappers();
		if ( ! in_array( 'hi', $streams ) ) {
			stream_wrapper_register( "hi", "HiEngineParser" )
			or die( _t( "Failed to register HiEngine stream protocol" ) );
		}
	}

	/**
	 *
	 * A function which outputs the result of a transposed
	 * template to the output stream
	 * @param template $ Name of template to display
	 */
	public function display( $template )
	{
		extract( $this->engine_vars );
		//Utils::debug($this->engine_vars);die();
		if ( $this->template_exists( $template ) ) {
			$template_file = isset( $this->template_map[$template] ) ? $this->template_map[$template] : null;
			$template_file = Plugins::filter( 'include_template_file', $template_file, $template, __CLASS__ );
			$template_file = 'hi://' . $template_file;
			$fc = file_get_contents( $template_file );
			//echo($fc);
			eval( '?'.'>' . $fc );
			//include $template_file;  // stopped working properly in PHP 5.2.8 
		}
	}
}

/**
 * HiEngineParser - A stream filtering class for the HiEngine
 */
class HiEngineParser
{
	protected $file;
	protected $filename;
	protected $position;
	protected $contexts;
	protected $strings = array();

	/**
	 * Open a HiEngineParser stream
	 *
	 * @param string $path Path of the opened resource, including the protocol specifier
	 * @param string $mode Mode used to open the file
	 * @param integer $options Bitmask options for opening this stream
	 * @param string $opened_path The actual path opened if using relative path, by reference
	 * @return boolean true on success
	 */
	function stream_open( $path, $mode, $options, &$opened_path )
	{
		$this->filename = substr( $path, 5 );
		$this->file = file_get_contents( $this->filename );

		// This processed value should cache and invalidate if a checksum of the template changes!  :)
		$this->file = $this->process( $this->file );

		$this->position = 0;

		return true;
	}

	/**
	 * Read data from a HiEngineParser stream
	 *
	 * @param integer $count Number of characters to read from the current position
	 * @return string Characters read from the stream
	 */
	function stream_read( $count )
	{
		if ( $this->stream_eof() ) {
			return false;
		}
		$ret = substr( $this->file, $this->position, $count );
		$this->position += strlen( $ret );
		return $ret;
	}

	/**
	 * Srite data to a HiEngineParser stream
	 *
	 * @param string $data Data to write
	 * @return boolean false, since this stream type is read-only
	 */
	function stream_write( $data )
	{
		// HiEngineParser streams are read-only
		return false;
	}

	/**
	 * Report the position in the stream
	 *
	 * @return integer the position in the stream
	 */
	function stream_tell()
	{
		return $this->position;
	}

	/**
	 * Report whether the stream is at the end of the file
	 *
	 * @return boolean true if the file pointer is at or beyond the end of the file
	 */
	function stream_eof()
	{
		return $this->position >= strlen( $this->file );
	}

	/**
	 * Seek to a specific position within the stream
	 *
	 * @param integer $offset The offset from the specified position
	 * @param integer $whence The position to seek from
	 * @return boolean true if seek was successful
	 */
	function stream_seek( $offset, $whence )
	{
		switch ( $whence ) {
			case SEEK_SET:
				if ( $offset < strlen( $this->file ) && $offset >= 0 ) {
					$this->position = $offset;
					return true;
				}
				else {
					return false;
				}
				break;

			case SEEK_CUR:
				if ( $offset >= 0 ) {
					$this->position += $offset;
					return true;
				}
				else {
					return false;
				}
				break;

			case SEEK_END:
				if ( strlen( $this->file ) + $offset >= 0 ) {
					$this->position = strlen( $this->file ) + $offset;
					return true;
				}
				else {
					return false;
				}
				break;

			default:
				return false;
		}
		
	}

	/**
	 * Return fstat() info as required when calling stats on the stream
	 * @return array An array of stat info
	 */
	function stream_stat()
	{
		return array();
	}

	/**
	 * Process the template file for template tags
	 *
	 * @param string $template The template file contents
	 * @return string The processed template
	 */
	function process( $template )
	{
		$template = preg_replace_callback( '/\{hi:(".+?")((?:\s*[\w\.]+){0,2})\s*\}/smu', array( $this, 'hi_quote' ), $template );
		$template = preg_replace_callback( '%\{hi:([^\?]+?)\}(.+?)\{/hi:\1\}%ism', array( $this, 'hi_loop' ), $template );
		$template = preg_replace_callback( '%\{hi:\?\s*(.+?)\}(.+?)\{/hi:\?\}%ismu', array( $this, 'hi_if' ), $template );
		$template = preg_replace_callback( '%\{hi:([^:}]+?:.+?)\}%i', array( $this, 'hi_command' ), $template );
		$template = preg_replace_callback( '%\{hi:(.+?)\}%i', array( $this, 'hi_var' ), $template );
		return $template;
	}
	
	/**
	 * Replace a single function template tag with its PHP counterpart
	 *
	 * @param array $matches The match array found in HiEngineParser::process()
	 * @return string The PHP replacement for the function template tag
	 */
	function hi_command( $matches )
	{
		$cmd = trim( $matches[1] );

		// Catch tags in the format {hi:command:parameter}
		if ( preg_match( '/^(\w+):(.+)$/u', $cmd, $cmd_matches ) ) {
			switch ( strtolower( $cmd_matches[1] ) ) {
				case 'area':
					return '<?php $theme->area(\'' . $cmd_matches[2] . '\'); ?>';
				case 'display':
					return '<?php $theme->display(\'' . $cmd_matches[2] . '\'); ?>';
				case 'option':
				case 'options':
					return '<?php Options::out(\'' . $cmd_matches[2] . '\'); ?>';
				case 'siteurl':
					return '<?php Site::out_url( \'' . $cmd_matches[2] . '\' ); ?>';
				case 'url':
					return '<?php URL::out( \'' . $cmd_matches[2] . '\' ); ?>';
				case 'session':
					switch ( $cmd_matches[2] ) {
						case 'messages':
							return '<?php if (Session::has_messages()){Session::messages_out();} ?>';
						case 'errors':
							return '<?php if (Session::has_errors()){Session::messages_out();} ?>';
					}
				// this is an internal match
				case 'context':
					return $this->hi_to_var( $cmd_matches[2] );
				case 'escape':
					return '<?php echo Utils::htmlspecialchars( ' . $this->hi_to_var( $cmd_matches[2] ) . ' ); ?>';
			}
		}
		
		return $matches[0];
	}

	/**
	 * Replace a single template tag with its PHP counterpart
	 *
	 * @param array $matches The match array found in HiEngineParser::process()
	 * @return string The PHP replacement for the template tag
	 */
	function hi_var( $matches )
	{
		$cmd = trim( $matches[1] );
		$params = array();
		$returnval = false;

		if ( preg_match_all( '/(?<=\s)(?P<name>[@a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff.]*)\s*=\s*(?P<value>(?P<quot>["\']).+?\3|[^"\'\s]+)/i', $cmd, $foundparams, PREG_SET_ORDER ) ) {
			foreach ( $foundparams as $p ) {
				$params[$p['name']] = trim( $p['value'], $p['quot'] );
			}
		}

		// Straight variable or property output, ala {hi:variable_name} or {hi:post.title}
		if ( preg_match( '%(^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff.]*)%i', $cmd, $cmdmatch ) ) {
			$cmdmatch = str_replace( '.', '->', $cmdmatch[1] );
			if ( count( $this->contexts ) ) {
				// Build a conditional that checks for the most specific, then the least
				// eg.- $a->b->c->d->x, then $a->b->c->x, down to just $x
				$ctx = $this->contexts;
				$prefixes = array();
				foreach ( $ctx as $void ) {
					$prefixes[] = implode( '->', $this->contexts );
					array_pop( $ctx );
				}
				$output = '';
				foreach ( $prefixes as $prefix ) {
					$output .= '(is_object($' . $prefix . ') && !'.'is_null($' . $prefix . '->' . $cmdmatch . ')) ? $' . $prefix . '->' . $cmdmatch . ' : ';
				}
				$output .= '$' . $cmdmatch;
				$returnval = $output;
			}
			else {
				$returnval = '$'. $cmdmatch;
			}
		}
		if ( $returnval !== false ) {
			$returnval = $this->apply_parameters( $returnval, $params );
			return '<?php echo '. $returnval . '; ?>';
		}

		// Use tags in the format {hi:@foo} as theme functions, ala $theme->foo();
		if ( $cmd[0] == '@' ) {
			return '<?php $theme->' . substr( $cmd, 1 ) . '(); ?>';
		}

		// Didn't match anything we support so far
		return $matches[0];
	}
	
	/**
	 * Take the found paramters on a variable tag and apply them to the output
	 *
	 * @param array $returnval The expression to be output
	 * @param array $params An associative array of parameters
	 * @return string The PHP expression with the paramters applied
	 */
	function apply_parameters( $returnval, $params )
	{
		foreach ( $params as $k => $v ) {
			if ( $k[0] == '@' ) {
				$returnval = '$theme->' . substr( $cmd, 1 ) . '(' . $returnval . ", '" . $v . "')";
			}
			switch ( $k ) {
				case 'dateformat':
					$returnval = "call_user_func(array(" . $returnval . ", 'format'), '" . addslashes( $v ) . "')";
					break; 
			}
		}
		return $returnval;
	}

	/**
	 * Replace a loop tag section with its PHP counterpart, and add the context to the stack
	 *
	 * @param array $matches The match array found in HiEngineParser::process()
	 * @return string The PHP replacement for the template tag
	 */
	function hi_loop( $matches )
	{
		$hivar = $matches[1];
		$phpvar = $this->hi_to_var( $hivar );
		$iterator = strpos( $hivar, '.' ) ? substr( $hivar, strrpos( $hivar, '.' ) + 1 ) : $hivar;
		$output = '<?php foreach(' . $phpvar . ' as $' . $iterator . '_index => $' . $iterator . '_1): ?>';
		$this->contexts[] = "{$iterator}_1";
		$output .= $this->process( $matches[2] );
		$output .= '<?php endforeach; ?>';
		array_pop( $this->contexts );
		return $output;
	}

	/**
	 * Replace variables in the hiengine syntax with PHP varaibles
	 * @param array $matches The match array found in hi_if ()
	 * @returns string A PHP variable string to use as the replacement
	 */
	function var_replace( $matches )
	{
		$var = $matches[1];

		if ( is_callable( $var ) ) {
			return $var;
		}
		if ( preg_match( '/true|false|null|isset|empty/i', $var ) ) {
			return $var;
		}

		$var = $this->hi_to_var( $var );

		return $var;
	}

	function hi_to_var( $hisyntax )
	{
		$var = str_replace( '.', '->', $hisyntax );
		if ( count( $this->contexts ) ) {
			// Build a conditional that checks for the most specific, then the least
			// eg.- $a->b->c->d->x, then $a->b->c->x, down to just $x
			$ctx = $this->contexts;
			$prefixes = array();
			foreach ( $ctx as $void ) {
				$prefixes[] = implode( '->', $this->contexts );
				array_pop( $ctx );
			}
			$output = '';
			foreach ( $prefixes as $prefix ) {
				$output .= '(!is_null($' . $prefix . '->' . $var . ') ? $' . $prefix . '->' . $var . ' : ';
			}
			$output .= '$' . $var . ')';
			return $output;
		}
		else {
			return '$'. $var;
		}
	}

	/**
	 * Creates a table of static strings in hiengine expressions to be replaced in later
	 * @param array $matches The match found in hi_if ()
	 * @returns string An uncommon string index for the stored static string.
	 */
	function string_stack( $matches )
	{
		$key = chr( 0 ) . count( $this->strings ) . chr( 1 );
		$this->strings[$key] = $matches[0];
		return $key;
	}

	/**
	 * Replace an if tag section with its PHP counterpart
	 *
	 * @param array $matches The match array found in HiEngineParser::process()
	 * @return string The PHP replacement for the template tag
	 */
	function hi_if ( $matches )
	{
		list( $void, $eval, $context ) = $matches;

		$eval = preg_replace_callback( '/([\'"]).*?(?<!\\\\)\1/i', array( $this, 'string_stack' ), $eval );
		$eval = preg_replace_callback( '/\b((?<!::)[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff.]*(?!::))\b/i', array( $this, 'var_replace' ), $eval );
		$eval = preg_replace( '/(?<!=)=(?!=)/i', '==', $eval );
		$eval = str_replace( array_keys( $this->strings ), $this->strings, $eval );

		$context = preg_replace( '/\{hi:\?else\?\}/i', '<?php else: ?>', $context );

		$output = '<?php if (' . $eval . '): ?>';
		$output .= $this->process( $context );
		$output .= '<?php endif; ?>';
		return $output;
	}

	/**
	* Prepare strings for translation
	* @param array $matches Matches in HiEngineParser::process()
	* @param string The PHP replacement for the template tag
	*/
	function hi_quote( $matches )
	{
		$args = preg_split( '/\s+/u', trim( $matches[2] ) );

		preg_match_all( '/"(.+?)(?<!\\\\)"/', $matches[1], $quotes );
		$count = 0;
		$all_vars = array();
		foreach ( $quotes[1] as $index => $quote ) {
			preg_match_all( '/{hi:([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff.]*)}/', $quote, $vars, PREG_SET_ORDER );
			foreach ( $vars as $var ) {
				$count++;
				$quote = str_replace( $var[0], '%'.$count.'\$s', $quote );
				$all_vars[] = '{hi:context:' . $var[1] . '}';  //$this->hi_to_var($var[1]);
			}
			$quotes[1][$index] = $quote;
		}
		if ( count( $quotes[1] ) > 1 ) {
			$output = '<?php printf(_n("'.$quotes[1][0].'", "'.$quotes[1][1].'", {hi:context:'.$args[0].'})';
			// Add vars
			if ( count( $all_vars ) > 0 ) {
				$output .= ', ' . implode( ', ', $all_vars );
			}
			array_shift( $args );
		}
		else {
			$output = '<?php _e("'.$quotes[1][0].'"';
			// Add vars
			if ( count( $all_vars ) > 0 ) {
				$output .= ', array(' . implode( ', ', $all_vars ) . ')';
			}
		}

		// Add the domain, if any
		if ( isset( $args[0] ) ) {
			$output .= ', "'.$args[0].'"';
		}

		// Close the tag
		$output .= '); ?>';

		return $output;
	}
}

?>
Back to Top