twig.c 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Twig Extension |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2011 Derick Rethans |
  6. +----------------------------------------------------------------------+
  7. | Redistribution and use in source and binary forms, with or without |
  8. | modification, are permitted provided that the conditions mentioned |
  9. | in the accompanying LICENSE file are met (BSD, revised). |
  10. +----------------------------------------------------------------------+
  11. | Author: Derick Rethans <derick@derickrethans.nl> |
  12. +----------------------------------------------------------------------+
  13. */
  14. #ifdef HAVE_CONFIG_H
  15. #include "config.h"
  16. #endif
  17. #include "php.h"
  18. #include "php_twig.h"
  19. #include "ext/standard/php_string.h"
  20. #include "ext/standard/php_smart_str.h"
  21. #include "Zend/zend_object_handlers.h"
  22. #include "Zend/zend_interfaces.h"
  23. #include "Zend/zend_exceptions.h"
  24. #ifndef Z_ADDREF_P
  25. #define Z_ADDREF_P(pz) (pz)->refcount++
  26. #endif
  27. #if PHP_VERSION_ID >= 50300
  28. #define APPLY_TSRMLS_DC TSRMLS_DC
  29. #define APPLY_TSRMLS_CC TSRMLS_CC
  30. #define APPLY_TSRMLS_FETCH()
  31. #else
  32. #define APPLY_TSRMLS_DC
  33. #define APPLY_TSRMLS_CC
  34. #define APPLY_TSRMLS_FETCH() TSRMLS_FETCH()
  35. #endif
  36. ZEND_BEGIN_ARG_INFO_EX(twig_template_get_attribute_args, ZEND_SEND_BY_VAL, ZEND_RETURN_VALUE, 6)
  37. ZEND_ARG_INFO(0, template)
  38. ZEND_ARG_INFO(0, object)
  39. ZEND_ARG_INFO(0, item)
  40. ZEND_ARG_INFO(0, arguments)
  41. ZEND_ARG_INFO(0, type)
  42. ZEND_ARG_INFO(0, isDefinedTest)
  43. ZEND_END_ARG_INFO()
  44. zend_function_entry twig_functions[] = {
  45. PHP_FE(twig_template_get_attributes, twig_template_get_attribute_args)
  46. {NULL, NULL, NULL}
  47. };
  48. zend_module_entry twig_module_entry = {
  49. STANDARD_MODULE_HEADER,
  50. "twig",
  51. twig_functions,
  52. NULL,
  53. NULL,
  54. NULL,
  55. NULL,
  56. NULL,
  57. PHP_TWIG_VERSION,
  58. STANDARD_MODULE_PROPERTIES
  59. };
  60. #ifdef COMPILE_DL_TWIG
  61. ZEND_GET_MODULE(twig)
  62. #endif
  63. int TWIG_ARRAY_KEY_EXISTS(zval *array, char* key, int key_len)
  64. {
  65. if (Z_TYPE_P(array) != IS_ARRAY) {
  66. return 0;
  67. }
  68. return zend_symtable_exists(Z_ARRVAL_P(array), key, key_len + 1);
  69. }
  70. int TWIG_INSTANCE_OF(zval *object, zend_class_entry *interface TSRMLS_DC)
  71. {
  72. if (Z_TYPE_P(object) != IS_OBJECT) {
  73. return 0;
  74. }
  75. return instanceof_function(Z_OBJCE_P(object), interface TSRMLS_CC);
  76. }
  77. int TWIG_INSTANCE_OF_USERLAND(zval *object, char *interface TSRMLS_DC)
  78. {
  79. zend_class_entry **pce;
  80. if (Z_TYPE_P(object) != IS_OBJECT) {
  81. return 0;
  82. }
  83. if (zend_lookup_class(interface, strlen(interface), &pce TSRMLS_CC) == FAILURE) {
  84. return 0;
  85. }
  86. return instanceof_function(Z_OBJCE_P(object), *pce TSRMLS_CC);
  87. }
  88. zval *TWIG_GET_ARRAYOBJECT_ELEMENT(zval *object, zval *offset TSRMLS_DC)
  89. {
  90. zend_class_entry *ce = Z_OBJCE_P(object);
  91. zval *retval;
  92. if (Z_TYPE_P(object) == IS_OBJECT) {
  93. SEPARATE_ARG_IF_REF(offset);
  94. zend_call_method_with_1_params(&object, ce, NULL, "offsetget", &retval, offset);
  95. zval_ptr_dtor(&offset);
  96. if (!retval) {
  97. if (!EG(exception)) {
  98. zend_error(E_ERROR, "Undefined offset for object of type %s used as array", ce->name);
  99. }
  100. return NULL;
  101. }
  102. return retval;
  103. }
  104. return NULL;
  105. }
  106. int TWIG_ISSET_ARRAYOBJECT_ELEMENT(zval *object, zval *offset TSRMLS_DC)
  107. {
  108. zend_class_entry *ce = Z_OBJCE_P(object);
  109. zval *retval;
  110. if (Z_TYPE_P(object) == IS_OBJECT) {
  111. SEPARATE_ARG_IF_REF(offset);
  112. zend_call_method_with_1_params(&object, ce, NULL, "offsetexists", &retval, offset);
  113. zval_ptr_dtor(&offset);
  114. if (!retval) {
  115. if (!EG(exception)) {
  116. zend_error(E_ERROR, "Undefined offset for object of type %s used as array", ce->name);
  117. }
  118. return 0;
  119. }
  120. return (retval && Z_TYPE_P(retval) == IS_BOOL && Z_LVAL_P(retval));
  121. }
  122. return 0;
  123. }
  124. char *TWIG_STRTOLOWER(const char *str, int str_len)
  125. {
  126. char *item_dup;
  127. item_dup = estrndup(str, str_len);
  128. php_strtolower(item_dup, str_len);
  129. return item_dup;
  130. }
  131. zval *TWIG_CALL_USER_FUNC_ARRAY(zval *object, char *function, zval *arguments TSRMLS_DC)
  132. {
  133. zend_fcall_info fci;
  134. zval ***args = NULL;
  135. int arg_count = 0;
  136. HashTable *table;
  137. HashPosition pos;
  138. int i = 0;
  139. zval *retval_ptr;
  140. zval *zfunction;
  141. if (arguments) {
  142. table = HASH_OF(arguments);
  143. args = safe_emalloc(sizeof(zval **), table->nNumOfElements, 0);
  144. zend_hash_internal_pointer_reset_ex(table, &pos);
  145. while (zend_hash_get_current_data_ex(table, (void **)&args[i], &pos) == SUCCESS) {
  146. i++;
  147. zend_hash_move_forward_ex(table, &pos);
  148. }
  149. arg_count = table->nNumOfElements;
  150. }
  151. MAKE_STD_ZVAL(zfunction);
  152. ZVAL_STRING(zfunction, function, 1);
  153. fci.size = sizeof(fci);
  154. fci.function_table = EG(function_table);
  155. fci.function_name = zfunction;
  156. fci.symbol_table = NULL;
  157. #if PHP_VERSION_ID >= 50300
  158. fci.object_ptr = object;
  159. #else
  160. fci.object_pp = &object;
  161. #endif
  162. fci.retval_ptr_ptr = &retval_ptr;
  163. fci.param_count = arg_count;
  164. fci.params = args;
  165. fci.no_separation = 0;
  166. if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
  167. zval_dtor(zfunction);
  168. efree(zfunction);
  169. zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), 0 TSRMLS_CC, "Could not execute %s::%s()", zend_get_class_entry(object TSRMLS_CC)->name, function TSRMLS_CC);
  170. }
  171. if (args) {
  172. efree(fci.params);
  173. }
  174. zval_dtor(zfunction);
  175. efree(zfunction);
  176. return retval_ptr;
  177. }
  178. int TWIG_CALL_BOOLEAN(zval *object, char *functionName TSRMLS_DC)
  179. {
  180. zval *ret;
  181. int res;
  182. ret = TWIG_CALL_USER_FUNC_ARRAY(object, functionName, NULL TSRMLS_CC);
  183. res = Z_LVAL_P(ret);
  184. zval_ptr_dtor(&ret);
  185. return res;
  186. }
  187. zval *TWIG_GET_STATIC_PROPERTY(zval *class, char *prop_name TSRMLS_DC)
  188. {
  189. zval **tmp_zval;
  190. zend_class_entry *ce;
  191. if (class == NULL || Z_TYPE_P(class) != IS_OBJECT) {
  192. return NULL;
  193. }
  194. ce = zend_get_class_entry(class TSRMLS_CC);
  195. #if PHP_VERSION_ID >= 50400
  196. tmp_zval = zend_std_get_static_property(ce, prop_name, strlen(prop_name), 0, NULL TSRMLS_CC);
  197. #else
  198. tmp_zval = zend_std_get_static_property(ce, prop_name, strlen(prop_name), 0 TSRMLS_CC);
  199. #endif
  200. return *tmp_zval;
  201. }
  202. zval *TWIG_GET_ARRAY_ELEMENT_ZVAL(zval *class, zval *prop_name TSRMLS_DC)
  203. {
  204. zval **tmp_zval;
  205. char *tmp_name;
  206. if (class == NULL || Z_TYPE_P(class) != IS_ARRAY || Z_TYPE_P(prop_name) != IS_STRING) {
  207. if (class != NULL && Z_TYPE_P(class) == IS_OBJECT && TWIG_INSTANCE_OF(class, zend_ce_arrayaccess TSRMLS_CC)) {
  208. // array access object
  209. return TWIG_GET_ARRAYOBJECT_ELEMENT(class, prop_name TSRMLS_CC);
  210. }
  211. return NULL;
  212. }
  213. convert_to_string(prop_name);
  214. tmp_name = Z_STRVAL_P(prop_name);
  215. if (zend_symtable_find(HASH_OF(class), tmp_name, strlen(tmp_name)+1, (void**) &tmp_zval) == SUCCESS) {
  216. return *tmp_zval;
  217. }
  218. return NULL;
  219. }
  220. zval *TWIG_GET_ARRAY_ELEMENT(zval *class, char *prop_name, int prop_name_length TSRMLS_DC)
  221. {
  222. zval **tmp_zval;
  223. if (class == NULL/* || Z_TYPE_P(class) != IS_ARRAY*/) {
  224. return NULL;
  225. }
  226. if (class != NULL && Z_TYPE_P(class) == IS_OBJECT && TWIG_INSTANCE_OF(class, zend_ce_arrayaccess TSRMLS_CC)) {
  227. // array access object
  228. zval *tmp_name_zval;
  229. zval *tmp_ret_zval;
  230. ALLOC_INIT_ZVAL(tmp_name_zval);
  231. ZVAL_STRING(tmp_name_zval, prop_name, 1);
  232. tmp_ret_zval = TWIG_GET_ARRAYOBJECT_ELEMENT(class, tmp_name_zval TSRMLS_CC);
  233. zval_dtor(tmp_name_zval);
  234. efree(tmp_name_zval);
  235. return tmp_ret_zval;
  236. }
  237. if (zend_symtable_find(HASH_OF(class), prop_name, prop_name_length+1, (void**)&tmp_zval) == SUCCESS) {
  238. return *tmp_zval;
  239. }
  240. return NULL;
  241. }
  242. zval *TWIG_PROPERTY(zval *object, zval *propname TSRMLS_DC)
  243. {
  244. zval *tmp = NULL;
  245. if (Z_OBJ_HT_P(object)->read_property) {
  246. #if PHP_VERSION_ID >= 50400
  247. tmp = Z_OBJ_HT_P(object)->read_property(object, propname, BP_VAR_IS, NULL TSRMLS_CC);
  248. #else
  249. tmp = Z_OBJ_HT_P(object)->read_property(object, propname, BP_VAR_IS TSRMLS_CC);
  250. #endif
  251. if (tmp != EG(uninitialized_zval_ptr)) {
  252. return tmp;
  253. } else {
  254. return NULL;
  255. }
  256. }
  257. return tmp;
  258. }
  259. int TWIG_HAS_PROPERTY(zval *object, zval *propname TSRMLS_DC)
  260. {
  261. if (Z_OBJ_HT_P(object)->has_property) {
  262. #if PHP_VERSION_ID >= 50400
  263. return Z_OBJ_HT_P(object)->has_property(object, propname, 0, NULL TSRMLS_CC);
  264. #else
  265. return Z_OBJ_HT_P(object)->has_property(object, propname, 0 TSRMLS_CC);
  266. #endif
  267. }
  268. return 0;
  269. }
  270. int TWIG_HAS_DYNAMIC_PROPERTY(zval *object, char *prop, int prop_len TSRMLS_DC)
  271. {
  272. if (Z_OBJ_HT_P(object)->get_properties) {
  273. return zend_hash_quick_exists(
  274. Z_OBJ_HT_P(object)->get_properties(object TSRMLS_CC), // the properties hash
  275. prop, // property name
  276. prop_len + 1, // property length
  277. zend_get_hash_value(prop, prop_len + 1) // hash value
  278. );
  279. }
  280. return 0;
  281. }
  282. zval *TWIG_PROPERTY_CHAR(zval *object, char *propname TSRMLS_DC)
  283. {
  284. zval *tmp_name_zval, *tmp;
  285. ALLOC_INIT_ZVAL(tmp_name_zval);
  286. ZVAL_STRING(tmp_name_zval, propname, 1);
  287. tmp = TWIG_PROPERTY(object, tmp_name_zval TSRMLS_CC);
  288. zval_dtor(tmp_name_zval);
  289. efree(tmp_name_zval);
  290. return tmp;
  291. }
  292. int TWIG_CALL_B_0(zval *object, char *method)
  293. {
  294. return 0;
  295. }
  296. zval *TWIG_CALL_S(zval *object, char *method, char *arg0 TSRMLS_DC)
  297. {
  298. zend_fcall_info fci;
  299. zval **args[1];
  300. zval *argument;
  301. zval *zfunction;
  302. zval *retval_ptr;
  303. MAKE_STD_ZVAL(argument);
  304. ZVAL_STRING(argument, arg0, 1);
  305. args[0] = &argument;
  306. MAKE_STD_ZVAL(zfunction);
  307. ZVAL_STRING(zfunction, method, 1);
  308. fci.size = sizeof(fci);
  309. fci.function_table = EG(function_table);
  310. fci.function_name = zfunction;
  311. fci.symbol_table = NULL;
  312. #if PHP_VERSION_ID >= 50300
  313. fci.object_ptr = object;
  314. #else
  315. fci.object_pp = &object;
  316. #endif
  317. fci.retval_ptr_ptr = &retval_ptr;
  318. fci.param_count = 1;
  319. fci.params = args;
  320. fci.no_separation = 0;
  321. if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
  322. zval_dtor(argument);
  323. return 0;
  324. }
  325. zval_dtor(zfunction);
  326. efree(zfunction);
  327. zval_dtor(argument);
  328. efree(argument);
  329. return retval_ptr;
  330. }
  331. int TWIG_CALL_SB(zval *object, char *method, char *arg0 TSRMLS_DC)
  332. {
  333. zval *retval_ptr;
  334. int success;
  335. retval_ptr = TWIG_CALL_S(object, method, arg0 TSRMLS_CC);
  336. success = (retval_ptr && (Z_TYPE_P(retval_ptr) == IS_BOOL) && Z_LVAL_P(retval_ptr));
  337. if (retval_ptr) {
  338. zval_ptr_dtor(&retval_ptr);
  339. }
  340. return success;
  341. }
  342. int TWIG_CALL_Z(zval *object, char *method, zval *arg1 TSRMLS_DC)
  343. {
  344. zend_fcall_info fci;
  345. zval **args[1];
  346. zval *zfunction;
  347. zval *retval_ptr;
  348. int success;
  349. args[0] = &arg1;
  350. MAKE_STD_ZVAL(zfunction);
  351. ZVAL_STRING(zfunction, method, 1);
  352. fci.size = sizeof(fci);
  353. fci.function_table = EG(function_table);
  354. fci.function_name = zfunction;
  355. fci.symbol_table = NULL;
  356. #if PHP_VERSION_ID >= 50300
  357. fci.object_ptr = object;
  358. #else
  359. fci.object_pp = &object;
  360. #endif
  361. fci.retval_ptr_ptr = &retval_ptr;
  362. fci.param_count = 1;
  363. fci.params = args;
  364. fci.no_separation = 0;
  365. if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
  366. zval_dtor(zfunction);
  367. efree(zfunction);
  368. if (retval_ptr) {
  369. zval_ptr_dtor(&retval_ptr);
  370. }
  371. return 0;
  372. }
  373. zval_dtor(zfunction);
  374. efree(zfunction);
  375. success = (retval_ptr && (Z_TYPE_P(retval_ptr) == IS_BOOL) && Z_LVAL_P(retval_ptr));
  376. if (retval_ptr) {
  377. zval_ptr_dtor(&retval_ptr);
  378. }
  379. return success;
  380. }
  381. int TWIG_CALL_ZZ(zval *object, char *method, zval *arg1, zval *arg2 TSRMLS_DC)
  382. {
  383. zend_fcall_info fci;
  384. zval **args[2];
  385. zval *zfunction;
  386. zval *retval_ptr;
  387. int success;
  388. args[0] = &arg1;
  389. args[1] = &arg2;
  390. MAKE_STD_ZVAL(zfunction);
  391. ZVAL_STRING(zfunction, method, 1);
  392. fci.size = sizeof(fci);
  393. fci.function_table = EG(function_table);
  394. fci.function_name = zfunction;
  395. fci.symbol_table = NULL;
  396. #if PHP_VERSION_ID >= 50300
  397. fci.object_ptr = object;
  398. #else
  399. fci.object_pp = &object;
  400. #endif
  401. fci.retval_ptr_ptr = &retval_ptr;
  402. fci.param_count = 2;
  403. fci.params = args;
  404. fci.no_separation = 0;
  405. if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
  406. zval_dtor(zfunction);
  407. return 0;
  408. }
  409. zval_dtor(zfunction);
  410. success = (retval_ptr && (Z_TYPE_P(retval_ptr) == IS_BOOL) && Z_LVAL_P(retval_ptr));
  411. if (retval_ptr) {
  412. zval_ptr_dtor(&retval_ptr);
  413. }
  414. return success;
  415. }
  416. #ifndef Z_SET_REFCOUNT_P
  417. # define Z_SET_REFCOUNT_P(pz, rc) pz->refcount = rc
  418. # define Z_UNSET_ISREF_P(pz) pz->is_ref = 0
  419. #endif
  420. void TWIG_NEW(zval *object, char *class, zval *arg0, zval *arg1 TSRMLS_DC)
  421. {
  422. zend_class_entry **pce;
  423. if (zend_lookup_class(class, strlen(class), &pce TSRMLS_CC) == FAILURE) {
  424. return;
  425. }
  426. Z_TYPE_P(object) = IS_OBJECT;
  427. object_init_ex(object, *pce);
  428. Z_SET_REFCOUNT_P(object, 1);
  429. Z_UNSET_ISREF_P(object);
  430. TWIG_CALL_ZZ(object, "__construct", arg0, arg1 TSRMLS_CC);
  431. }
  432. static int twig_add_array_key_to_string(void *pDest APPLY_TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
  433. {
  434. smart_str *buf;
  435. char *joiner;
  436. buf = va_arg(args, smart_str*);
  437. joiner = va_arg(args, char*);
  438. if (buf->len != 0) {
  439. smart_str_appends(buf, joiner);
  440. }
  441. if (hash_key->nKeyLength == 0) {
  442. smart_str_append_long(buf, (long) hash_key->h);
  443. } else {
  444. char *key, *tmp_str;
  445. int key_len, tmp_len;
  446. key = php_addcslashes(hash_key->arKey, hash_key->nKeyLength - 1, &key_len, 0, "'\\", 2 TSRMLS_CC);
  447. tmp_str = php_str_to_str_ex(key, key_len, "\0", 1, "' . \"\\0\" . '", 12, &tmp_len, 0, NULL);
  448. smart_str_appendl(buf, tmp_str, tmp_len);
  449. efree(key);
  450. efree(tmp_str);
  451. }
  452. return 0;
  453. }
  454. char *TWIG_IMPLODE_ARRAY_KEYS(char *joiner, zval *array TSRMLS_DC)
  455. {
  456. smart_str collector = { 0, 0, 0 };
  457. smart_str_appendl(&collector, "", 0);
  458. zend_hash_apply_with_arguments(HASH_OF(array) APPLY_TSRMLS_CC, twig_add_array_key_to_string, 2, &collector, joiner);
  459. smart_str_0(&collector);
  460. return collector.c;
  461. }
  462. static void TWIG_THROW_EXCEPTION(char *exception_name TSRMLS_DC, char *message, ...)
  463. {
  464. char *buffer;
  465. va_list args;
  466. zend_class_entry **pce;
  467. if (zend_lookup_class(exception_name, strlen(exception_name), &pce TSRMLS_CC) == FAILURE)
  468. {
  469. return;
  470. }
  471. va_start(args, message);
  472. vspprintf(&buffer, 0, message, args);
  473. va_end(args);
  474. zend_throw_exception_ex(*pce, 0 TSRMLS_CC, buffer);
  475. }
  476. char *TWIG_GET_CLASS_NAME(zval *object TSRMLS_DC)
  477. {
  478. char *class_name;
  479. zend_uint class_name_len;
  480. if (Z_TYPE_P(object) != IS_OBJECT) {
  481. return "";
  482. }
  483. zend_get_object_classname(object, (const char **) &class_name, &class_name_len TSRMLS_CC);
  484. return class_name;
  485. }
  486. static int twig_add_method_to_class(void *pDest APPLY_TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
  487. {
  488. zval *retval;
  489. char *item;
  490. size_t item_len;
  491. zend_function *mptr = (zend_function *) pDest;
  492. if (!(mptr->common.fn_flags & ZEND_ACC_PUBLIC)) {
  493. return 0;
  494. }
  495. retval = va_arg(args, zval*);
  496. item_len = strlen(mptr->common.function_name);
  497. item = estrndup(mptr->common.function_name, item_len);
  498. php_strtolower(item, item_len);
  499. add_assoc_stringl_ex(retval, item, item_len+1, item, item_len, 0);
  500. return 0;
  501. }
  502. static int twig_add_property_to_class(void *pDest APPLY_TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
  503. {
  504. zend_class_entry *ce;
  505. zval *retval;
  506. char *class_name, *prop_name;
  507. zend_property_info *pptr = (zend_property_info *) pDest;
  508. if (!(pptr->flags & ZEND_ACC_PUBLIC)) {
  509. return 0;
  510. }
  511. ce = *va_arg(args, zend_class_entry**);
  512. retval = va_arg(args, zval*);
  513. zend_unmangle_property_name(pptr->name, pptr->name_length, (const char **) &class_name, (const char **) &prop_name);
  514. add_assoc_string(retval, prop_name, prop_name, 1);
  515. return 0;
  516. }
  517. static void twig_add_class_to_cache(zval *cache, zval *object, char *class_name TSRMLS_DC)
  518. {
  519. zval *class_info, *class_methods, *class_properties;
  520. zend_class_entry *class_ce;
  521. class_ce = zend_get_class_entry(object TSRMLS_CC);
  522. ALLOC_INIT_ZVAL(class_info);
  523. ALLOC_INIT_ZVAL(class_methods);
  524. ALLOC_INIT_ZVAL(class_properties);
  525. array_init(class_info);
  526. array_init(class_methods);
  527. array_init(class_properties);
  528. // add all methods to self::cache[$class]['methods']
  529. zend_hash_apply_with_arguments(&class_ce->function_table APPLY_TSRMLS_CC, twig_add_method_to_class, 1, class_methods);
  530. zend_hash_apply_with_arguments(&class_ce->properties_info APPLY_TSRMLS_CC, twig_add_property_to_class, 2, &class_ce, class_properties);
  531. add_assoc_zval(class_info, "methods", class_methods);
  532. add_assoc_zval(class_info, "properties", class_properties);
  533. add_assoc_zval(cache, class_name, class_info);
  534. }
  535. /* {{{ proto mixed twig_template_get_attributes(TwigTemplate template, mixed object, mixed item, array arguments, string type, boolean isDefinedTest, boolean ignoreStrictCheck)
  536. A C implementation of TwigTemplate::getAttribute() */
  537. PHP_FUNCTION(twig_template_get_attributes)
  538. {
  539. zval *template;
  540. zval *object;
  541. char *item;
  542. int item_len;
  543. zval zitem;
  544. zval *arguments = NULL;
  545. zval *ret = NULL;
  546. char *type = NULL;
  547. int type_len = 0;
  548. zend_bool isDefinedTest = 0;
  549. zend_bool ignoreStrictCheck = 0;
  550. int free_ret = 0;
  551. zval *tmp_self_cache;
  552. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ozs|asbb", &template, &object, &item, &item_len, &arguments, &type, &type_len, &isDefinedTest, &ignoreStrictCheck) == FAILURE) {
  553. return;
  554. }
  555. INIT_PZVAL(&zitem);
  556. ZVAL_STRINGL(&zitem, item, item_len, 0);
  557. switch (is_numeric_string(item, item_len, &Z_LVAL(zitem), &Z_DVAL(zitem), 0)) {
  558. case IS_LONG:
  559. Z_TYPE(zitem) = IS_LONG;
  560. break;
  561. case IS_DOUBLE:
  562. Z_TYPE(zitem) = IS_DOUBLE;
  563. convert_to_long(&zitem);
  564. break;
  565. }
  566. if (!type) {
  567. type = "any";
  568. }
  569. /*
  570. // array
  571. if (Twig_TemplateInterface::METHOD_CALL !== $type) {
  572. if ((is_array($object) && array_key_exists($item, $object))
  573. || ($object instanceof ArrayAccess && isset($object[$item]))
  574. ) {
  575. if ($isDefinedTest) {
  576. return true;
  577. }
  578. return $object[$item];
  579. }
  580. */
  581. if (strcmp("method", type) != 0) {
  582. // printf("XXXmethod: %s\n", type);
  583. if ((TWIG_ARRAY_KEY_EXISTS(object, item, item_len))
  584. || (TWIG_INSTANCE_OF(object, zend_ce_arrayaccess TSRMLS_CC) && TWIG_ISSET_ARRAYOBJECT_ELEMENT(object, &zitem TSRMLS_CC))
  585. ) {
  586. zval *ret;
  587. if (isDefinedTest) {
  588. RETURN_TRUE;
  589. }
  590. ret = TWIG_GET_ARRAY_ELEMENT(object, item, item_len TSRMLS_CC);
  591. if (!ret) {
  592. ret = &EG(uninitialized_zval);
  593. }
  594. RETVAL_ZVAL(ret, 1, 0);
  595. if (free_ret) {
  596. zval_ptr_dtor(&ret);
  597. }
  598. return;
  599. }
  600. /*
  601. if (Twig_TemplateInterface::ARRAY_CALL === $type) {
  602. if ($isDefinedTest) {
  603. return false;
  604. }
  605. if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
  606. return null;
  607. }
  608. */
  609. if (strcmp("array", type) == 0) {
  610. if (isDefinedTest) {
  611. RETURN_FALSE;
  612. }
  613. if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
  614. return;
  615. }
  616. /*
  617. if (is_object($object)) {
  618. throw new Twig_Error_Runtime(sprintf('Key "%s" in object (with ArrayAccess) of type "%s" does not exist', $item, get_class($object)));
  619. // array
  620. } else {
  621. throw new Twig_Error_Runtime(sprintf('Key "%s" for array with keys "%s" does not exist', $item, implode(', ', array_keys($object))));
  622. }
  623. }
  624. }
  625. */
  626. if (Z_TYPE_P(object) == IS_OBJECT) {
  627. TWIG_THROW_EXCEPTION("Twig_Error_Runtime" TSRMLS_CC, "Key \"%s\" in object (with ArrayAccess) of type \"%s\" does not exist", item, TWIG_GET_CLASS_NAME(object TSRMLS_CC));
  628. } else {
  629. TWIG_THROW_EXCEPTION("Twig_Error_Runtime" TSRMLS_CC, "Key \"%s\" for array with keys \"%s\" does not exist", item, TWIG_IMPLODE_ARRAY_KEYS(", ", object TSRMLS_CC));
  630. }
  631. return;
  632. }
  633. }
  634. /*
  635. if (!is_object($object)) {
  636. if ($isDefinedTest) {
  637. return false;
  638. }
  639. */
  640. if (Z_TYPE_P(object) != IS_OBJECT) {
  641. if (isDefinedTest) {
  642. RETURN_FALSE;
  643. }
  644. /*
  645. if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
  646. return null;
  647. }
  648. throw new Twig_Error_Runtime(sprintf('Item "%s" for "%s" does not exist', $item, implode(', ', array_keys($object))));
  649. }
  650. */
  651. if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
  652. RETURN_FALSE;
  653. }
  654. if (Z_TYPE_P(object) == IS_ARRAY) {
  655. TWIG_THROW_EXCEPTION("Twig_Error_Runtime" TSRMLS_CC, "Item \"%s\" for \"Array\" does not exist", item);
  656. } else {
  657. Z_ADDREF_P(object);
  658. convert_to_string_ex(&object);
  659. TWIG_THROW_EXCEPTION("Twig_Error_Runtime" TSRMLS_CC, "Item \"%s\" for \"%s\" does not exist", item, Z_STRVAL_P(object));
  660. zval_ptr_dtor(&object);
  661. }
  662. return;
  663. }
  664. /*
  665. // get some information about the object
  666. $class = get_class($object);
  667. if (!isset(self::$cache[$class])) {
  668. $r = new ReflectionClass($class);
  669. self::$cache[$class] = array('methods' => array(), 'properties' => array());
  670. foreach ($r->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
  671. self::$cache[$class]['methods'][strtolower($method->getName())] = true;
  672. }
  673. foreach ($r->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
  674. self::$cache[$class]['properties'][$property->getName()] = true;
  675. }
  676. }
  677. */
  678. if (Z_TYPE_P(object) == IS_OBJECT) {
  679. char *class_name = NULL;
  680. class_name = TWIG_GET_CLASS_NAME(object TSRMLS_CC);
  681. tmp_self_cache = TWIG_GET_STATIC_PROPERTY(template, "cache" TSRMLS_CC);
  682. if (!TWIG_GET_ARRAY_ELEMENT(tmp_self_cache, class_name, strlen(class_name) TSRMLS_CC)) {
  683. twig_add_class_to_cache(tmp_self_cache, object, class_name TSRMLS_CC);
  684. }
  685. efree(class_name);
  686. }
  687. /*
  688. // object property
  689. if (Twig_TemplateInterface::METHOD_CALL !== $type) {
  690. if (isset(self::$cache[$class]['properties'][$item])
  691. || isset($object->$item) || array_key_exists($item, $object)
  692. ) {
  693. if ($isDefinedTest) {
  694. return true;
  695. }
  696. if ($this->env->hasExtension('sandbox')) {
  697. $this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item);
  698. }
  699. return $object->$item;
  700. }
  701. }
  702. */
  703. if (strcmp("method", type) != 0) {
  704. zval *tmp_class, *tmp_properties, *tmp_item;
  705. char *class_name = NULL;
  706. class_name = TWIG_GET_CLASS_NAME(object TSRMLS_CC);
  707. tmp_class = TWIG_GET_ARRAY_ELEMENT(tmp_self_cache, class_name, strlen(class_name) TSRMLS_CC);
  708. tmp_properties = TWIG_GET_ARRAY_ELEMENT(tmp_class, "properties", strlen("properties") TSRMLS_CC);
  709. tmp_item = TWIG_GET_ARRAY_ELEMENT(tmp_properties, item, item_len TSRMLS_CC);
  710. efree(class_name);
  711. if (tmp_item || TWIG_HAS_PROPERTY(object, &zitem TSRMLS_CC) || TWIG_HAS_DYNAMIC_PROPERTY(object, item, item_len TSRMLS_CC)) {
  712. if (isDefinedTest) {
  713. RETURN_TRUE;
  714. }
  715. if (TWIG_CALL_SB(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "hasExtension", "sandbox" TSRMLS_CC)) {
  716. TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "sandbox" TSRMLS_CC), "checkPropertyAllowed", object, &zitem TSRMLS_CC);
  717. }
  718. if (EG(exception)) {
  719. return;
  720. }
  721. ret = TWIG_PROPERTY(object, &zitem TSRMLS_CC);
  722. RETURN_ZVAL(ret, 1, 0);
  723. }
  724. }
  725. /*
  726. // object method
  727. $lcItem = strtolower($item);
  728. if (isset(self::$cache[$class]['methods'][$lcItem])) {
  729. $method = $item;
  730. } elseif (isset(self::$cache[$class]['methods']['get'.$lcItem])) {
  731. $method = 'get'.$item;
  732. } elseif (isset(self::$cache[$class]['methods']['is'.$lcItem])) {
  733. $method = 'is'.$item;
  734. } elseif (isset(self::$cache[$class]['methods']['__call'])) {
  735. $method = $item;
  736. */
  737. {
  738. char *lcItem = TWIG_STRTOLOWER(item, item_len);
  739. int lcItem_length;
  740. char *method = NULL;
  741. char *tmp_method_name_get;
  742. char *tmp_method_name_is;
  743. zval *tmp_class, *tmp_methods;
  744. char *class_name = NULL;
  745. class_name = TWIG_GET_CLASS_NAME(object TSRMLS_CC);
  746. lcItem_length = strlen(lcItem);
  747. tmp_method_name_get = emalloc(4 + lcItem_length);
  748. tmp_method_name_is = emalloc(3 + lcItem_length);
  749. sprintf(tmp_method_name_get, "get%s", lcItem);
  750. sprintf(tmp_method_name_is, "is%s", lcItem);
  751. tmp_class = TWIG_GET_ARRAY_ELEMENT(tmp_self_cache, class_name, strlen(class_name) TSRMLS_CC);
  752. tmp_methods = TWIG_GET_ARRAY_ELEMENT(tmp_class, "methods", strlen("methods") TSRMLS_CC);
  753. efree(class_name);
  754. if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, lcItem, lcItem_length TSRMLS_CC)) {
  755. method = item;
  756. } else if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, tmp_method_name_get, lcItem_length + 3 TSRMLS_CC)) {
  757. method = tmp_method_name_get;
  758. } else if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, tmp_method_name_is, lcItem_length + 2 TSRMLS_CC)) {
  759. method = tmp_method_name_is;
  760. } else if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, "__call", 6 TSRMLS_CC)) {
  761. method = item;
  762. /*
  763. } else {
  764. if ($isDefinedTest) {
  765. return false;
  766. }
  767. if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
  768. return null;
  769. }
  770. throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist', $item, get_class($object)));
  771. }
  772. if ($isDefinedTest) {
  773. return true;
  774. }
  775. */
  776. } else {
  777. efree(tmp_method_name_get);
  778. efree(tmp_method_name_is);
  779. efree(lcItem);
  780. if (isDefinedTest) {
  781. RETURN_FALSE;
  782. }
  783. if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
  784. return;
  785. }
  786. TWIG_THROW_EXCEPTION("Twig_Error_Runtime" TSRMLS_CC, "Method \"%s\" for object \"%s\" does not exist", item, TWIG_GET_CLASS_NAME(object TSRMLS_CC));
  787. return;
  788. }
  789. if (isDefinedTest) {
  790. efree(tmp_method_name_get);
  791. efree(tmp_method_name_is);
  792. efree(lcItem);
  793. RETURN_TRUE;
  794. }
  795. /*
  796. if ($this->env->hasExtension('sandbox')) {
  797. $this->env->getExtension('sandbox')->checkMethodAllowed($object, $method);
  798. }
  799. */
  800. if (TWIG_CALL_SB(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "hasExtension", "sandbox" TSRMLS_CC)) {
  801. TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "sandbox" TSRMLS_CC), "checkMethodAllowed", object, &zitem TSRMLS_CC);
  802. }
  803. if (EG(exception)) {
  804. efree(tmp_method_name_get);
  805. efree(tmp_method_name_is);
  806. efree(lcItem);
  807. return;
  808. }
  809. /*
  810. $ret = call_user_func_array(array($object, $method), $arguments);
  811. */
  812. if (Z_TYPE_P(object) == IS_OBJECT) {
  813. ret = TWIG_CALL_USER_FUNC_ARRAY(object, method, arguments TSRMLS_CC);
  814. free_ret = 1;
  815. }
  816. efree(tmp_method_name_get);
  817. efree(tmp_method_name_is);
  818. efree(lcItem);
  819. }
  820. /*
  821. if ($object instanceof Twig_TemplateInterface) {
  822. return new Twig_Markup($ret, $this->env->getCharset());
  823. }
  824. */
  825. if (TWIG_INSTANCE_OF_USERLAND(object, "Twig_TemplateInterface" TSRMLS_CC)) {
  826. zval *charset = TWIG_CALL_USER_FUNC_ARRAY(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getCharset", NULL TSRMLS_CC);
  827. TWIG_NEW(return_value, "Twig_Markup", ret, charset TSRMLS_CC);
  828. zval_ptr_dtor(&charset);
  829. if (ret) {
  830. zval_ptr_dtor(&ret);
  831. }
  832. return;
  833. }
  834. /*
  835. return $ret;
  836. */
  837. if (ret) {
  838. RETVAL_ZVAL(ret, 1, 0);
  839. if (free_ret) {
  840. zval_ptr_dtor(&ret);
  841. }
  842. }
  843. }