]> git.immae.eu Git - github/wallabag/wallabag.git/blob - vendor/twig/twig/ext/twig/twig.c
twig implementation
[github/wallabag/wallabag.git] / vendor / twig / twig / ext / twig / twig.c
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
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif
18
19 #include "php.h"
20 #include "php_twig.h"
21 #include "ext/standard/php_string.h"
22 #include "ext/standard/php_smart_str.h"
23
24 #include "Zend/zend_object_handlers.h"
25 #include "Zend/zend_interfaces.h"
26 #include "Zend/zend_exceptions.h"
27
28 #ifndef Z_ADDREF_P
29 #define Z_ADDREF_P(pz) (pz)->refcount++
30 #endif
31
32 #define FREE_DTOR(z) \
33 zval_dtor(z); \
34 efree(z);
35
36 #if PHP_VERSION_ID >= 50300
37 #define APPLY_TSRMLS_DC TSRMLS_DC
38 #define APPLY_TSRMLS_CC TSRMLS_CC
39 #define APPLY_TSRMLS_FETCH()
40 #else
41 #define APPLY_TSRMLS_DC
42 #define APPLY_TSRMLS_CC
43 #define APPLY_TSRMLS_FETCH() TSRMLS_FETCH()
44 #endif
45
46 ZEND_BEGIN_ARG_INFO_EX(twig_template_get_attribute_args, ZEND_SEND_BY_VAL, ZEND_RETURN_VALUE, 6)
47 ZEND_ARG_INFO(0, template)
48 ZEND_ARG_INFO(0, object)
49 ZEND_ARG_INFO(0, item)
50 ZEND_ARG_INFO(0, arguments)
51 ZEND_ARG_INFO(0, type)
52 ZEND_ARG_INFO(0, isDefinedTest)
53 ZEND_END_ARG_INFO()
54
55 zend_function_entry twig_functions[] = {
56 PHP_FE(twig_template_get_attributes, twig_template_get_attribute_args)
57 {NULL, NULL, NULL}
58 };
59
60
61 zend_module_entry twig_module_entry = {
62 STANDARD_MODULE_HEADER,
63 "twig",
64 twig_functions,
65 NULL,
66 NULL,
67 NULL,
68 NULL,
69 NULL,
70 PHP_TWIG_VERSION,
71 STANDARD_MODULE_PROPERTIES
72 };
73
74
75 #ifdef COMPILE_DL_TWIG
76 ZEND_GET_MODULE(twig)
77 #endif
78
79 int TWIG_ARRAY_KEY_EXISTS(zval *array, zval *key)
80 {
81 zval temp;
82 int result;
83
84 if (Z_TYPE_P(array) != IS_ARRAY) {
85 return 0;
86 }
87
88 switch (Z_TYPE_P(key)) {
89 case IS_NULL:
90 return zend_hash_exists(Z_ARRVAL_P(array), "", 1);
91
92 case IS_BOOL:
93 case IS_DOUBLE:
94 convert_to_long(key);
95 case IS_LONG:
96 return zend_hash_index_exists(Z_ARRVAL_P(array), Z_LVAL_P(key));
97
98 default:
99 convert_to_string(key);
100 return zend_symtable_exists(Z_ARRVAL_P(array), Z_STRVAL_P(key), Z_STRLEN_P(key) + 1);
101 }
102 }
103
104 int TWIG_INSTANCE_OF(zval *object, zend_class_entry *interface TSRMLS_DC)
105 {
106 if (Z_TYPE_P(object) != IS_OBJECT) {
107 return 0;
108 }
109 return instanceof_function(Z_OBJCE_P(object), interface TSRMLS_CC);
110 }
111
112 int TWIG_INSTANCE_OF_USERLAND(zval *object, char *interface TSRMLS_DC)
113 {
114 zend_class_entry **pce;
115 if (Z_TYPE_P(object) != IS_OBJECT) {
116 return 0;
117 }
118 if (zend_lookup_class(interface, strlen(interface), &pce TSRMLS_CC) == FAILURE) {
119 return 0;
120 }
121 return instanceof_function(Z_OBJCE_P(object), *pce TSRMLS_CC);
122 }
123
124 zval *TWIG_GET_ARRAYOBJECT_ELEMENT(zval *object, zval *offset TSRMLS_DC)
125 {
126 zend_class_entry *ce = Z_OBJCE_P(object);
127 zval *retval;
128
129 if (Z_TYPE_P(object) == IS_OBJECT) {
130 SEPARATE_ARG_IF_REF(offset);
131 zend_call_method_with_1_params(&object, ce, NULL, "offsetget", &retval, offset);
132
133 zval_ptr_dtor(&offset);
134
135 if (!retval) {
136 if (!EG(exception)) {
137 zend_error(E_ERROR, "Undefined offset for object of type %s used as array", ce->name);
138 }
139 return NULL;
140 }
141
142 return retval;
143 }
144 return NULL;
145 }
146
147 int TWIG_ISSET_ARRAYOBJECT_ELEMENT(zval *object, zval *offset TSRMLS_DC)
148 {
149 zend_class_entry *ce = Z_OBJCE_P(object);
150 zval *retval;
151
152 if (Z_TYPE_P(object) == IS_OBJECT) {
153 SEPARATE_ARG_IF_REF(offset);
154 zend_call_method_with_1_params(&object, ce, NULL, "offsetexists", &retval, offset);
155
156 zval_ptr_dtor(&offset);
157
158 if (!retval) {
159 if (!EG(exception)) {
160 zend_error(E_ERROR, "Undefined offset for object of type %s used as array", ce->name);
161 }
162 return 0;
163 }
164
165 return (retval && Z_TYPE_P(retval) == IS_BOOL && Z_LVAL_P(retval));
166 }
167 return 0;
168 }
169
170 char *TWIG_STRTOLOWER(const char *str, int str_len)
171 {
172 char *item_dup;
173
174 item_dup = estrndup(str, str_len);
175 php_strtolower(item_dup, str_len);
176 return item_dup;
177 }
178
179 zval *TWIG_CALL_USER_FUNC_ARRAY(zval *object, char *function, zval *arguments TSRMLS_DC)
180 {
181 zend_fcall_info fci;
182 zval ***args = NULL;
183 int arg_count = 0;
184 HashTable *table;
185 HashPosition pos;
186 int i = 0;
187 zval *retval_ptr;
188 zval *zfunction;
189
190 if (arguments) {
191 table = HASH_OF(arguments);
192 args = safe_emalloc(sizeof(zval **), table->nNumOfElements, 0);
193
194 zend_hash_internal_pointer_reset_ex(table, &pos);
195
196 while (zend_hash_get_current_data_ex(table, (void **)&args[i], &pos) == SUCCESS) {
197 i++;
198 zend_hash_move_forward_ex(table, &pos);
199 }
200 arg_count = table->nNumOfElements;
201 }
202
203 MAKE_STD_ZVAL(zfunction);
204 ZVAL_STRING(zfunction, function, 1);
205 fci.size = sizeof(fci);
206 fci.function_table = EG(function_table);
207 fci.function_name = zfunction;
208 fci.symbol_table = NULL;
209 #if PHP_VERSION_ID >= 50300
210 fci.object_ptr = object;
211 #else
212 fci.object_pp = &object;
213 #endif
214 fci.retval_ptr_ptr = &retval_ptr;
215 fci.param_count = arg_count;
216 fci.params = args;
217 fci.no_separation = 0;
218
219 if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
220 ALLOC_INIT_ZVAL(retval_ptr);
221 ZVAL_BOOL(retval_ptr, 0);
222 }
223
224 if (args) {
225 efree(fci.params);
226 }
227 FREE_DTOR(zfunction);
228 return retval_ptr;
229 }
230
231 int TWIG_CALL_BOOLEAN(zval *object, char *functionName TSRMLS_DC)
232 {
233 zval *ret;
234 int res;
235
236 ret = TWIG_CALL_USER_FUNC_ARRAY(object, functionName, NULL TSRMLS_CC);
237 res = Z_LVAL_P(ret);
238 zval_ptr_dtor(&ret);
239 return res;
240 }
241
242 zval *TWIG_GET_STATIC_PROPERTY(zval *class, char *prop_name TSRMLS_DC)
243 {
244 zval **tmp_zval;
245 zend_class_entry *ce;
246
247 if (class == NULL || Z_TYPE_P(class) != IS_OBJECT) {
248 return NULL;
249 }
250
251 ce = zend_get_class_entry(class TSRMLS_CC);
252 #if PHP_VERSION_ID >= 50400
253 tmp_zval = zend_std_get_static_property(ce, prop_name, strlen(prop_name), 0, NULL TSRMLS_CC);
254 #else
255 tmp_zval = zend_std_get_static_property(ce, prop_name, strlen(prop_name), 0 TSRMLS_CC);
256 #endif
257 return *tmp_zval;
258 }
259
260 zval *TWIG_GET_ARRAY_ELEMENT_ZVAL(zval *class, zval *prop_name TSRMLS_DC)
261 {
262 zval **tmp_zval;
263 char *tmp_name;
264
265 if (class == NULL || Z_TYPE_P(class) != IS_ARRAY) {
266 if (class != NULL && Z_TYPE_P(class) == IS_OBJECT && TWIG_INSTANCE_OF(class, zend_ce_arrayaccess TSRMLS_CC)) {
267 // array access object
268 return TWIG_GET_ARRAYOBJECT_ELEMENT(class, prop_name TSRMLS_CC);
269 }
270 return NULL;
271 }
272
273 switch(Z_TYPE_P(prop_name)) {
274 case IS_NULL:
275 zend_hash_find(HASH_OF(class), "", 1, (void**) &tmp_zval);
276 return *tmp_zval;
277
278 case IS_BOOL:
279 case IS_DOUBLE:
280 convert_to_long(prop_name);
281 case IS_LONG:
282 zend_hash_index_find(HASH_OF(class), Z_LVAL_P(prop_name), (void **) &tmp_zval);
283 return *tmp_zval;
284
285 case IS_STRING:
286 zend_symtable_find(HASH_OF(class), Z_STRVAL_P(prop_name), Z_STRLEN_P(prop_name) + 1, (void**) &tmp_zval);
287 return *tmp_zval;
288 }
289
290 return NULL;
291 }
292
293 zval *TWIG_GET_ARRAY_ELEMENT(zval *class, char *prop_name, int prop_name_length TSRMLS_DC)
294 {
295 zval **tmp_zval;
296
297 if (class == NULL/* || Z_TYPE_P(class) != IS_ARRAY*/) {
298 return NULL;
299 }
300
301 if (class != NULL && Z_TYPE_P(class) == IS_OBJECT && TWIG_INSTANCE_OF(class, zend_ce_arrayaccess TSRMLS_CC)) {
302 // array access object
303 zval *tmp_name_zval;
304 zval *tmp_ret_zval;
305
306 ALLOC_INIT_ZVAL(tmp_name_zval);
307 ZVAL_STRING(tmp_name_zval, prop_name, 1);
308 tmp_ret_zval = TWIG_GET_ARRAYOBJECT_ELEMENT(class, tmp_name_zval TSRMLS_CC);
309 FREE_DTOR(tmp_name_zval);
310 return tmp_ret_zval;
311 }
312
313 if (zend_symtable_find(HASH_OF(class), prop_name, prop_name_length+1, (void**)&tmp_zval) == SUCCESS) {
314 return *tmp_zval;
315 }
316 return NULL;
317 }
318
319 zval *TWIG_PROPERTY(zval *object, zval *propname TSRMLS_DC)
320 {
321 zval *tmp = NULL;
322
323 if (Z_OBJ_HT_P(object)->read_property) {
324 #if PHP_VERSION_ID >= 50400
325 tmp = Z_OBJ_HT_P(object)->read_property(object, propname, BP_VAR_IS, NULL TSRMLS_CC);
326 #else
327 tmp = Z_OBJ_HT_P(object)->read_property(object, propname, BP_VAR_IS TSRMLS_CC);
328 #endif
329 if (tmp != EG(uninitialized_zval_ptr)) {
330 return tmp;
331 } else {
332 return NULL;
333 }
334 }
335 return tmp;
336 }
337
338 int TWIG_HAS_PROPERTY(zval *object, zval *propname TSRMLS_DC)
339 {
340 if (Z_OBJ_HT_P(object)->has_property) {
341 #if PHP_VERSION_ID >= 50400
342 return Z_OBJ_HT_P(object)->has_property(object, propname, 0, NULL TSRMLS_CC);
343 #else
344 return Z_OBJ_HT_P(object)->has_property(object, propname, 0 TSRMLS_CC);
345 #endif
346 }
347 return 0;
348 }
349
350 int TWIG_HAS_DYNAMIC_PROPERTY(zval *object, char *prop, int prop_len TSRMLS_DC)
351 {
352 if (Z_OBJ_HT_P(object)->get_properties) {
353 return zend_hash_quick_exists(
354 Z_OBJ_HT_P(object)->get_properties(object TSRMLS_CC), // the properties hash
355 prop, // property name
356 prop_len + 1, // property length
357 zend_get_hash_value(prop, prop_len + 1) // hash value
358 );
359 }
360 return 0;
361 }
362
363 zval *TWIG_PROPERTY_CHAR(zval *object, char *propname TSRMLS_DC)
364 {
365 zval *tmp_name_zval, *tmp;
366
367 ALLOC_INIT_ZVAL(tmp_name_zval);
368 ZVAL_STRING(tmp_name_zval, propname, 1);
369 tmp = TWIG_PROPERTY(object, tmp_name_zval TSRMLS_CC);
370 FREE_DTOR(tmp_name_zval);
371 return tmp;
372 }
373
374 int TWIG_CALL_B_0(zval *object, char *method)
375 {
376 return 0;
377 }
378
379 zval *TWIG_CALL_S(zval *object, char *method, char *arg0 TSRMLS_DC)
380 {
381 zend_fcall_info fci;
382 zval **args[1];
383 zval *argument;
384 zval *zfunction;
385 zval *retval_ptr;
386
387 MAKE_STD_ZVAL(argument);
388 ZVAL_STRING(argument, arg0, 1);
389 args[0] = &argument;
390
391 MAKE_STD_ZVAL(zfunction);
392 ZVAL_STRING(zfunction, method, 1);
393 fci.size = sizeof(fci);
394 fci.function_table = EG(function_table);
395 fci.function_name = zfunction;
396 fci.symbol_table = NULL;
397 #if PHP_VERSION_ID >= 50300
398 fci.object_ptr = object;
399 #else
400 fci.object_pp = &object;
401 #endif
402 fci.retval_ptr_ptr = &retval_ptr;
403 fci.param_count = 1;
404 fci.params = args;
405 fci.no_separation = 0;
406
407 if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
408 FREE_DTOR(zfunction);
409 zval_ptr_dtor(&argument);
410 return 0;
411 }
412 FREE_DTOR(zfunction);
413 zval_ptr_dtor(&argument);
414 return retval_ptr;
415 }
416
417 int TWIG_CALL_SB(zval *object, char *method, char *arg0 TSRMLS_DC)
418 {
419 zval *retval_ptr;
420 int success;
421
422 retval_ptr = TWIG_CALL_S(object, method, arg0 TSRMLS_CC);
423 success = (retval_ptr && (Z_TYPE_P(retval_ptr) == IS_BOOL) && Z_LVAL_P(retval_ptr));
424
425 if (retval_ptr) {
426 zval_ptr_dtor(&retval_ptr);
427 }
428
429 return success;
430 }
431
432 int TWIG_CALL_Z(zval *object, char *method, zval *arg1 TSRMLS_DC)
433 {
434 zend_fcall_info fci;
435 zval **args[1];
436 zval *zfunction;
437 zval *retval_ptr;
438 int success;
439
440 args[0] = &arg1;
441
442 MAKE_STD_ZVAL(zfunction);
443 ZVAL_STRING(zfunction, method, 1);
444 fci.size = sizeof(fci);
445 fci.function_table = EG(function_table);
446 fci.function_name = zfunction;
447 fci.symbol_table = NULL;
448 #if PHP_VERSION_ID >= 50300
449 fci.object_ptr = object;
450 #else
451 fci.object_pp = &object;
452 #endif
453 fci.retval_ptr_ptr = &retval_ptr;
454 fci.param_count = 1;
455 fci.params = args;
456 fci.no_separation = 0;
457
458 if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
459 FREE_DTOR(zfunction);
460 if (retval_ptr) {
461 zval_ptr_dtor(&retval_ptr);
462 }
463 return 0;
464 }
465
466 FREE_DTOR(zfunction);
467
468 success = (retval_ptr && (Z_TYPE_P(retval_ptr) == IS_BOOL) && Z_LVAL_P(retval_ptr));
469 if (retval_ptr) {
470 zval_ptr_dtor(&retval_ptr);
471 }
472
473 return success;
474 }
475
476 int TWIG_CALL_ZZ(zval *object, char *method, zval *arg1, zval *arg2 TSRMLS_DC)
477 {
478 zend_fcall_info fci;
479 zval **args[2];
480 zval *zfunction;
481 zval *retval_ptr;
482 int success;
483
484 args[0] = &arg1;
485 args[1] = &arg2;
486
487 MAKE_STD_ZVAL(zfunction);
488 ZVAL_STRING(zfunction, method, 1);
489 fci.size = sizeof(fci);
490 fci.function_table = EG(function_table);
491 fci.function_name = zfunction;
492 fci.symbol_table = NULL;
493 #if PHP_VERSION_ID >= 50300
494 fci.object_ptr = object;
495 #else
496 fci.object_pp = &object;
497 #endif
498 fci.retval_ptr_ptr = &retval_ptr;
499 fci.param_count = 2;
500 fci.params = args;
501 fci.no_separation = 0;
502
503 if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
504 FREE_DTOR(zfunction);
505 return 0;
506 }
507
508 FREE_DTOR(zfunction);
509
510 success = (retval_ptr && (Z_TYPE_P(retval_ptr) == IS_BOOL) && Z_LVAL_P(retval_ptr));
511 if (retval_ptr) {
512 zval_ptr_dtor(&retval_ptr);
513 }
514
515 return success;
516 }
517
518 #ifndef Z_SET_REFCOUNT_P
519 # define Z_SET_REFCOUNT_P(pz, rc) pz->refcount = rc
520 # define Z_UNSET_ISREF_P(pz) pz->is_ref = 0
521 #endif
522
523 void TWIG_NEW(zval *object, char *class, zval *arg0, zval *arg1 TSRMLS_DC)
524 {
525 zend_class_entry **pce;
526
527 if (zend_lookup_class(class, strlen(class), &pce TSRMLS_CC) == FAILURE) {
528 return;
529 }
530
531 Z_TYPE_P(object) = IS_OBJECT;
532 object_init_ex(object, *pce);
533 Z_SET_REFCOUNT_P(object, 1);
534 Z_UNSET_ISREF_P(object);
535
536 TWIG_CALL_ZZ(object, "__construct", arg0, arg1 TSRMLS_CC);
537 }
538
539 static int twig_add_array_key_to_string(void *pDest APPLY_TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
540 {
541 smart_str *buf;
542 char *joiner;
543 APPLY_TSRMLS_FETCH();
544
545 buf = va_arg(args, smart_str*);
546 joiner = va_arg(args, char*);
547
548 if (buf->len != 0) {
549 smart_str_appends(buf, joiner);
550 }
551
552 if (hash_key->nKeyLength == 0) {
553 smart_str_append_long(buf, (long) hash_key->h);
554 } else {
555 char *key, *tmp_str;
556 int key_len, tmp_len;
557 key = php_addcslashes(hash_key->arKey, hash_key->nKeyLength - 1, &key_len, 0, "'\\", 2 TSRMLS_CC);
558 tmp_str = php_str_to_str_ex(key, key_len, "\0", 1, "' . \"\\0\" . '", 12, &tmp_len, 0, NULL);
559
560 smart_str_appendl(buf, tmp_str, tmp_len);
561 efree(key);
562 efree(tmp_str);
563 }
564
565 return 0;
566 }
567
568 char *TWIG_IMPLODE_ARRAY_KEYS(char *joiner, zval *array TSRMLS_DC)
569 {
570 smart_str collector = { 0, 0, 0 };
571
572 smart_str_appendl(&collector, "", 0);
573 zend_hash_apply_with_arguments(HASH_OF(array) APPLY_TSRMLS_CC, twig_add_array_key_to_string, 2, &collector, joiner);
574 smart_str_0(&collector);
575
576 return collector.c;
577 }
578
579 static void TWIG_THROW_EXCEPTION(char *exception_name TSRMLS_DC, char *message, ...)
580 {
581 char *buffer;
582 va_list args;
583 zend_class_entry **pce;
584
585 if (zend_lookup_class(exception_name, strlen(exception_name), &pce TSRMLS_CC) == FAILURE) {
586 return;
587 }
588
589 va_start(args, message);
590 vspprintf(&buffer, 0, message, args);
591 va_end(args);
592
593 zend_throw_exception_ex(*pce, 0 TSRMLS_CC, buffer);
594 efree(buffer);
595 }
596
597 static void TWIG_RUNTIME_ERROR(zval *template TSRMLS_DC, char *message, ...)
598 {
599 char *buffer;
600 va_list args;
601 zend_class_entry **pce;
602 zval *ex;
603 zval *constructor;
604 zval *zmessage;
605 zval *lineno;
606 zval *filename_func;
607 zval *filename;
608 zval *constructor_args[3];
609 zval *constructor_retval;
610
611 if (zend_lookup_class("Twig_Error_Runtime", strlen("Twig_Error_Runtime"), &pce TSRMLS_CC) == FAILURE) {
612 return;
613 }
614
615 va_start(args, message);
616 vspprintf(&buffer, 0, message, args);
617 va_end(args);
618
619 MAKE_STD_ZVAL(ex);
620 object_init_ex(ex, *pce);
621
622 // Call Twig_Error constructor
623 MAKE_STD_ZVAL(constructor);
624 MAKE_STD_ZVAL(zmessage);
625 MAKE_STD_ZVAL(lineno);
626 MAKE_STD_ZVAL(filename);
627 MAKE_STD_ZVAL(filename_func);
628 MAKE_STD_ZVAL(constructor_retval);
629
630 ZVAL_STRINGL(constructor, "__construct", sizeof("__construct")-1, 1);
631 ZVAL_STRING(zmessage, buffer, 1);
632 ZVAL_LONG(lineno, -1);
633
634 // Get template filename
635 ZVAL_STRINGL(filename_func, "getTemplateName", sizeof("getTemplateName")-1, 1);
636 call_user_function(EG(function_table), &template, filename_func, filename, 0, 0 TSRMLS_CC);
637
638 constructor_args[0] = zmessage;
639 constructor_args[1] = lineno;
640 constructor_args[2] = filename;
641 call_user_function(EG(function_table), &ex, constructor, constructor_retval, 3, constructor_args TSRMLS_CC);
642
643 zval_ptr_dtor(&constructor_retval);
644 zval_ptr_dtor(&zmessage);
645 zval_ptr_dtor(&lineno);
646 zval_ptr_dtor(&filename);
647 FREE_DTOR(constructor);
648 FREE_DTOR(filename_func);
649 efree(buffer);
650
651 zend_throw_exception_object(ex TSRMLS_CC);
652 }
653
654 static char *TWIG_GET_CLASS_NAME(zval *object TSRMLS_DC)
655 {
656 char *class_name;
657 zend_uint class_name_len;
658
659 if (Z_TYPE_P(object) != IS_OBJECT) {
660 return "";
661 }
662 #if PHP_API_VERSION >= 20100412
663 zend_get_object_classname(object, (const char **) &class_name, &class_name_len TSRMLS_CC);
664 #else
665 zend_get_object_classname(object, &class_name, &class_name_len TSRMLS_CC);
666 #endif
667 return class_name;
668 }
669
670 static int twig_add_method_to_class(void *pDest APPLY_TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
671 {
672 zval *retval;
673 char *item;
674 size_t item_len;
675 zend_function *mptr = (zend_function *) pDest;
676 APPLY_TSRMLS_FETCH();
677
678 if (!(mptr->common.fn_flags & ZEND_ACC_PUBLIC)) {
679 return 0;
680 }
681
682 retval = va_arg(args, zval*);
683
684 item_len = strlen(mptr->common.function_name);
685 item = estrndup(mptr->common.function_name, item_len);
686 php_strtolower(item, item_len);
687
688 add_assoc_stringl_ex(retval, item, item_len+1, item, item_len, 0);
689
690 return 0;
691 }
692
693 static int twig_add_property_to_class(void *pDest APPLY_TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
694 {
695 zend_class_entry *ce;
696 zval *retval;
697 char *class_name, *prop_name;
698 zend_property_info *pptr = (zend_property_info *) pDest;
699 APPLY_TSRMLS_FETCH();
700
701 if (!(pptr->flags & ZEND_ACC_PUBLIC)) {
702 return 0;
703 }
704
705 ce = *va_arg(args, zend_class_entry**);
706 retval = va_arg(args, zval*);
707
708 #if PHP_API_VERSION >= 20100412
709 zend_unmangle_property_name(pptr->name, pptr->name_length, (const char **) &class_name, (const char **) &prop_name);
710 #else
711 zend_unmangle_property_name(pptr->name, pptr->name_length, &class_name, &prop_name);
712 #endif
713
714 add_assoc_string(retval, prop_name, prop_name, 1);
715
716 return 0;
717 }
718
719 static void twig_add_class_to_cache(zval *cache, zval *object, char *class_name TSRMLS_DC)
720 {
721 zval *class_info, *class_methods, *class_properties;
722 zend_class_entry *class_ce;
723
724 class_ce = zend_get_class_entry(object TSRMLS_CC);
725
726 ALLOC_INIT_ZVAL(class_info);
727 ALLOC_INIT_ZVAL(class_methods);
728 ALLOC_INIT_ZVAL(class_properties);
729 array_init(class_info);
730 array_init(class_methods);
731 array_init(class_properties);
732 // add all methods to self::cache[$class]['methods']
733 zend_hash_apply_with_arguments(&class_ce->function_table APPLY_TSRMLS_CC, twig_add_method_to_class, 1, class_methods);
734 zend_hash_apply_with_arguments(&class_ce->properties_info APPLY_TSRMLS_CC, twig_add_property_to_class, 2, &class_ce, class_properties);
735
736 add_assoc_zval(class_info, "methods", class_methods);
737 add_assoc_zval(class_info, "properties", class_properties);
738 add_assoc_zval(cache, class_name, class_info);
739 }
740
741 /* {{{ proto mixed twig_template_get_attributes(TwigTemplate template, mixed object, mixed item, array arguments, string type, boolean isDefinedTest, boolean ignoreStrictCheck)
742 A C implementation of TwigTemplate::getAttribute() */
743 PHP_FUNCTION(twig_template_get_attributes)
744 {
745 zval *template;
746 zval *object;
747 char *item;
748 int item_len;
749 zval *zitem, ztmpitem;
750 zval *arguments = NULL;
751 zval *ret = NULL;
752 char *type = NULL;
753 int type_len = 0;
754 zend_bool isDefinedTest = 0;
755 zend_bool ignoreStrictCheck = 0;
756 int free_ret = 0;
757 zval *tmp_self_cache;
758
759
760 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ozz|asbb", &template, &object, &zitem, &arguments, &type, &type_len, &isDefinedTest, &ignoreStrictCheck) == FAILURE) {
761 return;
762 }
763
764 // convert the item to a string
765 ztmpitem = *zitem;
766 zval_copy_ctor(&ztmpitem);
767 convert_to_string(&ztmpitem);
768 item_len = Z_STRLEN(ztmpitem);
769 item = estrndup(Z_STRVAL(ztmpitem), item_len);
770 zval_dtor(&ztmpitem);
771
772 if (!type) {
773 type = "any";
774 }
775
776 /*
777 // array
778 if (Twig_TemplateInterface::METHOD_CALL !== $type) {
779 $arrayItem = is_bool($item) || is_float($item) ? (int) $item : $item;
780
781 if ((is_array($object) && array_key_exists($arrayItem, $object))
782 || ($object instanceof ArrayAccess && isset($object[$arrayItem]))
783 ) {
784 if ($isDefinedTest) {
785 return true;
786 }
787
788 return $object[$arrayItem];
789 }
790 */
791
792
793 if (strcmp("method", type) != 0) {
794 if ((TWIG_ARRAY_KEY_EXISTS(object, zitem))
795 || (TWIG_INSTANCE_OF(object, zend_ce_arrayaccess TSRMLS_CC) && TWIG_ISSET_ARRAYOBJECT_ELEMENT(object, zitem TSRMLS_CC))
796 ) {
797
798 if (isDefinedTest) {
799 RETURN_TRUE;
800 }
801
802 ret = TWIG_GET_ARRAY_ELEMENT_ZVAL(object, zitem TSRMLS_CC);
803
804 if (!ret) {
805 ret = &EG(uninitialized_zval);
806 }
807 RETVAL_ZVAL(ret, 1, 0);
808 if (free_ret) {
809 zval_ptr_dtor(&ret);
810 }
811 return;
812 }
813 /*
814 if (Twig_TemplateInterface::ARRAY_CALL === $type) {
815 if ($isDefinedTest) {
816 return false;
817 }
818 if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
819 return null;
820 }
821 */
822 if (strcmp("array", type) == 0 || Z_TYPE_P(object) != IS_OBJECT) {
823 if (isDefinedTest) {
824 RETURN_FALSE;
825 }
826 if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
827 return;
828 }
829 /*
830 if (is_object($object)) {
831 throw new Twig_Error_Runtime(sprintf('Key "%s" in object (with ArrayAccess) of type "%s" does not exist', $arrayItem, get_class($object)), -1, $this->getTemplateName());
832 } elseif (is_array($object)) {
833 throw new Twig_Error_Runtime(sprintf('Key "%s" for array with keys "%s" does not exist', $arrayItem, implode(', ', array_keys($object))), -1, $this->getTemplateName());
834 } elseif (Twig_TemplateInterface::ARRAY_CALL === $type) {
835 throw new Twig_Error_Runtime(sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
836 } else {
837 throw new Twig_Error_Runtime(sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
838 }
839 }
840 }
841 */
842 if (Z_TYPE_P(object) == IS_OBJECT) {
843 TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" in object (with ArrayAccess) of type \"%s\" does not exist", item, TWIG_GET_CLASS_NAME(object TSRMLS_CC));
844 } else if (Z_TYPE_P(object) == IS_ARRAY) {
845 TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" for array with keys \"%s\" does not exist", item, TWIG_IMPLODE_ARRAY_KEYS(", ", object TSRMLS_CC));
846 } else {
847 char *type_name = zend_zval_type_name(object);
848 Z_ADDREF_P(object);
849 convert_to_string(object);
850 TWIG_RUNTIME_ERROR(template TSRMLS_CC,
851 (strcmp("array", type) == 0)
852 ? "Impossible to access a key (\"%s\") on a %s variable (\"%s\")"
853 : "Impossible to access an attribute (\"%s\") on a %s variable (\"%s\")",
854 item, type_name, Z_STRVAL_P(object));
855 zval_ptr_dtor(&object);
856 }
857 return;
858 }
859 }
860
861 /*
862 if (!is_object($object)) {
863 if ($isDefinedTest) {
864 return false;
865 }
866 */
867
868 if (Z_TYPE_P(object) != IS_OBJECT) {
869 if (isDefinedTest) {
870 RETURN_FALSE;
871 }
872 /*
873 if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
874 return null;
875 }
876 throw new Twig_Error_Runtime(sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
877 }
878 */
879 if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
880 return;
881 }
882
883 char *type_name = zend_zval_type_name(object);
884 Z_ADDREF_P(object);
885 convert_to_string_ex(&object);
886
887 TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to invoke a method (\"%s\") on a %s variable (\"%s\")", item, type_name, Z_STRVAL_P(object));
888
889 zval_ptr_dtor(&object);
890
891 return;
892 }
893 /*
894 $class = get_class($object);
895 */
896 char *class_name = NULL;
897 zval *tmp_class;
898
899 class_name = TWIG_GET_CLASS_NAME(object TSRMLS_CC);
900 tmp_self_cache = TWIG_GET_STATIC_PROPERTY(template, "cache" TSRMLS_CC);
901 tmp_class = TWIG_GET_ARRAY_ELEMENT(tmp_self_cache, class_name, strlen(class_name) TSRMLS_CC);
902
903 if (!tmp_class) {
904 twig_add_class_to_cache(tmp_self_cache, object, class_name TSRMLS_CC);
905 tmp_class = TWIG_GET_ARRAY_ELEMENT(tmp_self_cache, class_name, strlen(class_name) TSRMLS_CC);
906 }
907 efree(class_name);
908
909 /*
910 // object property
911 if (Twig_TemplateInterface::METHOD_CALL !== $type) {
912 if (isset($object->$item) || array_key_exists((string) $item, $object)) {
913 if ($isDefinedTest) {
914 return true;
915 }
916
917 if ($this->env->hasExtension('sandbox')) {
918 $this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item);
919 }
920
921 return $object->$item;
922 }
923 }
924 */
925 if (strcmp("method", type) != 0) {
926 zval *tmp_properties, *tmp_item;
927
928 tmp_properties = TWIG_GET_ARRAY_ELEMENT(tmp_class, "properties", strlen("properties") TSRMLS_CC);
929 tmp_item = TWIG_GET_ARRAY_ELEMENT(tmp_properties, item, item_len TSRMLS_CC);
930
931 if (tmp_item || TWIG_HAS_PROPERTY(object, zitem TSRMLS_CC) || TWIG_HAS_DYNAMIC_PROPERTY(object, item, item_len TSRMLS_CC)) {
932 if (isDefinedTest) {
933 RETURN_TRUE;
934 }
935 if (TWIG_CALL_SB(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "hasExtension", "sandbox" TSRMLS_CC)) {
936 TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "sandbox" TSRMLS_CC), "checkPropertyAllowed", object, zitem TSRMLS_CC);
937 }
938 if (EG(exception)) {
939 return;
940 }
941
942 ret = TWIG_PROPERTY(object, zitem TSRMLS_CC);
943 RETURN_ZVAL(ret, 1, 0);
944 }
945 }
946 /*
947 // object method
948 if (!isset(self::$cache[$class]['methods'])) {
949 self::$cache[$class]['methods'] = array_change_key_case(array_flip(get_class_methods($object)));
950 }
951
952 $lcItem = strtolower($item);
953 if (isset(self::$cache[$class]['methods'][$lcItem])) {
954 $method = (string) $item;
955 } elseif (isset(self::$cache[$class]['methods']['get'.$lcItem])) {
956 $method = 'get'.$item;
957 } elseif (isset(self::$cache[$class]['methods']['is'.$lcItem])) {
958 $method = 'is'.$item;
959 } elseif (isset(self::$cache[$class]['methods']['__call'])) {
960 $method = (string) $item;
961 */
962 {
963 char *lcItem = TWIG_STRTOLOWER(item, item_len);
964 int lcItem_length;
965 char *method = NULL;
966 char *tmp_method_name_get;
967 char *tmp_method_name_is;
968 zval *tmp_methods;
969
970 lcItem_length = strlen(lcItem);
971 tmp_method_name_get = emalloc(4 + lcItem_length);
972 tmp_method_name_is = emalloc(3 + lcItem_length);
973
974 sprintf(tmp_method_name_get, "get%s", lcItem);
975 sprintf(tmp_method_name_is, "is%s", lcItem);
976
977 tmp_methods = TWIG_GET_ARRAY_ELEMENT(tmp_class, "methods", strlen("methods") TSRMLS_CC);
978
979 if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, lcItem, lcItem_length TSRMLS_CC)) {
980 method = item;
981 } else if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, tmp_method_name_get, lcItem_length + 3 TSRMLS_CC)) {
982 method = tmp_method_name_get;
983 } else if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, tmp_method_name_is, lcItem_length + 2 TSRMLS_CC)) {
984 method = tmp_method_name_is;
985 } else if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, "__call", 6 TSRMLS_CC)) {
986 method = item;
987 /*
988 } else {
989 if ($isDefinedTest) {
990 return false;
991 }
992
993 if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
994 return null;
995 }
996
997 throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist', $item, get_class($object)), -1, $this->getTemplateName());
998 }
999
1000 if ($isDefinedTest) {
1001 return true;
1002 }
1003 */
1004 } else {
1005 efree(tmp_method_name_get);
1006 efree(tmp_method_name_is);
1007 efree(lcItem);
1008
1009 if (isDefinedTest) {
1010 RETURN_FALSE;
1011 }
1012 if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
1013 return;
1014 }
1015 TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Method \"%s\" for object \"%s\" does not exist", item, TWIG_GET_CLASS_NAME(object TSRMLS_CC));
1016 return;
1017 }
1018
1019 if (isDefinedTest) {
1020 efree(tmp_method_name_get);
1021 efree(tmp_method_name_is);
1022 efree(lcItem);
1023 RETURN_TRUE;
1024 }
1025 /*
1026 if ($this->env->hasExtension('sandbox')) {
1027 $this->env->getExtension('sandbox')->checkMethodAllowed($object, $method);
1028 }
1029 */
1030 if (TWIG_CALL_SB(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "hasExtension", "sandbox" TSRMLS_CC)) {
1031 TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "sandbox" TSRMLS_CC), "checkMethodAllowed", object, zitem TSRMLS_CC);
1032 }
1033 if (EG(exception)) {
1034 efree(tmp_method_name_get);
1035 efree(tmp_method_name_is);
1036 efree(lcItem);
1037 return;
1038 }
1039 /*
1040 $ret = call_user_func_array(array($object, $method), $arguments);
1041 */
1042 ret = TWIG_CALL_USER_FUNC_ARRAY(object, method, arguments TSRMLS_CC);
1043 free_ret = 1;
1044 efree(tmp_method_name_get);
1045 efree(tmp_method_name_is);
1046 efree(lcItem);
1047 }
1048 /*
1049 // useful when calling a template method from a template
1050 // this is not supported but unfortunately heavily used in the Symfony profiler
1051 if ($object instanceof Twig_TemplateInterface) {
1052 return $ret === '' ? '' : new Twig_Markup($ret, $this->env->getCharset());
1053 }
1054
1055 return $ret;
1056 */
1057 // ret can be null, if e.g. the called method throws an exception
1058 if (ret) {
1059 if (TWIG_INSTANCE_OF_USERLAND(object, "Twig_TemplateInterface" TSRMLS_CC)) {
1060 if (Z_STRLEN_P(ret) != 0) {
1061 zval *charset = TWIG_CALL_USER_FUNC_ARRAY(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getCharset", NULL TSRMLS_CC);
1062 TWIG_NEW(return_value, "Twig_Markup", ret, charset TSRMLS_CC);
1063 zval_ptr_dtor(&charset);
1064 if (ret) {
1065 zval_ptr_dtor(&ret);
1066 }
1067 return;
1068 }
1069 }
1070
1071 RETVAL_ZVAL(ret, 1, 0);
1072 if (free_ret) {
1073 zval_ptr_dtor(&ret);
1074 }
1075 }
1076 }