Project

General

Profile

1
<?php
2
//error_reporting(E_ALL ^ E_NOTICE);
3
/*~ class.quickskin.php
4
.---------------------------------------------------------------------------.
5
|  Software: QuickSkin Class                                                |
6
|   Version: 5.0                                                            |
7
|   Contact: andy.prevost@worxteam.com,andy@codeworx.ca                     |
8
|      Info: http://quickskin.sourceforge.net                               |
9
|   Support: http://sourceforge.net/projects/quickskin/                     |
10
| ------------------------------------------------------------------------- |
11
|    Author: Andy Prevost andy.prevost@worxteam.com (admin)                 |
12
|    Author: Manuel 'EndelWar' Dalla Lana endelwar@aregar.it (former admin) |
13
|    Author: Philipp v. Criegern philipp@criegern.com (original founder)    |
14
| Copyright (c) 2002-2009, Andy Prevost. All Rights Reserved.               |
15
|    * NOTE: QuickSkin is the SmartTemplate project renamed. SmartTemplate  |
16
|            information and downloads can still be accessed at the         |
17
|            smarttemplate.sourceforge.net site                             |
18
| ------------------------------------------------------------------------- |
19
|   License: Distributed under the Lesser General Public License (LGPL)     |
20
|            http://www.gnu.org/copyleft/lesser.html                        |
21
| This program is distributed in the hope that it will be useful - WITHOUT  |
22
| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or     |
23
| FITNESS FOR A PARTICULAR PURPOSE.                                         |
24
| ------------------------------------------------------------------------- |
25
| We offer a number of paid services:                                       |
26
| - Web Hosting on highly optimized fast and secure servers                 |
27
| - Technology Consulting                                                   |
28
| - Oursourcing (highly qualified programmers and graphic designers)        |
29
'---------------------------------------------------------------------------'
30
Last modified: January 01 2009 ~*/
31

    
32
/* designed to work with PHP5 - will NOT work with PHP4 */
33

    
34
/* PURPOSE: 'Compiles' HTML-Templates to PHP Code
35
 *
36
 * Usage Example I:
37
 *
38
 * $page = new QuickSkin( "template.html" );
39
 * $page->assign( 'TITLE',  'TemplateDemo - Userlist' );
40
 * $page->assign( 'user',   DB_read_all( 'select * from ris_user' ) );
41
 * $page->output();
42
 *
43
 * Usage Example II:
44
 *
45
 * $data = array(
46
 *             'TITLE' => 'TemplateDemo - Userlist',
47
 *             'user'  => DB_read_all( 'select * from ris_user' )
48
 *         );
49
 * $page = new QuickSkin( "template.html" );
50
 * $page->output( $data );
51
 *
52
 */
