PageRenderTime 1207ms CodeModel.GetById 182ms app.highlight 769ms RepoModel.GetById 176ms app.codeStats 1ms

/simplate.cpp

https://github.com/yunoka/simplate
C++ | 2330 lines | 1710 code | 215 blank | 405 comment | 336 complexity | e64a10aa48784859a9f0bfcfa983347e MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2  +----------------------------------------------------------------------+
   3  | PHP Version 4/5                                                      |
   4  +----------------------------------------------------------------------+
   5  | Copyright (c) Kazuhiro IIzuka All rights reserved.                   |
   6  +----------------------------------------------------------------------+
   7  | This source file is the BSD License,                                 |
   8  | that is bundled with this package in the file LICENSE, and is        |
   9  | available through the world-wide-web at the following url:           |
  10  | http://static.aimy.jp/license.txt                                    |
  11  +----------------------------------------------------------------------+
  12  | Author: Kazuhiro IIzuka                                              |
  13  +----------------------------------------------------------------------+
  14*/
  15
  16/* $Id$ */
  17
  18/**
  19 * @file simplate.cpp
  20 * @brief Simplate class implementation
  21 * @author Kazuhiro IIzuka
  22 * @author Shimizu
  23 * @version $Id$
  24 * Copyright (C) Kazuhiro IIzuka
  25 */
  26
  27#ifdef HAVE_CONFIG_H
  28#include "config.h"
  29#endif
  30#ifdef PHP_WIN32
  31#include "config.w32.h"
  32#endif // PHP_WIN32
  33
  34// C++ header...
  35#include <iostream>
  36#include <ctime>
  37#include <string>
  38#include <vector>
  39#include <set>
  40using std::cout;
  41using std::cerr;
  42using std::endl;
  43using std::string;
  44using std::vector;
  45using std::set;
  46
  47extern "C" {
  48#include "php.h"
  49#include "php_ini.h"
  50#include "ext/standard/info.h"
  51#include "ext/standard/php_var.h" // var_dump
  52#ifdef PHP_WIN32
  53#include "win32/flock.h"
  54#else
  55#include "ext/standard/flock_compat.h"
  56#endif // PHP_WIN32
  57}
  58#include "php_simplate.h"
  59
  60//Debug
  61#ifdef SIMPLATE_DEBUG
  62FILE *fp;
  63char msg[1024];
  64#ifdef PHP_WIN32
  65#define DEBUG_PRINTF(format, ...) \
  66    sprintf(msg, format, __VA_ARGS__); \
  67    fp = fopen("debug.log", "a"); \
  68    if (NULL != fp) fprintf(fp, "%s(%d): %s\n", __FUNCTION__, __LINE__, msg); \
  69    fclose(fp);
  70#else
  71#define DEBUG_PRINTF(format, ...) \
  72    sprintf(msg, format, __VA_ARGS__); \
  73    fp = fopen("debug.log", "a"); \
  74    if (NULL != fp) fprintf(fp, "%s(%d): %s\n", __func__, __LINE__, msg); \
  75    fclose(fp);
  76#endif // PHP_WIN32
  77#else
  78#define DEBUG_PRINTF(format, ...)
  79#endif // SIMPLATE_DEBUG
  80
  81//#define PHP5.3 or more
  82#ifndef Z_REFCOUNT_P
  83
  84#define Z_REFCOUNT_PP(ppz)              ((*(ppz))->refcount)
  85#define Z_SET_REFCOUNT_PP(ppz, rc)      ((*(ppz))->refcount = rc)
  86#define Z_ADDREF_PP(ppz)                (++(*(ppz))->refcount)
  87#define Z_DELREF_PP(ppz)                (--(*(ppz))->refcount)
  88#define Z_ISREF_PP(ppz)                 ((*(ppz))->is_ref)
  89#define Z_SET_ISREF_PP(ppz)             ((*(ppz))->is_ref = 1)
  90#define Z_UNSET_ISREF_PP(ppz)           ((*(ppz))->is_ref = 0)
  91#define Z_SET_ISREF_TO_PP(ppz, isref)   ((*(ppz))->is_ref = isref)
  92
  93#define Z_REFCOUNT_P(pz)                ((pz)->refcount)
  94#define Z_SET_REFCOUNT_P(z, rc)         ((pz)->refcount = rc)
  95#define Z_ADDREF_P(pz)                  (++(pz)->refcount)
  96#define Z_DELREF_P(pz)                  (--(pz)->refcount)
  97#define Z_ISREF_P(pz)                   ((pz)->is_ref)
  98#define Z_SET_ISREF_P(pz)               ((pz)->is_ref = 1)
  99#define Z_UNSET_ISREF_P(pz)             ((pz)->is_ref = 0)
 100#define Z_SET_ISREF_TO_P(z, isref)      ((pz)->is_ref = isref)
 101
 102#define Z_REFCOUNT(z)                   ((z).refcount)
 103#define Z_SET_REFCOUNT(z, rc)           ((z).refcount = rc)
 104#define Z_ADDREF(z)                     (++(z).refcount)
 105#define Z_DELREF(z)                     (--(z).refcount)
 106#define Z_ISREF(z)                      ((z).is_ref)
 107#define Z_SET_ISREF(z)                  ((z).is_ref = 1)
 108#define Z_UNSET_ISREF(z)                ((z).is_ref = 0)
 109#define Z_SET_ISREF_TO(z, isref)        ((z).is_ref = isref)
 110
 111#endif
 112
 113//#define USE_ZEND_EXECUTE
 114#define SET_ZVAL_STRING(z, s) \
 115    INIT_PZVAL(&z); \
 116    Z_STRVAL(z) = estrndup(s, strlen(s)); \
 117    Z_STRLEN(z) = strlen(s); \
 118    Z_TYPE(z) = IS_STRING;
 119
 120/* If you declare any globals in php_simplate.h uncomment this: */
 121ZEND_DECLARE_MODULE_GLOBALS(simplate)
 122
 123/* True global resources - no need for thread safety here */
 124static int le_simplate;
 125
 126// default parameters
 127#define DEFAULT_TEMPLATE_DIR "template"
 128#define DEFAULT_COMPILE_DIR "template_c"
 129#define DEFAULT_CACHE_DIR "cache"
 130#define DEFAULT_LEFT_DELIMITER "<{"
 131#define DEFAULT_RIGHT_DELIMITER "}>"
 132#define DEFAULT_COMPILE_CHECK true
 133#define DEFAULT_FORCE_COMPILE false
 134#define DEFAULT_LAZY_CHECK false
 135#define DEFAULT_CACHE_LIFETIME 3600
 136#define DEFAULT_CACHING 0 // 0: no caching
 137                          // 1: use class cache_lifetime value
 138                          // 2: use cache_lifetime in cache file
 139
 140// fetch mode
 141#define SIMPLATE_FETCH 0
 142#define SIMPLATE_DISPLAY 1
 143
 144// parameter names
 145#define TEMPLATE_DIR "template_dir"
 146#define COMPILE_DIR "compile_dir"
 147#define CACHE_DIR "cache_dir"
 148#define LEFT_DELIMITER "left_delimiter"
 149#define RIGHT_DELIMITER "right_delimiter"
 150#define COMPILE_CHECK "compile_check"
 151#define FORCE_COMPILE "force_compile"
 152#define LAZY_CHECK "lazy_check"
 153#define VERSION "0.4.2.y_1"
 154#define CACHE_LIFETIME "cache_lifetime"
 155#define CACHING "caching"
 156
 157// class entry pointer
 158static zend_class_entry *simplate_entry_ptr;
 159static zend_function_entry php_simplate_functions[] = {
 160#ifdef ZEND_ENGINE_2
 161  ZEND_ME(simplate, __construct, NULL, ZEND_ACC_PUBLIC)
 162  ZEND_ME(simplate, assign, NULL, ZEND_ACC_PUBLIC)
 163  ZEND_ME(simplate, fetch, NULL, ZEND_ACC_PUBLIC)
 164  ZEND_ME(simplate, display, NULL, ZEND_ACC_PUBLIC)
 165  ZEND_ME(simplate, clear_cache, NULL, ZEND_ACC_PUBLIC)
 166  ZEND_ME(simplate, register_prefilter, NULL, ZEND_ACC_PUBLIC)
 167  ZEND_ME(simplate, register_postfilter, NULL, ZEND_ACC_PUBLIC)
 168#else
 169  PHP_FALIAS(simplate, simplate_init, NULL)
 170  PHP_FALIAS(assign, simplate_assign, NULL)
 171  PHP_FALIAS(fetch, simplate_fetch, NULL)
 172  PHP_FALIAS(display, simplate_display, NULL)
 173  PHP_FALIAS(clear_cache, simplate_clear_cache, NULL)
 174  PHP_FALIAS(register_prefilter, simplate_register_prefilter, NULL)
 175  PHP_FALIAS(register_postfilter, simplate_register_postfilter, NULL)
 176#endif // ZEND_ENGINE_2
 177  {NULL, NULL, NULL}
 178};
 179
 180/* {{{ simplate_functions[]
 181 *
 182 * Every user visible function must have an entry in simplate_functions[].
 183 */
 184function_entry simplate_functions[] = {
 185    {NULL, NULL, NULL} /* Must be the last line in simplate_functions[] */
 186};
 187/* }}} */
 188
 189/* {{{ simplate_module_entry
 190 */
 191zend_module_entry simplate_module_entry = {
 192#if ZEND_MODULE_API_NO >= 20010901
 193    STANDARD_MODULE_HEADER,
 194#endif
 195    "simplate",
 196    simplate_functions,
 197    PHP_MINIT(simplate),
 198    PHP_MSHUTDOWN(simplate),
 199    PHP_RINIT(simplate), /* Replace with NULL if there's nothing to do at request start */
 200    PHP_RSHUTDOWN(simplate), /* Replace with NULL if there's nothing to do at request end */
 201    PHP_MINFO(simplate),
 202#if ZEND_MODULE_API_NO >= 20010901
 203    VERSION,
 204#endif
 205    STANDARD_MODULE_PROPERTIES
 206};
 207/* }}} */
 208
 209#ifdef COMPILE_DL_SIMPLATE
 210BEGIN_EXTERN_C()
 211ZEND_GET_MODULE(simplate)
 212END_EXTERN_C()
 213#endif
 214
 215/* {{{ PHP_INI
 216 */
 217/* Remove comments and fill if you need to have entries in php.ini
 218PHP_INI_BEGIN()
 219    STD_PHP_INI_ENTRY("simplate.global_value",      "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_simplate_globals, simplate_globals)
 220    STD_PHP_INI_ENTRY("simplate.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_simplate_globals, simplate_globals)
 221PHP_INI_END()
 222*/
 223/* }}} */
 224
 225/* {{{ php_simplate_init_globals
 226 */
 227/* Uncomment this function if you have INI entries */
 228static void php_simplate_init_globals(
 229    zend_simplate_globals *simplate_globals TSRMLS_DC
 230)
 231{
 232    simplate_globals->global_string;
 233}
 234/* }}} */
 235
 236#if 0
 237// create custom objects
 238typedef struct{
 239    string fetch_buffer;
 240    zend_object zo;
 241}simplate_object;
 242
 243static void simplate_object_dtor(void *object, zend_object_handle handle TSRMLS_DC)
 244{
 245    zend_objects_destroy_object(object, handle TSRMLS_CC);
 246}
 247static void simplate_objects_clone(void *object, void **object_clone TSRMLS_DC)
 248{
 249    simplate_object *intern = (simplate_object*)object;
 250    simplate_object **intern_clone = (simplate_object**)object_clone;
 251
 252    *intern_clone = emalloc(sizeof(simplate_object));
 253    (*intern_object)->zo.ce = intern->zo.ce;
 254    (*intern_object)->zo.in_get = 0;
 255    (*intern_object)->zo.in_set = 0;
 256    ALLOC_HASHTABLE((*intern_clone)->zo.properties);
 257    (*intern_clone)->fetch_buffer=
 258
 259}
 260#endif // 0
 261
 262/* {{{ Returns full path of the specified filename.
 263 *
 264 * ex)
 265 *   filename = "foo.tpl"
 266 *     => template_dir + DEFAULT_SLASH + "foo.tpl"
 267 *   filename = "$variable"
 268 *     => template_dir + DEFAULT_SLASH + $variable
 269 *   filename = "$variable.tpl"
 270 *     => template_dir + DEFAULT_SLASH + $variable ".tpl"
 271 *   filename = "/path/to/foo.tpl"
 272 *     => "/path/to/foo.tpl"
 273 *
 274 * @param zval   *obj         instance of this class
 275 * @param string template_dir template directory
 276 * @param string filename     filename
 277 *
 278 * @return string full path of the specified filename
 279 */
 280static string get_include_filename(
 281    zval   *obj,
 282    string template_dir,
 283    string filename TSRMLS_DC
 284)
 285{
 286    string included_filename; // return value
 287
 288    // search "$" (use variable or don't use variable)
 289    size_t pos = filename.find("$");
 290    if (pos != string::npos) {
 291        // use variable
 292        string include_variable = "";
 293
 294        // From "$" to "." or "/" string is cut out.
 295        //   $variable => $variable
 296        //   $variable.tpl => $variable
 297        include_variable = filename.substr(pos);
 298        size_t dot_pos  = include_variable.find(".");
 299        size_t dot_pos2 = include_variable.find("/");
 300        if (dot_pos > dot_pos2 || (dot_pos == string::npos && dot_pos2 != string::npos )){
 301        	dot_pos = dot_pos2;
 302        }
 303        if (dot_pos != string::npos) {
 304            include_variable = include_variable.substr(0, dot_pos);
 305        }
 306
 307DEBUG_PRINTF("[B2]include_variable = (%s)", const_cast<char*>(include_variable.c_str()));
 308
 309        // delete "$"
 310        //   $variable => variable
 311        include_variable.erase(0, 1);
 312
 313#ifdef ZEND_ENGINE_2
 314        zval *_tpl_vars = zend_read_property(Z_OBJCE_P(obj), obj, const_cast<char*>("_tpl_vars"), strlen("_tpl_vars"), 1 TSRMLS_CC);
 315#else
 316        zval *_tpl_vars;
 317        if (zend_hash_find(Z_OBJPROP_P(obj), const_cast<char*>("_tpl_vars"), sizeof("_tpl_vars"), (void**)&_tpl_vars) != SUCCESS) {
 318            zend_error(E_ERROR, "_tpl_vars not found from zend_hash.");
 319        }
 320#endif // ZEND_ENGINE_2
 321        zval **zinclude_file;
 322        string new_filename;
 323        if (zend_hash_find(Z_ARRVAL_P(_tpl_vars), const_cast<char*>(include_variable.c_str()), include_variable.length() + 1, (void**)&zinclude_file) == SUCCESS
 324            && Z_TYPE_PP(zinclude_file) == IS_STRING
 325        ) {
 326DEBUG_PRINTF("(%s) -> (%s)", const_cast<char*>(include_variable.c_str()), Z_STRVAL_PP(zinclude_file));
 327            new_filename = filename.replace(filename.find("$"), include_variable.length() + 1, Z_STRVAL_PP(zinclude_file));
 328        } else {
 329            zend_error(E_ERROR, "include file variable = ($%s) is not assigned.", include_variable.c_str());
 330        }
 331        if (new_filename[0] == DEFAULT_SLASH) {
 332            // use variable
 333            // $variable = "/path/to/file.tpl"
 334            included_filename = new_filename;
 335        } else {
 336            included_filename = template_dir + DEFAULT_SLASH + new_filename;
 337        }
 338    } else if (filename[0] == DEFAULT_SLASH) {
 339        // don't use variable
 340        // <{include file="/path/to/file.tpl"}>
 341        included_filename = filename;
 342    } else {
 343        // don't use variable
 344        // <{include file="file.tpl"}>
 345        included_filename = template_dir + DEFAULT_SLASH + filename;
 346    }
 347    return included_filename;
 348}
 349/* }}} */
 350
 351/* {{{ Read contents from the specified filename, and replaces xml start-end tags.
 352 *
 353 *   <?xml
 354 *     => <?php echo'<?xml'; ?>
 355 *   ?>
 356 *     => <?php echo'?>'; ?>
 357 *
 358 * @param const char *filename filename
 359 *
 360 * @return string contents
 361 */
 362static string _readfile(
 363    const char *filename TSRMLS_DC
 364)
 365{
 366    string line;
 367
 368    // check open_basedir restriction in php.ini
 369    if (php_check_open_basedir(filename TSRMLS_CC)) {
 370        return "";
 371    }
 372
 373#ifdef PHP_WIN32
 374    php_stream *strm;
 375    char *buffer = NULL;
 376    size_t buffer_length = 0;
 377
 378    strm = php_stream_open_wrapper(const_cast<char*>(filename), "rb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL);
 379    if (!strm) {
 380        zend_error(E_ERROR, "Cannot read such file or directory:%s", const_cast<char*>(filename));
 381        return "";
 382    }
 383
 384    buffer_length = php_stream_copy_to_mem(strm, &buffer, PHP_STREAM_COPY_ALL, 0);
 385    php_stream_close(strm);
 386    if (buffer_length < 0) {
 387        zend_error(E_ERROR, "Cannot read such file or directory:%s", const_cast<char*>(filename));
 388        efree(buffer);
 389        return "";
 390    }
 391    line = buffer;
 392    efree(buffer);
 393#else // PHP_WIN32
 394    FILE *fp;
 395    char buf[8192];
 396    fp = VCWD_FOPEN(filename, "rb");
 397    if (fp == NULL) {
 398        zend_error(E_ERROR, "Cannot read such file or directory:%s", const_cast<char*>(filename));
 399        return "";
 400    }
 401
 402    while (fgets(buf, sizeof(buf), fp) != 0) {
 403        line += buf;
 404    }
 405    fclose(fp);
 406#endif // PHP_WIN32
 407
 408    // replace xml tag
 409    //   <?xml => <?php echo'<?xml'; ?>
 410    //   ?>    => <?php echo'?>'; ?>
 411    const char *xml_tag = "<?xml";
 412    size_t pos = 0;
 413    string new_tag;
 414    while ((pos = line.find(xml_tag, pos)) != string::npos) {
 415        new_tag = "<?php echo \'";
 416        new_tag += xml_tag;
 417        new_tag += "\'; ?>";
 418        line.replace(pos, strlen(xml_tag), new_tag);
 419        pos += new_tag.length();
 420
 421        new_tag = "<?php echo \'?>\'; ?>";
 422        pos = line.find("?>", pos);
 423        line.replace(pos, 2, new_tag);
 424        pos += new_tag.length();
 425    }
 426
 427    return line;
 428}
 429/* }}} */
 430
 431/* {{{ Trims the specified string.
 432 *
 433 * @param const char *s string
 434 *
 435 * @return string trim string
 436 */
 437static string trim(
 438    const char *s
 439)
 440{
 441    string new_str(s);
 442    // trim from left-side
 443    while (new_str[0] == ' ') {
 444        new_str.erase(0, 1);
 445    }
 446    // trim from right-side
 447    while (new_str[new_str.length() - 1] == ' ') {
 448        new_str.erase(new_str.length() - 1, 1);
 449    }
 450    return new_str;
 451}
 452/* }}} */
 453
 454/* {{{ Splits the specified string with the separator. The results is stored in the specified vector.
 455 *
 456 * @param const char     *s        string
 457 * @param vector<string> &e        vector
 458 * @param char           separator separator (= ".")
 459 *
 460 * @return void
 461 */
 462static void split_element(
 463    const char     *s,
 464    vector<string> &e,
 465    char           separator = '.'
 466)
 467{
 468    string str(s);
 469    size_t spos = 0, epos = 0;
 470    e.push_back(str.substr(spos, str.find(separator, spos)));
 471    while ((epos = str.find(separator, epos)) != string::npos) {
 472        e.push_back(str.substr(epos + 1, str.find(separator, epos + 1) - epos - 1));
 473        epos++;
 474    }
 475}
 476/* }}} */
 477
 478/* {{{ Looks for [a-z0-9_] consecutive string from i position of the specified variable. The result is stored in the specified new_variable.
 479 *
 480 * @param const char *variable
 481 * @param int        i
 482 * @param string     &new_variable
 483 *
 484 * @return int length of [a-z0-9_] consecutive string
 485 */
 486static int get_identifier(
 487    const char *variable,
 488    int        i,
 489    string     &new_variable
 490)
 491{
 492    int j = 0;
 493    while (isalnum(variable[i]) || variable[i] == '_') {
 494        new_variable += variable[i];
 495        i++;
 496        j++;
 497    }
 498    return j;
 499}
 500/* }}} */
 501
 502/* {{{ Parses the specified variable.
 503 *
 504 * array pattern
 505 *   $hoge[i] => _tpl_vars['hoge'][$i]
 506 *   $hoge[i].foo.bar => _tpl_vars['hoge'][$i]['foo']['bar']
 507 *   $hoge[i].huga[j].foo => _tpl_vars['hoge'][$i]['huga'][$j]['foo']
 508 *   $hoge[i]->huga[j]->foo => _tpl_vars['hoge'][$i]->huga[$j]->foo
 509 *
 510 * section pattern
 511 *   $simplate.section.i.index => $i
 512 *   $simplate.section.ary.count => count($this->_tpl_vars['ary'])
 513 *   $simplate.section.i.first => $this->_section['i']['first']
 514 *   $simplate.section.i.last => $this->_section['i']['last']
 515 *
 516 * arrow pattern
 517 *   $ary10.foo->bar => $this->_tpl_vars['ary10']['foo']->bar
 518 *   $foo->bar => $this->_tpl_vars['foo']->bar
 519 *
 520 * normal pattern
 521 *   $key => $this->_tpl_vars['key']
 522 *
 523 * @param const char *variable variable string
 524 *
 525 * @return string parsed variable string
 526 */
 527static string parse_variable(
 528    const char *variable
 529)
 530{
 531    string new_variable = "";
 532
 533DEBUG_PRINTF("variable = (%s)", variable);
 534
 535    if (variable[0] != '$' || strncmp(variable, "$this", 5) == 0) {
 536        new_variable = variable;
 537
 538DEBUG_PRINTF("new_variable = (%s)", const_cast<char*>(new_variable.c_str()));
 539
 540        return new_variable;
 541    }
 542
 543    if (strstr(variable, "[")) {
 544
 545DEBUG_PRINTF("%s", "array pattern");
 546
 547        // $hoge[i] => _tpl_vars['hoge'][$i]
 548        // $hoge[i].foo.bar => _tpl_vars['hoge'][$i]['foo']['bar']
 549        // $hoge[i].huga[j].foo => _tpl_vars['hoge'][$i]['huga'][$j]['foo']
 550        // $hoge[i]->huga[j]->foo => _tpl_vars['hoge'][$i]->huga[$j]->foo
 551
 552        int i = 0;
 553        while (variable[i]) {
 554            if (variable[i] == '$') {
 555                new_variable += "$this->_tpl_vars['";
 556                i++;
 557                i += get_identifier(variable, i, new_variable);
 558                new_variable += "']";
 559            } else if (variable[i] == '.') {
 560                new_variable += "['";
 561                i++;
 562                i += get_identifier(variable, i, new_variable);
 563                new_variable += "']";
 564            } else if (variable[i] == '[') {
 565                new_variable += "[$";
 566                i++;
 567                i += get_identifier(variable, i, new_variable);
 568                new_variable += "]";
 569                i++;
 570            } else if (i > 0 && variable[i] == '>' && variable[i - 1] == '-') {
 571                new_variable += "->";
 572                i++;
 573                i += get_identifier(variable, i, new_variable);
 574            } else {
 575                i++;
 576            }
 577        }
 578    } else if (strstr(variable, ".")) {
 579
 580DEBUG_PRINTF("%s", "section pattern");
 581
 582        vector<string> elements;
 583        split_element(variable + 1, elements);
 584
 585#ifdef SIMPLATE_DEBUG
 586for (vector<string>::iterator i = elements.begin(); i != elements.end(); ++i) {
 587DEBUG_PRINTF("vector(i) = (%s)", const_cast<char*>((*i).c_str()));
 588}
 589#endif // SIMPLATE_DEBUG
 590
 591        // $simplate.section.i.index => $i
 592        // $simplate.section.ary.count => count($this->_tpl_vars['ary'])
 593        // $simplate.section.i.first => $this->_section['i']['first']
 594        // $simplate.section.i.last => $this->_section['i']['last']
 595        // $ary10.foo->bar => $this->_tpl_vars["ary10"]["foo"]->bar
 596        if (elements.size() == 4 && elements[1] == "section" && elements[3] == "index") {
 597            new_variable += "$";
 598            new_variable += elements[2];
 599        } else if (elements.size() == 4 && elements[1] == "section" && elements[3] == "count") {
 600            new_variable += "count($this->_tpl_vars['";
 601            new_variable += elements[2];
 602            new_variable += "'])";
 603        } else if (elements.size() == 4 && elements[1] == "section" && (elements[3] == "first"|| elements[3] == "last")) {
 604            new_variable += "$this->_section['";
 605            new_variable += elements[2];
 606            new_variable += "']['";
 607            new_variable += elements[3];
 608            new_variable += "']";
 609        } else {
 610            new_variable += "$this->_tpl_vars";
 611            for (vector<string>::iterator i = elements.begin(); i != elements.end(); ++i) {
 612                new_variable += "['";
 613
 614                size_t pos = 0;
 615                if ((pos = (*i).find("->")) != string::npos) {
 616                    new_variable += (*i).substr(0, pos);
 617                    new_variable += "']";
 618                    new_variable += (*i).substr(pos);
 619                } else {
 620                    new_variable += *i;
 621                    new_variable += "']";
 622                }
 623            }
 624        }
 625    } else if (strstr(variable, "->")) {
 626
 627DEBUG_PRINTF("%s", "arrow pattern");
 628
 629        // $foo->bar => $this->_tpl_vars['foo']->bar
 630        string temp = variable;
 631        new_variable += "$this->_tpl_vars['";
 632        new_variable += temp.substr(1, temp.find("->") - 1);
 633        new_variable += "']";
 634        new_variable += temp.substr(temp.find("->"));
 635    } else {
 636
 637DEBUG_PRINTF("%s", "normal pattern");
 638
 639        // $key => $this->_tpl_vars['key']
 640        new_variable += "$this->_tpl_vars['";
 641        new_variable += (variable + 1);
 642        new_variable += "']";
 643    }
 644
 645DEBUG_PRINTF("new_variable = (%s)", const_cast<char*>(new_variable.c_str()));
 646
 647    return new_variable;
 648}
 649/* }}} */
 650
 651/* {{{ Returns the value of the simplate tag's attribute to which specified key is coinciding
 652 *
 653 * @param const char *s   simplate tag
 654 * @param const char *key attribute name
 655 *
 656 * @return string the value of simplate tag's attribute
 657 */
 658static string get_element(
 659    const char *s,
 660    const char *key
 661)
 662{
 663    int i;
 664    string value = "";
 665    string key_name;
 666
 667DEBUG_PRINTF("s = (%s), key = (%s)", s, key);
 668
 669    while ((s = strstr(s, key)) != NULL) {
 670        i = 0;
 671        key_name = "";
 672        while (isalnum(*(s + i))) {
 673            key_name += *(s + i);
 674            i++;
 675        }
 676        if (strcmp(key_name.c_str(), key) == 0) {
 677            while (*(s + i) != '=') {
 678                i++;
 679            }
 680            i++;
 681            while (*(s + i) == '\'' || *(s + i) == '"' || *(s + i) == ' ') {
 682                i++;
 683            }
 684            while (*(s + i) != '\'' && *(s + i) != '"' && *(s + i) != ' ' && *(s + i)) {
 685                value += *(s + i);
 686                i++;
 687            }
 688            break;
 689        }
 690        s++;
 691    }
 692
 693DEBUG_PRINTF("value = (%s)", const_cast<char*>(value.c_str()));
 694
 695    return value;
 696}
 697/* }}} */
 698
 699/* {{{ Returns true if the specified file has compiled.
 700 *
 701 * @param zval       *obj             instance of this
 702 * @param const char *template_dir    template directory
 703 * @param const char *compile_dir     compile direcotry
 704 * @param const char *filename        target file
 705 * @param const char *left_delimiter  left-delimiter
 706 * @param const char *right_delimiter right-delimiter
 707 *
 708 * @return boolean returns true if the specified file has compiled
 709 */
 710static inline bool _is_compiled(
 711    zval       *obj,
 712    const char *template_dir,
 713    const char *compile_dir,
 714    const char *filename,
 715    const char *left_delimiter,
 716    const char *right_delimiter TSRMLS_DC
 717)
 718{
 719    string error;
 720    unsigned char compile_check = DEFAULT_COMPILE_CHECK;
 721    unsigned char force_compile = 0;
 722    unsigned char lazy_check = 0;
 723
 724#ifdef ZEND_ENGINE_2
 725    zval *temp_zval;
 726
 727    // Reads property COMPILE_CHECK
 728    temp_zval = zend_read_property(Z_OBJCE_P(obj), obj, const_cast<char*>(COMPILE_CHECK), strlen(COMPILE_CHECK), 1 TSRMLS_CC);
 729    if (Z_TYPE_P(temp_zval) == IS_BOOL) {
 730        compile_check = Z_BVAL_P(temp_zval);
 731    }
 732
 733    // Reads property FORCE_COMPILE
 734    temp_zval = zend_read_property(Z_OBJCE_P(obj), obj, const_cast<char*>(FORCE_COMPILE), strlen(FORCE_COMPILE), 1 TSRMLS_CC);
 735    if (Z_TYPE_P(temp_zval) == IS_BOOL) {
 736        force_compile = Z_BVAL_P(temp_zval);
 737    }
 738
 739    // Reads property LAZY_CHECK
 740    temp_zval = zend_read_property(Z_OBJCE_P(obj), obj, const_cast<char*>(LAZY_CHECK), strlen(LAZY_CHECK), 1 TSRMLS_CC);
 741    if (Z_TYPE_P(temp_zval) == IS_BOOL) {
 742        lazy_check = Z_BVAL_P(temp_zval);
 743    }
 744#else
 745    zval **temp;
 746
 747    // Reads property COMPILE_CHECK
 748    if (zend_hash_find(Z_OBJPROP_P(obj), const_cast<char*>(COMPILE_CHECK), sizeof(COMPILE_CHECK), (void**)&temp) == SUCCESS) {
 749        if (Z_TYPE_PP(temp) == IS_BOOL) {
 750            compile_check = Z_BVAL_PP(temp);
 751        }
 752    }
 753
 754    // Reads property FORCE_COMPILE
 755    if (zend_hash_find(Z_OBJPROP_P(obj), const_cast<char*>(FORCE_COMPILE), sizeof(FORCE_COMPILE), (void**)&temp) == SUCCESS) {
 756        if (Z_TYPE_PP(temp) == IS_BOOL) {
 757            force_compile = Z_BVAL_PP(temp);
 758        }
 759    }
 760
 761    // Reads property LAZY_CHECK
 762    if (zend_hash_find(Z_OBJPROP_P(obj), const_cast<char*>(LAZY_CHECK), sizeof(LAZY_CHECK), (void**)&temp) == SUCCESS) {
 763        if (Z_TYPE_PP(temp) == IS_BOOL) {
 764            lazy_check = Z_BVAL_PP(temp);
 765        }
 766    }
 767#endif // ZEND_ENGINE_2
 768
 769DEBUG_PRINTF("compile_check = (%d)", compile_check);
 770DEBUG_PRINTF("force_compile = (%d)", force_compile);
 771DEBUG_PRINTF("lazy_check = (%d)", lazy_check);
 772
 773    // Returns false if force_compile is true.
 774    if (force_compile) {
 775        return false;
 776    }
 777
 778    // Has the compiled file already exists?
 779    struct stat cs;
 780    string full_compile_filename(compile_dir);
 781    full_compile_filename += DEFAULT_SLASH;
 782    full_compile_filename += filename;
 783    if (VCWD_STAT(full_compile_filename.c_str(), &cs) == -1) {
 784        // the compiled file doesn't exists.
 785        return false;
 786    }
 787
 788    // Returns true if compile_check is false.
 789    if (!compile_check) {
 790        return true;
 791    }
 792
 793    // compare the file and compiled file.
 794    struct stat ts;
 795    string full_template_filename(template_dir);
 796    full_template_filename += DEFAULT_SLASH;
 797    full_template_filename += filename;
 798
 799    if (VCWD_STAT(full_template_filename.c_str(), &ts) == -1) {
 800        zend_error(E_ERROR, "cannot stat:%s", full_template_filename.c_str());
 801        return false;
 802    }
 803
 804DEBUG_PRINTF("template_filename = (%s)", full_template_filename.c_str());
 805DEBUG_PRINTF("compiled_filename = (%s)", full_compile_filename.c_str());
 806DEBUG_PRINTF("cs.st_mtime = (%ld), ts.st_mtime = (%ld)", cs.st_mtime, ts.st_mtime);
 807
 808    // Returns false if template file is newer than compiled file.
 809    if (cs.st_mtime < ts.st_mtime) {
 810        return false;
 811    } else {
 812        // Not checks the included file if lazy check is true.
 813        if (!lazy_check) {
 814
 815            size_t pos = 0;
 816            size_t tag_start_pos, tag_end_pos;
 817            size_t element_start_pos, element_end_pos;
 818            string item;
 819            string file_content = _readfile(full_template_filename.c_str() TSRMLS_CC);
 820
 821            // Reads include tags.
 822            while ((pos = file_content.find(left_delimiter, pos)) != string::npos) {
 823                tag_start_pos = pos;
 824                element_start_pos = tag_start_pos + strlen(left_delimiter);
 825                element_end_pos = file_content.find(right_delimiter, tag_start_pos);
 826                if (element_end_pos == string::npos) {
 827                    zend_error(E_ERROR, "No closed delimiter:`%s' in %s", right_delimiter, full_template_filename.c_str());
 828                    return false;
 829                }
 830                tag_end_pos = element_end_pos + strlen(right_delimiter);
 831
 832                // Reads between left_delimiter and right_delimiter.
 833                item.assign(file_content, element_start_pos, element_end_pos - element_start_pos);
 834                item = trim(item.c_str());
 835
 836                // Finds include tag.
 837                if (item.substr(0, 7) == "include") {
 838
 839DEBUG_PRINTF("item = (%s)", item.c_str());
 840
 841                    string filename = get_element(item.c_str(), "file");
 842                    string included_filename = get_include_filename(obj, template_dir, filename TSRMLS_CC);
 843
 844                    // Check included template file timestamp
 845                    struct stat is;
 846                    if (VCWD_STAT(included_filename.c_str(), &is) == -1) {
 847                        zend_error(E_ERROR, "cannot stat:%s", included_filename.c_str());
 848                        return false;
 849                    }
 850
 851DEBUG_PRINTF("included_filename = (%s)", included_filename.c_str());
 852DEBUG_PRINTF("cs.st_mtime = (%ld), is.st_mtime = (%ld)", cs.st_mtime, is.st_mtime);
 853                    // Returns false if included file is newer than compiled file.
 854                    if (cs.st_mtime < is.st_mtime) {
 855                        return false;
 856                    }
 857
 858                    // Reads included file.
 859                    string new_condition = _readfile(included_filename.c_str() TSRMLS_CC);
 860
 861                    // Needs this check ?
 862                    if (new_condition.find(filename) != string::npos) {
 863                        zend_error(E_ERROR, "Found recursive include:%s", filename.c_str());
 864                        return "";
 865                    }
 866                    file_content.replace(tag_start_pos, tag_end_pos - tag_start_pos, new_condition);
 867                    continue;
 868                }
 869                pos++;
 870            }
 871        } // lazy check
 872    }
 873
 874    return true;
 875}
 876/* }}} */
 877
 878/* {{{ Parses and compiles the specified temaplte file.
 879 *
 880 * @param INTERNAL_FUNCTION_PARAMETERS see zend.h
 881 * @param char **fullfile_name         returns full path of template file
 882 * @param int  mode                    SIMPLATE_FETCH or SIMPLATE_DISPLAY
 883 * @param char **cache_content         returns results (use fetch mode)
 884 *
 885 * @return void
 886 */
 887void read_parse_template(
 888    INTERNAL_FUNCTION_PARAMETERS,
 889    char **fullfile_name,
 890    int mode,
 891    char **cache_content = NULL
 892)
 893{
 894    zval *obj;
 895    char *resource_name = NULL;
 896    int resource_name_len = 0;
 897    string template_dir;
 898    char *compile_dir = NULL;
 899    char *left_delimiter = NULL;
 900    char *right_delimiter = NULL;
 901    long caching = 0;
 902    long cache_lifetime = 0;
 903    size_t i = 0;
 904    string error;
 905
 906    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char*>("s"), &resource_name, &resource_name_len) == FAILURE) {
 907        return;
 908    }
 909
 910DEBUG_PRINTF("resource_name = (%s)", resource_name);
 911
 912    obj = getThis();
 913#ifdef ZEND_ENGINE_2
 914    zval *temp_zval;
 915    temp_zval = zend_read_property(Z_OBJCE_P(obj), obj, const_cast<char*>(TEMPLATE_DIR), strlen(TEMPLATE_DIR), 1 TSRMLS_CC);
 916    if (Z_TYPE_P(temp_zval) == IS_STRING) {
 917        template_dir = Z_STRVAL_P(temp_zval);
 918    }
 919    temp_zval = zend_read_property(Z_OBJCE_P(obj), obj, const_cast<char*>(COMPILE_DIR), strlen(COMPILE_DIR), 1 TSRMLS_CC);
 920    if (Z_TYPE_P(temp_zval) == IS_STRING) {
 921        compile_dir = Z_STRVAL_P(temp_zval);
 922    }
 923    temp_zval = zend_read_property(Z_OBJCE_P(obj), obj, const_cast<char*>(LEFT_DELIMITER), strlen(LEFT_DELIMITER), 1 TSRMLS_CC);
 924    if (Z_TYPE_P(temp_zval) == IS_STRING) {
 925        left_delimiter = Z_STRVAL_P(temp_zval);
 926    }
 927    temp_zval = zend_read_property(Z_OBJCE_P(obj), obj, const_cast<char*>(RIGHT_DELIMITER), strlen(RIGHT_DELIMITER), 1 TSRMLS_CC);
 928    if (Z_TYPE_P(temp_zval) == IS_STRING) {
 929        right_delimiter = Z_STRVAL_P(temp_zval);
 930    }
 931    temp_zval = zend_read_property(Z_OBJCE_P(obj), obj, const_cast<char*>(CACHING), strlen(CACHING), 1 TSRMLS_CC);
 932    if (Z_TYPE_P(temp_zval) == IS_LONG) {
 933        caching = Z_LVAL_P(temp_zval);
 934    }
 935    temp_zval = zend_read_property(Z_OBJCE_P(obj), obj, const_cast<char*>(CACHE_LIFETIME), strlen(CACHE_LIFETIME), 1 TSRMLS_CC);
 936    if (Z_TYPE_P(temp_zval) == IS_LONG) {
 937        cache_lifetime = Z_LVAL_P(temp_zval);
 938    }
 939#else
 940    zval **temp_zval;
 941
 942    // Logic to implement var_dump($this) for PHP4
 943    // $this = &$smarty;
 944    // zend_hash_update(&EG(symbol_table), "this", strlen("this") + 1, &obj, sizeof(zval*), NULL);
 945
 946    if (zend_hash_find(Z_OBJPROP_P(obj), TEMPLATE_DIR, sizeof(TEMPLATE_DIR), (void**)&temp_zval) == SUCCESS) {
 947        if ((*temp_zval)->type == IS_STRING) {
 948          template_dir = Z_STRVAL_PP(temp_zval);
 949        }
 950    }
 951    if (zend_hash_find(Z_OBJPROP_P(obj), COMPILE_DIR, sizeof(COMPILE_DIR), (void**)&temp_zval) == SUCCESS) {
 952        if ((*temp_zval)->type == IS_STRING) {
 953            compile_dir = Z_STRVAL_PP(temp_zval);
 954        }
 955    }
 956    if (zend_hash_find(Z_OBJPROP_P(obj), LEFT_DELIMITER, sizeof(LEFT_DELIMITER), (void**)&temp_zval) == SUCCESS) {
 957        if ((*temp_zval)->type == IS_STRING) {
 958            left_delimiter = Z_STRVAL_PP(temp_zval);
 959        }
 960    }
 961    if (zend_hash_find(Z_OBJPROP_P(obj), RIGHT_DELIMITER, sizeof(RIGHT_DELIMITER), (void**)&temp_zval) == SUCCESS) {
 962        if ((*temp_zval)->type == IS_STRING) {
 963            right_delimiter = Z_STRVAL_PP(temp_zval);
 964        }
 965    }
 966    if (zend_hash_find(Z_OBJPROP_P(obj), CACHING, sizeof(CACHING), (void**)&temp_zval) == SUCCESS) {
 967        if ((*temp_zval)->type == IS_LONG) {
 968            caching = Z_LVAL_PP(temp_zval);
 969        }
 970    }
 971    if (zend_hash_find(Z_OBJPROP_P(obj), CACHE_LIFETIME, sizeof(CACHE_LIFETIME), (void**)&temp_zval) == SUCCESS) {
 972        if ((*temp_zval)->type == IS_LONG) {
 973            cache_lifetime = Z_LVAL_PP(temp_zval);
 974        }
 975    }
 976#endif // ZEND_ENGINE_2
 977
 978DEBUG_PRINTF("template_dir = (%s)", template_dir.c_str());
 979DEBUG_PRINTF("compile_dir = (%s)", compile_dir);
 980DEBUG_PRINTF("left_delimiter = (%s)", left_delimiter);
 981DEBUG_PRINTF("right_delimiter = (%s)", right_delimiter);
 982DEBUG_PRINTF("caching = (%ld)", caching);
 983DEBUG_PRINTF("cache_lifetime = (%ld)", cache_lifetime);
 984
 985    if (template_dir[template_dir.length() - 1] == DEFAULT_SLASH) {
 986        template_dir.erase(template_dir.length() - 1, 1);
 987    }
 988    string full_template_filename(template_dir);
 989    full_template_filename += DEFAULT_SLASH;
 990    full_template_filename += resource_name;
 991
 992DEBUG_PRINTF("full_template_filename = (%s)", full_template_filename.c_str());
 993
 994    string full_compile_filename(compile_dir);
 995    if (full_compile_filename[full_compile_filename.length() - 1] != DEFAULT_SLASH) {
 996        full_compile_filename += DEFAULT_SLASH;
 997    }
 998    full_compile_filename += resource_name;
 999
