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