PageRenderTime 53ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

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

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