1000DEBUG_PRINTF("full_compile_filename = (%s)", full_compile_filename.c_str());
1001
1002    if (caching) {
1003        struct stat cache_stat;
1004        char *cache_dir;
1005        unsigned char compile_check;
1006        unsigned char force_compile;
1007#ifdef ZEND_ENGINE_2
1008        cache_dir = Z_STRVAL_P(zend_read_property(Z_OBJCE_P(obj), obj, const_cast<char*>(CACHE_DIR), strlen(CACHE_DIR), 1 TSRMLS_CC));
1009        compile_check = Z_BVAL_P(zend_read_property(Z_OBJCE_P(obj), obj, const_cast<char*>(COMPILE_CHECK), strlen(COMPILE_CHECK), 1 TSRMLS_CC));
1010        force_compile = Z_BVAL_P(zend_read_property(Z_OBJCE_P(obj), obj, const_cast<char*>(FORCE_COMPILE), strlen(FORCE_COMPILE), 1 TSRMLS_CC));
1011#else
1012#endif // ZEND_ENGINE_2
1013        if (cache_dir[strlen(cache_dir) - 1] == DEFAULT_SLASH) {
1014            cache_dir[strlen(cache_dir) - 1] = '\0';
1015        }
1016        // cache directory exists?
1017        if (VCWD_STAT(cache_dir, &cache_stat) != -1) {
1018            if (!S_ISDIR(cache_stat.st_mode)) {
1019                zend_error(E_ERROR, "does not exist cache directory: %s", cache_dir);
1020                return;
1021            }
1022        }
1023        string full_cache_filename(cache_dir);
1024        full_cache_filename += DEFAULT_SLASH;
1025        full_cache_filename += resource_name;
1026
1027DEBUG_PRINTF("full_cache_filename = (%s)", full_cache_filename.c_str());
1028
1029        // there are no cache file.
1030        if (VCWD_STAT(full_cache_filename.c_str(), &cache_stat) == -1 || force_compile) {
1031
1032create_cache:
1033            if (VCWD_STAT(full_compile_filename.c_str(), &cache_stat) == -1) {
1034                goto compile;
1035            }
1036
1037            zend_file_handle file_handle;
1038            zend_op_array *op_array;
1039            file_handle.filename = const_cast<char*>(full_compile_filename.c_str());
1040            file_handle.free_filename = 0;
1041            file_handle.type = ZEND_HANDLE_FILENAME;
1042            file_handle.opened_path = NULL;
1043            op_array = zend_compile_file(&file_handle, ZEND_INCLUDE TSRMLS_CC);
1044            if (!op_array) {
1045                zend_error(E_ERROR, "Error parsing script:%s", full_compile_filename.c_str());
1046                return;
1047            }
1048            zend_destroy_file_handle(&file_handle TSRMLS_CC);
1049
1050            SIMPLATE_G(global_string.str(std::string())); // let global_string empty.
1051            int (*old_output_func)(const char*, unsigned int TSRMLS_DC);
1052            old_output_func = OG(php_body_write);
1053            OG(php_body_write) = php_my_output_func;
1054            zend_execute(op_array TSRMLS_CC);
1055            OG(php_body_write) = old_output_func;
1056
1057#ifdef ZEND_ENGINE_2
1058            destroy_op_array(op_array TSRMLS_CC);
1059#else
1060            destroy_op_array(op_array);
1061#endif // ZEND_ENGINE_2
1062            efree(op_array);
1063
1064DEBUG_PRINTF("SIMPLATE_G(global_string) = (%ld)", SIMPLATE_G(global_string).str().length());
1065
1066            if (mode == SIMPLATE_FETCH) {
1067                *cache_content = estrndup(SIMPLATE_G(global_string).str().c_str(), SIMPLATE_G(global_string).str().length());
1068            } else {
1069                if (SIMPLATE_G(global_string).str().length() > 0) {
1070                    zend_printf("%s", SIMPLATE_G(global_string).str().c_str());
1071                }
1072            }
1073
1074#ifdef PHP_WIN32
1075            php_stream *strm = php_stream_open_wrapper(const_cast<char*>(full_cache_filename.c_str()), "wb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL);
1076            if (strm) {
1077                if (php_stream_supports_lock(strm)) {
1078                    php_stream_lock(strm, LOCK_EX);
1079                }
1080                if (SIMPLATE_G(global_string).str().length() > 0) {
1081                    php_stream_write_string(strm, const_cast<char*>(SIMPLATE_G(global_string).str().c_str()));
1082                }
1083                if (php_stream_supports_lock(strm)) {
1084                    php_stream_lock(strm, LOCK_UN);
1085                }
1086                php_stream_close(strm);
1087            }
1088#else
1089            FILE *fp = VCWD_FOPEN(full_cache_filename.c_str(), "wb");
1090            if (fp == NULL) {
1091                // fail to create cache
1092                zend_error(E_ERROR, "fail to create cache:%s", full_cache_filename.c_str());
1093                return;
1094            }
1095            if (fwrite(SIMPLATE_G(global_string).str().c_str(), 1, SIMPLATE_G(global_string).str().length(), fp) != SIMPLATE_G(global_string).str().length()) {
1096                // fail to write cache
1097                zend_error(E_ERROR, "fail to write cache:%s", full_cache_filename.c_str());
1098                // Notice: don't retun, yet.
1099            }
1100            fclose(fp);
1101#endif // PHP_WIN32
1102
1103        } else {
1104
1105DEBUG_PRINTF("%s", "cache file exists.");
1106
1107            if (compile_check) {
1108
1109DEBUG_PRINTF("%s", "check cache file is compiled.");
1110
1111                // check cache filestamp
1112                time_t now;
1113                time(&now);
1114                if (now - cache_stat.st_mtime > cache_lifetime) {
1115DEBUG_PRINTF("%s", "cache file is not compiled.");
1116                    goto create_cache;
1117                }
1118            }
1119
1120            string file_contents = _readfile(full_cache_filename.c_str() TSRMLS_CC);
1121            if (mode == SIMPLATE_FETCH) {
1122                *cache_content = estrndup(file_contents.c_str(), file_contents.length());
1123            } else {
1124                zend_printf("%s", file_contents.c_str());
1125            }
1126        }
1127DEBUG_PRINTF("%s", "end function");
1128
1129        return;
1130    }
1131
1132    // the directory for compiled templates exists?
1133    struct stat compile_dir_stat;
1134    if (VCWD_STAT(compile_dir, &compile_dir_stat) != -1) {
1135        if (!S_ISDIR(compile_dir_stat.st_mode)) {
1136            zend_error(E_ERROR, "%s is not directory", compile_dir);
1137            return;
1138        }
1139    } else {
1140        // mkdir recursively
1141        php_stream_context *context = NULL;
1142        if (!php_stream_mkdir(compile_dir, 0777, (PHP_STREAM_MKDIR_RECURSIVE | REPORT_ERRORS), context)) {
1143            zend_error(E_ERROR, "fail to mkdir (%s)", compile_dir);
1144            return;
1145        }
1146    }
1147
1148    // already compiled
1149    if (_is_compiled(obj, template_dir.c_str(), compile_dir, resource_name, left_delimiter, right_delimiter TSRMLS_CC)) {
1150
1151    } else {
1152compile:
1153        // compile now
1154        string compiled_file_content = _readfile(full_template_filename.c_str() TSRMLS_CC);
1155
1156        // prefilter
1157        zval *plugins = zend_read_property(Z_OBJCE_P(obj), obj, const_cast<char*>("_plugins"), strlen("_plugins"), 1 TSRMLS_CC);
1158        if (plugins && Z_TYPE_P(plugins) == IS_ARRAY) {
1159            zval **prefilter;
1160            if (zend_hash_find(Z_ARRVAL_P(plugins), "prefilter", sizeof("prefilter"), (void**)&prefilter) == SUCCESS
1161                && Z_TYPE_PP(prefilter) == IS_ARRAY
1162            ) {
1163                zval **elem;
1164                zend_hash_internal_pointer_reset(Z_ARRVAL_PP(prefilter));
1165                while (zend_hash_get_current_data(Z_ARRVAL_PP(prefilter), (void**)&elem) == SUCCESS) {
1166
1167DEBUG_PRINTF("prefilter function = (%s)", Z_STRVAL_PP(elem));
1168
1169                    zval prefilter_function, prefilter_ret;
1170                    zval zcontent;
1171                    zval *prefilter_argv[1];
1172                    prefilter_argv[0] = &zcontent;
1173                    SET_ZVAL_STRING(zcontent, compiled_file_content.c_str());
1174
1175                    INIT_ZVAL(prefilter_function);
1176                    ZVAL_STRING(&prefilter_function, Z_STRVAL_PP(elem), 1);
1177
1178                    if (call_user_function(EG(function_table), NULL, &prefilter_function, &prefilter_ret, 1, prefilter_argv TSRMLS_CC) == FAILURE) {
1179                        zval_dtor(&zcontent);
1180                        zend_error(E_ERROR, "fail to %s", Z_STRVAL_PP(elem));
1181                        return;
1182                    }
1183                    zval_dtor(&prefilter_function);
1184                    zval_dtor(&zcontent);
1185                    compiled_file_content = Z_STRVAL(prefilter_ret);
1186
1187DEBUG_PRINTF("compiled_file_content = (%s)", compiled_file_content.c_str());
1188
1189                    zval_dtor(&prefilter_ret);
1190                    zend_hash_move_forward(Z_ARRVAL_PP(prefilter));
1191                }
1192            }
1193        }
1194
1195        size_t pos = 0;
1196        size_t tag_start_pos, tag_end_pos;
1197        size_t element_start_pos, element_end_pos;
1198        string item;
1199        char end_comment_tag[12];
1200        string new_condition;
1201        sprintf(end_comment_tag, "*%s", right_delimiter);
1202        while ((pos = compiled_file_content.find(left_delimiter, pos)) != string::npos) {
1203
1204            // <{$hoge}>
1205            // ^         :tag_start_pos
1206            //   ^       :element_start_pos
1207            //        ^  :element_end_pos
1208            //          ^:tag_end_pos
1209            //   ^---^   :item
1210
1211            tag_start_pos = pos;
1212            element_start_pos = tag_start_pos + strlen(left_delimiter);
1213            element_end_pos = compiled_file_content.find(right_delimiter, tag_start_pos);
1214            if (element_end_pos == string::npos) {
1215                zend_error(E_ERROR, "No closed delimiter:`%s' in %s", right_delimiter, full_template_filename.c_str());
1216                return;
1217            }
1218            tag_end_pos = element_end_pos + strlen(right_delimiter);
1219
1220            item.assign(compiled_file_content, element_start_pos, element_end_pos - element_start_pos);
1221            item = trim(item.c_str());
1222            string variable;
1223            string new_variable;
1224
1225DEBUG_PRINTF("item = (%s)", item.c_str());
1226
1227            if (item[0] == '$') {
1228
1229DEBUG_PRINTF("variable item = (%s)", item.c_str());
1230
1231cond_var:
1232                new_condition = "<?php echo ";
1233                i = 0;
1234
1235                while (element_start_pos + i < element_end_pos) {
1236                    if (compiled_file_content[element_start_pos + i] == '+'
1237                        || (compiled_file_content[element_start_pos + i] == '-'
1238                            && compiled_file_content.at(element_start_pos + i + 1) != '>')
1239                        || compiled_file_content[element_start_pos + i] == '*'
1240                        || compiled_file_content[element_start_pos + i] == '/'
1241                        || compiled_file_content[element_start_pos + i] == '%'
1242                        || compiled_file_content[element_start_pos + i] == '('
1243                        || compiled_file_content[element_start_pos + i] == ')'
1244                        || compiled_file_content[element_start_pos + i] == ','
1245                        || compiled_file_content[element_start_pos + i] == '\''
1246                        || compiled_file_content[element_start_pos + i] == '"'
1247                        || compiled_file_content[element_start_pos + i] == '?'
1248                        || compiled_file_content[element_start_pos + i] == ':'
1249                        || compiled_file_content[element_start_pos + i] == ';'
1250                        || compiled_file_content[element_start_pos + i] == ','
1251                        || compiled_file_content[element_start_pos + i] == ' '
1252                    ) {
1253
1254                        if (variable != "$") {
1255                            new_variable = parse_variable(variable.c_str());
1256
1257DEBUG_PRINTF("(%s) = (%s)", variable.c_str(), new_variable.c_str());
1258
1259                        } else {
1260                            new_variable = variable;
1261                        }
1262                        new_condition += new_variable;
1263                        new_condition += compiled_file_content[element_start_pos + i];
1264                        variable = "";
1265                    } else {
1266                        variable += compiled_file_content[element_start_pos + i];
1267DEBUG_PRINTF("variable = (%s)", variable.c_str());
1268                    }
1269                    i++;
1270                }
1271                new_variable = parse_variable(variable.c_str());
1272
1273DEBUG_PRINTF("(%s) = (%s)", variable.c_str(), new_variable.c_str());
1274
1275                new_condition += new_variable;
1276                new_condition += "; ?>";
1277
1278DEBUG_PRINTF("new_condition = (%s)", new_condition.c_str());
1279
1280                compiled_file_content.replace(tag_start_pos, tag_end_pos - tag_start_pos, new_condition);
1281                pos += new_condition.length() - 1; // 20060527
1282            } else if (item[0] == '*') { // Comment
1283                if (item[item.length() - 1] != '*') { // support multi-line comment
1284                    tag_end_pos = compiled_file_content.find(end_comment_tag, tag_end_pos) + strlen(end_comment_tag);
1285                }
1286
1287DEBUG_PRINTF("COMMENT = (%s)", compiled_file_content.substr(tag_start_pos, tag_end_pos - tag_start_pos).c_str());
1288
1289                compiled_file_content.erase(tag_start_pos, tag_end_pos - tag_start_pos);
1290                pos = tag_start_pos - 1;
1291            } else {
1292
1293DEBUG_PRINTF("CONDITION = (%s)", item.c_str());
1294
1295                string command_tag;
1296                string condition;
1297                if (item.find(" ") != string::npos) {
1298                    command_tag = item.substr(0, item.find(" "));
1299                    condition = item.substr(item.find(" ") + 1);
1300                } else {
1301                    command_tag = item;
1302                }
1303
1304DEBUG_PRINTF("COMMAND = (%s)", command_tag.c_str());
1305DEBUG_PRINTF("CONDITION = (%s)", condition.c_str());
1306
1307                if (command_tag == "if") {
1308                    new_condition = "<?php if (";
1309cond_if:
1310                    i = 0;
1311                    while (i < condition.length()) {
1312                        if (condition[i] == '+'
1313                            || (condition[i] == '-' && condition.at(i + 1) != '>')
1314                            || condition[i] == '*'
1315                            || condition[i] == '/'
1316                            || condition[i] == '%'
1317                            || condition[i] == '<'
1318                            || (condition[i] == '>' && condition.at(i - 1) != '-')
1319                            || condition[i] == '='
1320                            || condition[i] == '!'
1321                            || condition[i] == '|'
1322                            || condition[i] == '&'
1323                            || condition[i] == '('
1324                            || condition[i] == ')'
1325                            || condition[i] == ','
1326                            || condition[i] == ' '
1327                        ) {
1328                            new_variable = parse_variable(variable.c_str());
1329
1330DEBUG_PRINTF("(%s) = (%s)", variable.c_str(), new_variable.c_str());
1331
1332                            new_condition += new_variable;
1333                            new_condition += condition[i];
1334                            variable = "";
1335                        } else {
1336                            variable += condition[i];
1337                        }
1338                        i++;
1339                    }
1340                    new_variable = parse_variable(variable.c_str());
1341                    new_condition += new_variable;
1342                    new_condition += ") { ?>";
1343                    compiled_file_content.replace(tag_start_pos, tag_end_pos - tag_start_pos, new_condition);
1344                    pos += new_condition.length() - 1; // 20060527
1345                } else if (command_tag == "/if") {
1346                    compiled_file_content.replace(tag_start_pos, tag_end_pos - tag_start_pos, "<?php } ?>");
1347                    pos += strlen("<?php } ?>") - 1; // 20060527
1348                } else if (command_tag == "else") {
1349                    if (item.find("else if") != string::npos) {
1350                        new_condition = "<?php } else if (";
1351                        condition = item.substr(item.find("else if") + strlen("else if"));
1352                        condition = trim(condition.c_str());
1353                        goto cond_if;
1354                    }
1355                    compiled_file_content.replace(tag_start_pos, tag_end_pos - tag_start_pos, "<?php } else { ?>");
1356                    pos += strlen("<?php } else { ?>") - 1; // 20060527
1357                } else if (command_tag == "elseif") {
1358                    new_condition = "<?php } else if (";
1359                    goto cond_if;
1360                } else if (command_tag == "section") {
1361                    string section_name = get_element(item.c_str(), "name");
1362                    string section_loop = get_element(item.c_str(), "loop");
1363                    string section_start = get_element(item.c_str(), "start");
1364                    string section_step = get_element(item.c_str(), "step");
1365                    string new_section_loop2 = "";
1366                    condition = section_loop;
1367
1368                    i = 0;
1369                    while (i < condition.length()) {
1370                        if (condition[i] == '+'
1371                            || condition[i] == '-'
1372                            || condition[i] == '*'
1373                            || condition[i] == '/'
1374                            || condition[i] == '%'
1375    

Large files files are truncated, but you can click here to view the full file