53

    
54
class QuickSkin {
55

    
56
  /////////////////////////////////////////////////
57
  // PROPERTIES, PUBLIC
58
  /////////////////////////////////////////////////
59

    
60
  /**
61
   * Reuse Code
62
   * Whether to use stored compiled php code or not (for debug purpose)
63
   * @var bool
64
   */
65
  public $reuse_code       = false;
66

    
67
  /**
68
   * Template Directory
69
   * Directory where all templates are stored, can be overwritten by
70
   * global configuration array $_CONFIG['template_dir']
71
   * @var string
72
   */
73
  public $template_dir     = '_skins/';
74

    
75
  /**
76
   * Temp Directory
77
   * Where to store compiled templates, can be overwritten by
78
   * global configuration array $_CONFIG['quickskin_compiled']
79
   * @var string
80
   */
81
  public $temp_dir         = '_skins_tmp/';
82

    
83
  /**
84
   * Cache Directory
85
   * Temporary folder for output cache storage, can be overwritten by
86
   * global configuration array $_CONFIG['quickskin_cache']
87
   * Note: typically set the same as the Temp Directory, but can be unique
88
   * @var string
89
   */
90
  public $cache_dir         = '_skins_tmp/';
91

    
92
  /**
93
   * Cache Lifetime
94
   * Default Output Cache Lifetime in Seconds, can be overwritten by
95
   * global configuration array $_CONFIG['cache_lifetime']
96
   * @var int
97
   */
98
  public $cache_lifetime    = 600; // seconds
99

    
100
  /**
101
   * Extensions Directory
102
   * Directory where all extensions are stored
103
   * @var string
104
   */
105
  public $extensions_dir    = '_lib/qx';      /* Directory where all extensions are stored */
106

    
107
  /**
108
   * Extension Prefix
109
   * Filename prefix on all the extensions files
110
   * @var string
111
   */
112
  public $extension_prefix  = 'qx_';
113

    
114
  /**
115
   * Left Delimiter
116
   * Default Left delimiter, can be overwritten by
117
   * global configuration array $_CONFIG['left_delimiter']
118
   * @var string
119
   */
120
  public $left_delimiter    = '{';
121

    
122
  /**
123
   * Right Delimiter
124
   * Default Right delimiter, can be overwritten by
125
   * global configuration array $_CONFIG['right_delimiter']
126
   * @var string
127
   */
128
  public $right_delimiter   = '}';
129

    
130
  /**
131
   * Extension Tagged
132
   * List of used QuickSkin Extensions
133
   * @var array
134
   */
135
  public $extension_tagged  = array();
136

    
137
  /**
138
   * QuickSkin Version Number
139
   * List of used QuickSkin Extensions
140
   * @var string
141
   */
142
  public $version           = '5.0';
143

    
144
  /////////////////////////////////////////////////
145
  // PROPERTIES, PRIVATE
146
  /////////////////////////////////////////////////
147

    
148
  private $cache_filename; /* Temporary file for output cache storage */
149
  private $tpl_file;       /* The template filename */
150
  private $cpl_file;       /* The compiled template filename */
151
  private $data = array(); /* Template content array */
152
  private $parser;         /* Parser Class */
153
  private $debugger;       /* Debugger Class */
154
  private $skins_sub_dir;  /* temporary variable to hold the subdirectory of the main template */
155
  private $supp_templates = '';   /* supplementary templates */
156
  private $supptemplate   = '';   /* supplementary template */
157

    
158
  /* QuickSkin Constructor
159
   * @access public
160
   * @param string $template_filename Template Filename
161
   */
162
  function __construct( $template_filename = '' ) {
163
    global $_CONFIG;
164
	// make extension directory setting
165
	if (!empty($_CONFIG['extensions_dir'])) {
166
      $this->extensions_dir  =  $_CONFIG['extensions_dir'];
167
    }  
168
    if (!empty($_CONFIG['quickskin_compiled'])) {
169
      $this->temp_dir  =  $_CONFIG['quickskin_compiled'];
170
    }
171
    if (!empty($_CONFIG['quickskin_cache'])) {
172
      $this->cache_dir  =  $_CONFIG['quickskin_cache'];
173
    }
174
    if (is_numeric($_CONFIG['cache_lifetime'])) {
175
      $this->cache_lifetime  =  $_CONFIG['cache_lifetime'];
176
    }
177
    if (!empty($_CONFIG['template_dir'])  &&  is_file($_CONFIG['template_dir'] . '/' . $template_filename)) {
178
      $this->template_dir  =  $_CONFIG['template_dir'];
179
    }
180
    $this->tpl_file  =  $template_filename;
181
    if ( dirname($this->tpl_file) != "") {
182
      $this->skins_sub_dir = dirname($this->tpl_file);
183
    }
184
  }
185

    
186
  /* DEPRECATED METHODS */
187
  /* Methods used in older parser versions, soon will be removed */
188
  function set_templatefile ($template_filename)  { $this->tpl_file  =  $template_filename; }
189
  function add_value ($name, $value )       { $this->assign($name, $value); }
190
  function add_array ($name, $value )       { $this->append($name, $value); }
191

    
192
  /* Process file or contents to strip out the <body tag (inclusive)
193
   * and the </body tag to the end
194
   *
195
   * Usage Example:
196
   * $page->getContents( '', '/contents.htm' );
197
   * or
198
   * $page->getContents( 'start of data .... end of data' );
199
   *
200
   * @access public
201
   * @param string $contents Parameter contents
202
   * @param string $filename Parameter filename (fully qualified)
203
   * @desc strip out body tags and return only page data
204
   */
205
  function getContents($contents, $filename="") {
206
    if ( $contents == '' && $filename != '' && file_exists($filename) ) {
207
      $contents = file_get_contents($filename);
208
    }
209

    
210
    // START process any PHP code
211
    ob_start();
212
    eval("?>".$contents."<?php ");
213
    $contents = ob_get_contents();
214
    ob_end_clean();
215
    // END process any PHP code
216
    $lower_contents = strtolower($contents);
217
    // determine if a <body tag exists and process if necessary
218
    $bodytag_start = strpos($lower_contents, "<body");
219
    if ( $bodytag_start !== false ) {
220
      $bodytag_end    = strpos($lower_contents, ">", $bodytag_start) + 1;
221
      // get contents with <body tag removed
222
      $contents       = substr($contents, $bodytag_end);
223
      $lower_contents = strtolower($contents);
224
      // work on </body closing tag
225
      $end_start      = strpos($lower_contents, "</body");
226
      $end_end        = strpos($lower_contents, ">", $bodytag_start) + 1;
227
      // return stripped out <body and </body tags
228
      return $this->getExtensions( substr($contents, 0, $end_start) );
229
    } else {
230
      // body tags not found, so return data
231
      return $this->getExtensions( $contents );
232
    }
233
  }
234

    
235
  /* Determine Contents Command from Variable Name
236
   * {variable}             :  array( "echo",              "variable" )  ->  echo $_obj['variable']
237
   * {variable > new_name}  :  array( "_obj['new_name']=", "variable" )  ->  $_obj['new_name']= $_obj['variable']
238
   * @param string $tag Variale Name used in Template
239
   * @return array  Array Command, Variable
240
   * @access private
241
   * @desc Determine Contents Command from Variable Name
242
   */
243
  function processCmd($tag) {
244
    if (preg_match('/^(.+) > ([a-zA-Z0-9_.]+)$/', $tag, $tagvar)) {
245
      $tag  =  $tagvar[1];
246
      list($newblock, $newskalar)  =  $this->var_name($tagvar[2]);
247
      $cmd  =  "\$$newblock"."['$newskalar']=";
248
    } else {
249
      $cmd  =  'echo';
250
    }
251
    $ret = array($cmd, $tag);
252
    return $ret;
253
  }
254

    
255
  /* Load and process the Extensions that may be used in the Contents
256
   *
257
   * Usage Example:
258
   * $tcnt = $this->getExtensions( $param );
259
   *
260
   * @access public
261
   * @param string $param (content to process)
262
   * @return string
263
   * @desc Load and process the Extensions that may be used in the Contents
264
   */
265
  function getExtensions($contents) {
266
    $header = '';
267
    /* Include Extensions */
268
    if (preg_match_all('/'.$this->left_delimiter.'([a-zA-Z0-9_]+):([^}]*)'.$this->right_delimiter.'/', $contents, $var)) {
269
      foreach ($var[2] as $cnt => $tag) {
270
        /* Determine Command (echo / $obj[n]=) */
271
        list($cmd, $tag)  =  $this->processCmd($tag);
272

    
273
        $extension  =  $var[1][$cnt];
274
        //if (!isset($this->extension_tagged[$extension])) {
275
          $header .= 'include_once "'.$this->extensions_dir."/".$this->extension_prefix."$extension.php\";\n";
276
        //  $this->extension_tagged[$extension]  =  true;
277
        //}
278
        if (!strlen($tag)) {
279
          $code  =  "<?php\n$cmd ".$this->extension_prefix."$extension();\n?>\n";
280
        } elseif (substr($tag, 0, 1) == '"') {
281
          $code  =  "<?php\n$cmd ".$this->extension_prefix."$extension($tag);\n?>\n";
282
        } elseif (strpos($tag, ',')) {
283
          list($tag, $addparam)  =  explode(',', $tag, 2);
284
          list($block, $skalar)  =  $this->var_name($tag);
285
          if (preg_match('/^([a-zA-Z_]+)/', $addparam, $match)) {
286
            $nexttag   =  $match[1];
287
            list($nextblock, $nextskalar)  =  $this->var_name($nexttag);
288
            $addparam  =  substr($addparam, strlen($nexttag));
289
            $code  =  "<?php\n$cmd ".$this->extension_prefix."$extension(\$$block"."['$skalar'],\$$nextblock"."['$nextskalar']"."$addparam);\n?>\n";
290
          } else {
291
            $code  =  "<?php\n$cmd ".$this->extension_prefix."$extension(\$$block"."['$skalar'],$addparam);\n?>\n";
292
          }
293
        } else {
294
          list($block, $skalar) = $this->var_name($tag);
295
          $code  =  "<?php\n$cmd ".$this->extension_prefix."$extension(\$$block"."['$skalar']);\n?>\n";
296
        }
297
        $contents  =  str_replace($var[0][$cnt],  $code,  $contents);
298
      }
299
    }
300
    // START process any PHP code
301
    ob_start();
302
    eval($header);
303
    eval("?>".$contents."<?php ");
304
    $contents = ob_get_contents();
305
    ob_end_clean();
306
    // END process any PHP code
307
    return $contents;
308
  }
309

    
310
  /* Assign Supplementary Template
311
   *
312
   * Usage Example:
313
   * $page->addtpl( 'sponsors', 'default/poweredby.htm' );
314
   *
315
   * @access public
316
   * @param string $name Parameter Name
317
   * @param string $value Parameter Value
318
   * @desc Assign Supplementary Template
319
   */
320
  function addtpl ( $name, $value = '' ) {
321
    if (is_array($name)) {
322
      foreach ($name as $k => $v) {
323
        $this->supptemplate[$k]  =  $v;
324
      }
325
    } else {
326
      $this->supptemplate[$name]  =  $value;
327
    }
328
  }
329

    
330
  /* Assign Template Content
331
   *
332
   * Usage Example:
333
   * $page->assign( 'TITLE',     'My Document Title' );
334
   * $page->assign( 'userlist',  array(
335
   *                               array( 'ID' => 123,  'NAME' => 'John Doe' ),
336
   *                               array( 'ID' => 124,  'NAME' => 'Jack Doe' ),
337
   *                             );
338
   *
339
   * @access public
340
   * @param string $name Parameter Name
341
   * @param mixed $value Parameter Value
342
   * @desc Assign Template Content
343
   */
344
  function assign ( $name, $value = '' ) {
345
    if (is_array($name)) {
346
      foreach ($name as $k => $v) {
347
        $this->data[$k]  =  $v;
348
      }
349
    } else {
350
      $this->data[$name]  =  $value;
351
    }
352
  }
353

    
354
  /* Assign Template Content
355
   *
356
   * Usage Example:
357
   * $page->append( 'userlist',  array( 'ID' => 123,  'NAME' => 'John Doe' ) );
358
   * $page->append( 'userlist',  array( 'ID' => 124,  'NAME' => 'Jack Doe' ) );
359
   *
360
   * @access public
361
   * @param string $name Parameter Name
362
   * @param mixed $value Parameter Value
363
   * @desc Assign Template Content
364
   */
365
  function append ( $name, $value ) {
366
    if (is_array($value)) {
367
      $this->data[$name][]  =  $value;
368
    } elseif (!is_array($this->data[$name])) {
369
      $this->data[$name]  .=  $value;
370
    }
371
  }
372

    
373
  /* Parser Wrapper
374
   * Returns Template Output as a String
375
   *
376
   * @access public
377
   * @param array $_top Content Array
378
   * @return string  Parsed Template
379
   * @desc Output Buffer Parser Wrapper
380
   */
381
  function result ( $_top = '' ) {
382
    ob_start();
383
    $this->output( $_top );
384
    $result  =  ob_get_contents();
385
    ob_end_clean();
386
    return $result;
387
  }
388

    
389
  /* Execute parsed Template
390
   * Prints Parsing Results to Standard Output
391
   *
392
   * @access public
393
   * @param array $_top Content Array
394
   * @desc Execute parsed Template
395
   */
396
  function output ( $_top = '' ) {
397
    global $_top;
398

    
399
    $data   = $this->data;
400
    /* Process supplementary templates */
401
    if ( is_array($this->supptemplate) && !empty($this->supptemplate) ) { // passed by addtpl functionality
402
      foreach ($this->supptemplate as $key => $value) {
403
        $supp_templates[$key] = file_get_contents($value);
404
      }
405
    }
406

    
407
    /* Make sure that folder names have a trailing '/' */
408
    if (strlen($this->template_dir)  &&  substr($this->template_dir, -1) != '/') {
409
      $this->template_dir  .=  '/';
410
    }
411
    if (strlen($this->temp_dir)  &&  substr($this->temp_dir, -1) != '/') {
412
      $this->temp_dir  .=  '/';
413
    }
414

    
415
    /* Prepare Template Content*/
416
    if (!is_array($_top)) {
417
      if (strlen($_top)) {
418
        $this->tpl_file  =  $_top;
419
      }
420
      $_top  =  $this->data;
421
    }
422
    $_obj  =  &$_top;
423
    $_stack_cnt  =  0;
424
    $_stack[$_stack_cnt++]  =  $_obj;
425

    
426
    /* Check if template is already compiled */
427
    $queryString = $_SERVER['QUERY_STRING'];
428
    $cpl_file_name = preg_replace('/[:\/.\\\\]/', '_', $this->tpl_file . '?' . $queryString);
429
    if (strlen($cpl_file_name) > 0) {
430
      $cpl_file_name = 'qs_' . md5($cpl_file_name);
431
      $this->cpl_file  =  $this->temp_dir . $cpl_file_name . '.php';
432
      $compile_template  =  true;
433
      if ($this->reuse_code) {
434
        if (is_file($this->cpl_file)) {
435
          if ($this->mtime($this->cpl_file) > $this->mtime($this->template_dir . $this->tpl_file)) {
436
            $compile_template  =  false;
437
          }
438
        }
439
      }
440
      if ($compile_template) {
441
        $this->parser = new QuickSkinParser();
442
        $this->parser->template_dir     = $this->template_dir;
443
        $this->parser->skins_sub_dir    = $this->skins_sub_dir;
444
        $this->parser->tpl_file         = $this->tpl_file;
445
        $this->parser->left_delimiter   = $this->left_delimiter;
446
        $this->parser->right_delimiter  = $this->right_delimiter;
447
        $this->parser->extensions_dir   = $this->extensions_dir;
448
        $this->parser->extension_prefix = $this->extension_prefix;
449
        $this->parser->supp_templates   = $this->supp_templates;
450

    
451
        if (!$this->parser->compile($this->cpl_file,$data,$this->supp_templates,$this->extensions_dir)) {
452
          exit('QuickSkin Parser Error: ' . $this->parser->error);
453
        }
454
      }
455
      /* Execute Compiled Template */
456
      include($this->cpl_file);
457
    } else {
458
      exit('QuickSkin Error: You must set a template file name');
459
    }
460
    /* Delete Global Content Array in order to allow multiple use of QuickSkin class in one script */
461
    unset ($GLOBALS['_top']);
462
  }
463

    
464
  /* Debug Template
465
   *
466
   * @access public
467
   * @param array $_top Content Array
468
   * @desc Debug Template
469
   */
470
  function debug ( $_top = '' ) {
471
    /* Prepare Template Content */
472
    if (!$_top) {
473
      $_top  =  $this->data;
474
    }
475
    if (@include_once('class.quickskindebugger.php')) {
476
      $this->debugger = new QuickSkinDebugger($this->template_dir . $this->tpl_file, $this->right_delimiter, $this->left_delimiter);
477
      $this->debugger->start($_top);
478
    } else {
479
      exit( 'QuickSkin Error: Cannot find class.quickskindebugger.php; check QuickSkin installation');
480
    }
481
  }
482

    
483
  /* Start Ouput Content Buffering
484
   *
485
   * Usage Example:
486
   * $page = new QuickSkin('template.html');
487
   * $page->use_cache();
488
   * ...
489
   *
490
   * @access public
491
   * @desc Output Cache
492
   */
493
  function use_cache ( $key = '' ) {
494
    if (empty($_POST)) {
495
      $this->cache_filename  =  $this->cache_dir . 'cache_' . md5($_SERVER['REQUEST_URI'] . serialize($key)) . '.ser';
496
      if (($_SERVER['HTTP_CACHE_CONTROL'] != 'no-cache')  &&  ($_SERVER['HTTP_PRAGMA'] != 'no-cache')  &&  @is_file($this->cache_filename)) {
497
        if ((time() - filemtime($this->cache_filename)) < $this->cache_lifetime) {
498
          readfile($this->cache_filename);
499
          exit;
500
        }
501
      }
502
      ob_start( array( &$this, 'cache_callback' ) );
503
    }
504
  }
505

    
506
  /* Output Buffer Callback Function
507
   *
508
   * @access private
509
   * @param string $output
510
   * @return string $output
511
   */
512
  function cache_callback ( $output ) {
513
    if ($hd = @fopen($this->cache_filename, 'w')) {
514
      fwrite($hd,  $output);
515
      fclose($hd);
516
    } else {
517
      $output = 'QuickSkin Error: failed to open cache file "' . $this->cache_filename . '"';
518
    }
519
    return $output;
520
  }
521

    
522
  /* Determine Last Filechange Date (if File exists)
523
   *
524
   * @access private
525
   * @param string $filename
526
   * @return mixed
527
   * @desc Determine Last Filechange Date
528
   */
529
  function mtime ( $filename ) {
530
    if (@is_file($filename)) {
531
      $ret = filemtime($filename);
532
      return $ret;
533
    }
534
  }
535

    
536
  /* Set (or reset) Properties (variables)
537
   *
538
   * Usage Example:
539
   * $page->set('reuse_code', true);
540
   *
541
   * @access public
542
   * @param string $name Parameter Name
543
   * @param mixed $value Parameter Value
544
   * NOTE: will not work with arrays, there are no arrays to set/reset */
545
  function set ( $name, $value = '' ) {
546
    if ( isset($this->$name) ) {
547
      $this->$name = $value;
548
    } else {
549
      exit( 'QuickSkin Error: Attempt to set a non-existant class property: ' . $name);
550
    }
551
  }
552

    
553
}
554

    
555
/*~
556
.---------------------------------------------------------------------------.
557
|  Software: QuickSkinParser Class * Used by QuickSkin Class                |
558
|   Version: 5.0                                                            |
559
|   Contact: andy.prevost@worxteam.com,andy@codeworx.ca                     |
560
|      Info: http://quickskin.sourceforge.net                               |
561
|   Support: http://sourceforge.net/projects/quickskin/                     |
562
| ------------------------------------------------------------------------- |
563
|    Author: Andy Prevost andy.prevost@worxteam.com (admin)                 |
564
|    Author: Manuel 'EndelWar' Dalla Lana endelwar@aregar.it (former admin) |
565
|    Author: Philipp v. Criegern philipp@criegern.com (original founder)    |
566
| Copyright (c) 2002-2009, Andy Prevost. All Rights Reserved.               |
567
|    * NOTE: QuickSkin is the SmartTemplate project renamed. SmartTemplate  |
568
|            information and downloads can still be accessed at the         |
569
|            smarttemplate.sourceforge.net site                             |
570
| ------------------------------------------------------------------------- |
571
|   License: Distributed under the Lesser General Public License (LGPL)     |
572
|            http://www.gnu.org/copyleft/lesser.html                        |
573
| This program is distributed in the hope that it will be useful - WITHOUT  |
574
| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or     |
575
| FITNESS FOR A PARTICULAR PURPOSE.                                         |
576
| ------------------------------------------------------------------------- |
577
| We offer a number of paid services:                                       |
578
| - Web Hosting on highly optimized fast and secure servers                 |
579
| - Technology Consulting                                                   |
580
| - Oursourcing (highly qualified programmers and graphic designers)        |
581
'---------------------------------------------------------------------------'
582
Last modified: January 01 2009 ~*/
583

    
584
class QuickSkinParser {
585

    
586
  /////////////////////////////////////////////////
587
  // PROPERTIES, PUBLIC
588
  /////////////////////////////////////////////////
589

    
590
  public $error;               /* Error messages */
591
  public $template;            /* The template itself */
592
  public $tpl_file;            /* The template filename */
593
  public $template_dir;        /* The template filename used to extract the dirname for subtemplates */
594
  public $skins_sub_dir;       /* The template subdirectory, passed by main class */
595
  public $extensions_dir;      /* The extension directory */
596
  public $extension_tagged = array(); /* List of used QuickSkin Extensions */
597
  public $left_delimiter;      /* Default Left delimiter */
598
  public $right_delimiter;     /* Default Right delimiter */
599
  public $supp_templates = ''; /* Contains array or single supplementary template(s) */
600
  public $extension_prefix;    /* filename prefix on all the extensions files */
601

    
602
  /* QuickSkinParser Constructor */
603
  /*
604
  function __construct() {
605
  }
606
  */
607

    
608
  /* Replace template logical expression in IF..ENDIF (|| or &&) with php logical expression
609
   * @access private
610
   * @desc replace template logical expression (|| or &&) with php logical expression
611
   * @author Bruce Huang (msn: huang_x_c@163.com)
612
   * @param string $src_page source page intended to be replaced
613
   */
614
  function replace_logic_expression( &$src_page ) {
615
    /* cannot find '||' or '&&' */
616
    if(!strpos($src_page, '||') && !strpos($src_page, '&&')) {
617
      return;
618
    }
619
    /* match 'ELSE' and the last sub expression */
620
    if (preg_match_all('/<!-- (ELSE)?IF \s*(\(*).*[|&]{2}\s*\(*\s*([a-zA-Z0-9_.]+)\s*([!=<>]+)\s*(["]?[^"]*?["]?)\s*(\)*)\s* -->/', $src_page, $var)) {
621
      foreach ($var[3] as $cnt => $tag) {
622
        list($parent, $block)  =  $this->var_name($tag);
623
        $cmp   =  $var[4][$cnt];
624
        $val   =  $var[5][$cnt];
625
        $else  =  ($var[1][$cnt] == 'ELSE') ? '} else' : '';
626
        if ($cmp == '=') {
627
          $cmp  =  '==';
628
        }
629
        if (preg_match('/"([^"]*)"/',$val,$matches)) {
630
          $code_suffix  =  "\$$parent"."['$block'] $cmp \"".$matches[1].$var[6][$cnt]."\"){\n?>";
631
        } elseif (preg_match('/([^"]*)/',$val,$matches)) {
632
          list($parent_right, $block_right)  =  $this->var_name($matches[1]);
633
          $code_suffix  =  "\$$parent"."['$block'] $cmp \$$parent_right"."['$block_right']".$var[6][$cnt]."){\?>";
634
        }
635

    
636
        /* match other sub expressions */
637
        if (preg_match_all('/([a-zA-Z0-9_.]+)\s*([!=<>]+)\s*(["]?[^"]*?["]?)\s*(\)*\s*[|&]{2}\s*\(*)\s*/', $var[0][$cnt], $sub_var)) {
638
          $code_mid = '';
639
          foreach($sub_var[1] as $sub_cnt => $sub_tag) {
640
            list($sub_parent, $sub_block) = $this->var_name($sub_tag);
641
            $cmp = $sub_var[2][$sub_cnt];
642
            $val = $sub_var[3][$sub_cnt];
643
            $logic_exp  =  $sub_var[4][$sub_cnt];
644
            if ($cmp == '=') {
645
              $cmp  =  '==';
646
            }
647
            if (preg_match('/"([^"]*)"/',$val,$matches)) {
648
              $code_mid  =  $code_mid."\$$sub_parent"."['$sub_block'] $cmp \"".$matches[1]."\"".$logic_exp;
649
            } elseif (preg_match('/([^"]*)/',$val,$matches)) {
650
              list($sub_parent_right, $sub_block_right)  =  $this->var_name($matches[1]);
651
              $code_mid  =  $code_mid."\$$sub_parent"."['$sub_block'] $cmp \$$sub_parent_right"."['$sub_block_right']".$logic_exp;
652
            }
653
          }
654
        }
655
        $code = "<?php\n".$else.'if ('.$var[2][$cnt].$code_mid.$code_suffix;
656
        $src_page = str_replace($var[0][$cnt],  $code,  $src_page);
657
      }
658
    }
659
  }
660

    
661
  /* Main Template Parser
662
   * @param string $compiled_template_filename Compiled Template Filename
663
   * @desc Creates Compiled PHP Template
664
   */
665
  function compile( $compiled_template_filename = '', $data='', $supp_templates='', $extensions_dir='' ) {
666

    
667
    $this->extension_prefix = preg_quote($this->extension_prefix);
668

    
669
    /* Load Template */
670
    $template_filename = $this->template_dir . $this->tpl_file;
671
    if ($hd = @fopen($template_filename, 'r')) {
672
      if (filesize($template_filename)) {
673
        $this->template = fread($hd, filesize($template_filename));
674
        $this->left_delimiter = preg_quote($this->left_delimiter);
675
        $this->right_delimiter = preg_quote($this->right_delimiter);
676
      } else {
677
        $this->template = 'QuickSkin Parser Error: File size is zero byte: ' .$template_filename;
678
      }
679
      fclose($hd);
680
    } else {
681
      $this->template = 'QuickSkin Parser Error: File not found: ' .$template_filename;
682
    }
683

    
684
    if (empty($this->template)) {
685
      return;
686
    }
687

    
688
    /* Do the variable substitution for paths, urls, subtemplates */
689
    $this->template = $this->worx_var_swap($this->template, $data, $supp_templates);
690

    
691
    $header = '';
692

    
693
    /* Code to allow subtemplates */
694
    if(eregi("<!-- INCLUDE", $this->template)) {
695
      while ($this->count_subtemplates() > 0) {
696
        preg_match_all('/<!-- INCLUDE ([a-zA-Z0-9\-_.]+) -->/', $this->template, $tvar);
697
        foreach($tvar[1] as $subfile) {
698
          if(file_exists($this->template_dir . '/' . $this->skins_sub_dir . '/' .$subfile)) {
699
            $subst = implode('',file($this->template_dir . '/' . $this->skins_sub_dir . '/' .$subfile));
700
          } else {
701
            $subst = 'QuickSkin Parser Error: Subtemplate not found: \''.$subfile.'\'';
702
          }
703
          $this->template = str_replace("<!-- INCLUDE $subfile -->", $subst, $this->template);
704
        }
705
      }
706
    }
707
    /* END, ELSE Blocks */
708
    $page  =  preg_replace("/<!-- ENDIF.+?-->/", "<?php\n}\n?>", $this->template);
709
    $page  =  preg_replace("/<!-- END[ a-zA-Z0-9_.]* -->/",  "<?php\n}\n\$_obj=\$_stack[--\$_stack_cnt];}\n?>", $page);
710
    $page  =  preg_replace("/<!-- ENDLOOP[ a-zA-Z0-9_.]* -->/",  "<?php\n}\n\$_obj=\$_stack[--\$_stack_cnt];}\n?>", $page);
711
    $page  =  str_replace("<!-- ELSE -->", "<?php\n} else {\n?>", $page);
712

    
713
    /* 'BEGIN - END' Blocks */
714
    if (preg_match_all('/<!-- LOOP ([a-zA-Z0-9_.]+) -->/', $page, $var)) {
715
      foreach ($var[1] as $tag) {
716
        list($parent, $block)  =  $this->var_name($tag);
717
        $code  =  "<?php\n"
718
            . "if (!empty(\$$parent"."['$block'])){\n"
719
            . "if (!is_array(\$$parent"."['$block']))\n"
720
            . "\$$parent"."['$block']=array(array('$block'=>\$$parent"."['$block']));\n"
721
            . "\$_stack[\$_stack_cnt++]=\$_obj;\n"
722
            . "\$rowcounter = 0;\n"
723
            . "foreach (\$$parent"."['$block'] as \$rowcnt=>\$$block) {\n"
724
              . "\$$block"."['ROWCNT']=(\$rowcounter);\n"
725
              . "\$$block"."['ALTROW']=\$rowcounter%2;\n"
726
              . "\$$block"."['ROWBIT']=\$rowcounter%2;\n"
727
              . "\$rowcounter++;"
728
              . "\$_obj=&\$$block;\n?>";
729
        $page  =  str_replace("<!-- LOOP $tag -->",  $code,  $page);
730
      }
731
    }
732

    
733
    /* replace logical operator in [ELSE]IF */
734
    $this->replace_logic_expression($page);
735

    
736
    /* 'IF nnn=mmm' Blocks */
737
    if (preg_match_all('/<!-- (ELSE)?IF ([a-zA-Z0-9_.]+)\s*([!=<>]+)\s*(["]?[^"]*["]?) -->/', $page, $var)) {
738
      foreach ($var[2] as $cnt => $tag) {
739
        list($parent, $block)  =  $this->var_name($tag);
740
        $cmp   =  $var[3][$cnt];
741
        $val   =  $var[4][$cnt];
742
        $else  =  ($var[1][$cnt] == 'ELSE') ? '} else' : '';
743
        if ($cmp == '=') {
744
          $cmp  =  '==';
745
        }
746

    
747
        if (preg_match('/"([^"]*)"/',$val,$matches)) {
748
          $code  =  "<?php\n$else"."if (\$$parent"."['$block'] $cmp \"".$matches[1]."\"){\n?>";
749
        } elseif (preg_match('/([^"]*)/',$val,$matches)) {
750
          list($parent_right, $block_right)  =  $this->var_name($matches[1]);
751
          $code  =  "<?php\n$else"."if (\$$parent"."['$block'] $cmp \$$parent_right"."['$block_right']){\n?>";
752
        }
753

    
754
        $page  =  str_replace($var[0][$cnt],  $code,  $page);
755
      }
756
    }
757

    
758
    /* 'IF nnn' Blocks */
759
    if (preg_match_all('/<!-- (ELSE)?IF ([a-zA-Z0-9_.]+) -->/', $page, $var)) {
760
      foreach ($var[2] as $cnt => $tag) {
761
        $else  =  ($var[1][$cnt] == 'ELSE') ? '} else' : '';
762
        list($parent, $block)  =  $this->var_name($tag);
763
        $code  =  "<?php\n$else"."if (!empty(\$$parent"."['$block'])){\n?>";
764
        $page  =  str_replace($var[0][$cnt],  $code,  $page);
765
      }
766
    }
767

    
768
    /* 'IF {extension:variable}'=mmm Blocks
769
     * e.g.
770
     * <!-- IF {count:list} > 0 -->
771
     * List populated
772
     * <!-- ELSE -->
773
     * List is empty
774
     * <!-- ENDIF -->
775
     * thanks to Khary Sharpe (ksharpe [at] kharysharpe [dot] com) for the initial code
776
     */
777
    if (preg_match_all('/<!-- (ELSE)?IF {([a-zA-Z0-9_]+):([^}]*)}\s*([!=<>]+)\s*(["]?[^"]*["]?) -->/', $page, $var)) {
778
      foreach ($var[2] as $cnt => $tag) {
779
        list($parent, $block)  =  $this->var_name($tag);
780
        $cmp   =  $var[4][$cnt];
781
        $val   =  $var[5][$cnt];
782
        $else  =  ($var[1][$cnt] == 'ELSE') ? '} else' : '';
783
        if ($cmp == '=') {
784
          $cmp  =  '==';
785
        }
786

    
787
        $extension = $var[2][$cnt];
788
        $extension_var = $var[3][$cnt];
789
        if (!isset($this->extension_tagged[$extension])) {
790
          $header .= 'include_once  "'.$this->extensions_dir."/".$this->extension_prefix."$extension.php\";\n";
791
          $this->extension_tagged[$extension] = true;
792
        }
793
        if (!strlen($extension_var)) {
794
          $code = "<?php\n$else"."if (".$this->extension_prefix."$extension() $cmp $val) {\n?>\n";
795
        } elseif (substr($extension_var, 0, 1) == '"') {
796
          $code = "<?php\n$else"."if (".$this->extension_prefix."$extension($extension_var) $cmp $val) {\n?>\n";
797
        } elseif (strpos($extension_var, ',')) {
798
          list($tag, $addparam) = explode(',', $extension_var, 2);
799
          list($block, $skalar) = $this->var_name($extension_var);
800
          if (preg_match('/^([a-zA-Z_]+)/', $addparam, $match)) {
801
            $nexttag = $match[1];
802
            list($nextblock, $nextskalar) = $this->var_name($nexttag);
803
            $addparam = substr($addparam, strlen($nexttag));
804
            $code = "<?php\n$else"."if (".$this->extension_prefix."$extension(\$$block"."['$skalar'],\$$nextblock"."['$nextskalar']"."$addparam) $cmp $val) {\n?>\n";
805
          } else {
806
            $code = "<?php\n$else"."if (".$this->extension_prefix."$extension(\$$block"."['$skalar'],$addparam) $cmp $val) {\n?>\n";
807
          }
808
        } else {
809
          list($block, $skalar) = $this->var_name($extension_var);
810
          $code = "<?php\n$else"."if (".$this->extension_prefix."$extension(\$$block"."['$skalar']) $cmp $val) {\n?>\n";
811
        }
812
        $page = str_replace($var[0][$cnt], $code, $page);
813
      }
814
    }
815

    
816
    /* Replace Scalars */
817
    if (preg_match_all('/'.$this->left_delimiter.'([a-zA-Z0-9_. >]+)'.$this->right_delimiter.'/', $page, $var)) {
818
      foreach ($var[1] as $fulltag) {
819
        /* Determine Command (echo / $obj[n]=) */
820
        list($cmd, $tag)  =  $this->cmd_name($fulltag);
821
        list($block, $skalar)  =  $this->var_name($tag);
822
        $code  =  "<?php\n$cmd \$$block"."['$skalar'];\n?>\n";
823
        $page  =  str_replace(stripslashes($this->left_delimiter).$fulltag.stripslashes($this->right_delimiter), $code, $page);
824
      }
825
    }
826

    
827
    /* Replace Translations */
828
    if (preg_match_all('/<"([a-zA-Z0-9_.]+)">/', $page, $var)) {
829
      foreach ($var[1] as $tag) {
830
        list($block, $skalar)  =  $this->var_name($tag);
831
        $code  =  "<?php\necho gettext('$skalar');\n?>\n";
832
        $page  =  str_replace('<"'.$tag.'">',  $code,  $page);
833
      }
834
    }
835

    
836
    /* Include Extensions */
837
    if (preg_match_all('/'.$this->left_delimiter.'([a-zA-Z0-9_]+):([^}]*)'.$this->right_delimiter.'/', $page, $var)) {
838
      foreach ($var[2] as $cnt => $tag) {
839
        /* Determine Command (echo / $obj[n]=) */
840
        list($cmd, $tag)  =  $this->cmd_name($tag);
841

    
842
        $extension  =  $var[1][$cnt];
843
        if (!isset($this->extension_tagged[$extension])) {
844
          $header .= 'include_once "'.$this->extensions_dir."/".$this->extension_prefix."$extension.php\";\n";
845
          $this->extension_tagged[$extension]  =  true;
846
        }
847
        if (!strlen($tag)) {
848
          $code  =  "<?php\n$cmd ".$this->extension_prefix."$extension();\n?>\n";
849
        } elseif (substr($tag, 0, 1) == '"') {
850
          $code  =  "<?php\n$cmd ".$this->extension_prefix."$extension($tag);\n?>\n";
851
        } elseif (strpos($tag, ',')) {
852
          list($tag, $addparam)  =  explode(',', $tag, 2);
853
          list($block, $skalar)  =  $this->var_name($tag);
854
          if (preg_match('/^([a-zA-Z_]+)/', $addparam, $match)) {
855
            $nexttag   =  $match[1];
856
            list($nextblock, $nextskalar)  =  $this->var_name($nexttag);
857
            $addparam  =  substr($addparam, strlen($nexttag));
858
            $code  =  "<?php\n$cmd ".$this->extension_prefix."$extension(\$$block"."['$skalar'],\$$nextblock"."['$nextskalar']"."$addparam);\n?>\n";
859
          } else {
860
            $code  =  "<?php\n$cmd ".$this->extension_prefix."$extension(\$$block"."['$skalar'],$addparam);\n?>\n";
861
          }
862
        } else {
863
          list($block, $skalar) = $this->var_name($tag);
864
          $code  =  "<?php\n$cmd ".$this->extension_prefix."$extension(\$$block"."['$skalar']);\n?>\n";
865
        }
866
        $page  =  str_replace($var[0][$cnt],  $code,  $page);
867
      }
868
    }
869

    
870
    /* Add Include Header */
871
    if (isset($header) && !empty($header)) {
872
      $page  =  "<?php\n$header\n?>$page";
873
    }
874

    
875
    /* do substitutions on included supplementary templates */
876
    $page = $this->worx_tpl_swap($page, $data, $supp_templates);
877

    
878
    /* Store Code to Temp Dir */
879
    if (strlen($compiled_template_filename)) {
880
      if ($hd  =  fopen($compiled_template_filename,  'w')) {
881
        fwrite($hd,  $page);
882
        fclose($hd);
883
        return true;
884
      } else {
885
        $this->error  =  'Could not write compiled file.';
886
        return false;
887
      }
888
    } else {
889
      return $page;
890
    }
891
  }
892

    
893
  /* Splits Template-Style Variable Names into an Array-Name/Key-Name Components
894
   * {example}               :  array( "_obj",                   "example" )  ->  $_obj['example']
895
   * {example.value}         :  array( "_obj['example']",        "value" )    ->  $_obj['example']['value']
896
   * {example.0.value}       :  array( "_obj['example'][0]",     "value" )    ->  $_obj['example'][0]['value']
897
   * {top.example}           :  array( "_stack[0]",              "example" )  ->  $_stack[0]['example']
898
   * {parent.example}        :  array( "_stack[$_stack_cnt-1]",  "example" )  ->  $_stack[$_stack_cnt-1]['example']
899
   * {parent.parent.example} :  array( "_stack[$_stack_cnt-2]",  "example" )  ->  $_stack[$_stack_cnt-2]['example']
900
   * @param string $tag Variale Name used in Template
901
   * @return array  Array Name, Key Name
902
   * @access private
903
   * @desc Splits Template-Style Variable Names into an Array-Name/Key-Name Components
904
   */
905
  function var_name($tag) {
906
    $parent_level  =  0;
907
    while (substr($tag, 0, 7) == 'parent.') {
908
      $tag  =  substr($tag, 7);
909
      $parent_level++;
910
    }
911
    if (substr($tag, 0, 4) == 'top.') {
912
      $obj  =  '_stack[0]';
913
      $tag  =  substr($tag,4);
914
    } elseif ($parent_level) {
915
      $obj  =  '_stack[$_stack_cnt-'.$parent_level.']';
916
    } else {
917
      $obj  =  '_obj';
918
    }
919
    while (is_int(strpos($tag, '.'))) {
920
      list($parent, $tag)  =  explode('.', $tag, 2);
921
      if (is_numeric($parent)) {
922
        $obj  .=  "[" . $parent . "]";
923
      } else {
924
        $obj  .=  "['" . $parent . "']";
925
      }
926
    }
927
    $ret = array($obj, $tag);
928
    return $ret;
929
  }
930

    
931
  /* Determine Template Command from Variable Name
932
   * {variable}             :  array( "echo",              "variable" )  ->  echo $_obj['variable']
933
   * {variable > new_name}  :  array( "_obj['new_name']=", "variable" )  ->  $_obj['new_name']= $_obj['variable']
934
   * @param string $tag Variale Name used in Template
935
   * @return array  Array Command, Variable
936
   * @access private
937
   * @desc Determine Template Command from Variable Name
938
   */
939
  function cmd_name($tag) {
940
    if (preg_match('/^(.+) > ([a-zA-Z0-9_.]+)$/', $tag, $tagvar)) {
941
      $tag  =  $tagvar[1];
942
      list($newblock, $newskalar)  =  $this->var_name($tagvar[2]);
943
      $cmd  =  "\$$newblock"."['$newskalar']=";
944
    } else {
945
      $cmd  =  'echo';
946
    }
947
    $ret = array($cmd, $tag);
948
    return $ret;
949
  }
950

    
951
  /* @return int Number of subtemplate included
952
   * @access private
953
   * @desc Count number of subtemplates included in current template
954
   */
955
  function count_subtemplates() {
956
    $ret = preg_match_all('/<!-- INCLUDE ([a-zA-Z0-9_.]+) -->/', $this->template, $tvar);
957
    unset($tvar);
958
    return $ret;
959
  }
960

    
961
  function worx_var_swap($tpldata, $data, $supp_templates) { /* do the substitution of the variables here */
962

    
963
    /* replace all the template elements (sub templates) */
964
    if ( is_array($supp_templates) && !empty($supp_templates) ) {
965
      foreach ($supp_templates as $key => $val) {
966
        $tpldata = str_replace("\{$key}", $val, $tpldata);
967
      }
968
    }
969
    /* do the substitution of the directory names here */
970

    
971
    return $tpldata;
972

    
973
  }
974

    
975
  function worx_tpl_swap($tpldata, $data, $supp_templates) { // do the substitution of the sub templates here 
976

    
977
	// do the substitution of the directory names here
978
	/*
979
    // do image link substitution 
980
    if ( $data['tpl_img'] != '' && $data['url_img'] != '' ) {
981
      $tpldata = str_replace($data['tpl_img'],$data['url_img'],$tpldata);
982
      unset($data['tpl_img']);
983
      unset($data['url_img']);
984
    } elseif (defined(_URL_USRIMG)) {
985
      $tpldata = str_replace('tplimgs/',_URL_USRIMG,$tpldata);
986
    }
987

    
988
    // do javascript link substitution
989
    if ( $data['tpl_js'] != '' && $data['url_js'] != '' ) {
990
      $tpldata = str_replace($data['tpl_js'],$data['url_js'],$tpldata);
991
      unset($data['img_tpl']);
992
      unset($data['url_js']);
993
    } elseif (defined(_URL_USRJS)) {
994
      $tpldata = str_replace('tpljs/',_URL_USRJS,$tpldata);
995
    }
996

    
997
    // do css link substitution
998
    if ( $data['tpl_css'] != '' && $data['url_css'] != '' ) {
999
      $tpldata = str_replace($data['tpl_css'],$data['url_css'],$tpldata);
1000
      unset($data['tpl_css']);
1001
      unset($data['url_css']);
1002
    } elseif (defined(_URL_USRCSS)) {
1003
      $tpldata = str_replace('url_css/',_URL_USRCSS,$tpldata);
1004
    }
1005
	*/
1006
    return $tpldata;
1007
	
1008
  }
1009

    
1010
}
1011

    
1012
?>
(1-1/3)