root / trunk / ext / php / phpext.c

Revision 325, 24.5 kB (checked in by indeyets, 3 months ago)

fixed PECL-bug 14384 (tabs in YAML cause segfault)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2  +----------------------------------------------------------------------+
3  | PHP Version 5                                                        |
4  +----------------------------------------------------------------------+
5  | Copyright (c) 1997-2007 The PHP Group                                |
6  +----------------------------------------------------------------------+
7  | This source file is subject to version 3.01 of the PHP license,      |
8  | that is bundled with this package in the file LICENSE, and is        |
9  | available through the world-wide-web at the following url:           |
10  | http://www.php.net/license/3_01.txt                                  |
11  | If you did not receive a copy of the PHP license and are unable to   |
12  | obtain it through the world-wide-web, please send a note to          |
13  | license@php.net so we can mail you a copy immediately.               |
14  +----------------------------------------------------------------------+
15  | Authors: Why the lucky stiff                                         |
16  |          Alexey Zakhlestin <indeyets@gmail.com>                      |
17  +----------------------------------------------------------------------+
18
19  $Id$
20*/
21#ifdef HAVE_CONFIG_H
22# include "config.h"
23#endif
24
25#include <syck.h>
26
27#include <php.h>
28#include <zend_exceptions.h>
29#include <zend_interfaces.h>
30#include <php_ini.h>
31#include <ext/standard/info.h>
32#include "php_syck.h"
33
34#ifndef Z_SET_REFCOUNT_P
35# define Z_SET_REFCOUNT_P(x, y) (x)->refcount = (y)
36#endif
37
38#ifndef true
39# define true 1
40# define false 0
41#endif
42
43#define PHP_SYCK_VERSION "0.9.3-dev"
44
45/**
46 * SyckException class
47**/
48#define PHP_SYCK_EXCEPTION_PARENT "UnexpectedValueException"
49#define PHP_SYCK_EXCEPTION_PARENT_LC "unexpectedvalueexception"
50#define PHP_SYCK_EXCEPTION_NAME "SyckException"
51
52static zend_class_entry *spl_ce_RuntimeException;
53zend_class_entry *syck_exception_entry;
54
55PHP_SYCK_API zend_class_entry *php_syck_get_exception_base()
56{
57        TSRMLS_FETCH();
58
59        if (!spl_ce_RuntimeException) {
60                zend_class_entry **pce;
61
62                if (zend_hash_find(CG(class_table), PHP_SYCK_EXCEPTION_PARENT_LC, sizeof(PHP_SYCK_EXCEPTION_PARENT_LC), (void **) &pce) == SUCCESS) {
63                        spl_ce_RuntimeException = *pce;
64                        return *pce;
65                }
66        } else {
67                return spl_ce_RuntimeException;
68        }
69
70        return zend_exception_get_default(TSRMLS_C);
71}
72
73
74static double inline zero()             { return 0.0; }
75static double inline one()              { return 1.0; }
76static double inline inf()              { return one() / zero(); }
77static double inline nanphp()   { return zero() / zero(); }
78
79typedef struct {
80        char *output;
81        size_t output_size;
82        size_t output_alloc;
83        unsigned char level;
84        zval **stack;
85} php_syck_emitter_xtra;
86
87void psex_init(php_syck_emitter_xtra *ptr)
88{
89        ptr->output = NULL;
90        ptr->output_size = 0;
91        ptr->output_alloc = 0;
92        ptr->stack = emalloc(sizeof(zval *) * 255);
93        ptr->level = 0;
94}
95
96void psex_free(php_syck_emitter_xtra *ptr)
97{
98        if (ptr->output) {
99                efree(ptr->output);
100                ptr->output = NULL;
101        }
102
103        if (ptr->stack) {
104                efree(ptr->stack);
105                ptr->stack = NULL;
106        }
107
108        ptr->output_size = 0;
109        ptr->output_alloc = 0;
110        ptr->level = 0;
111}
112
113void psex_add_output(php_syck_emitter_xtra *ptr, const char *data, size_t len)
114{
115        while (ptr->output_size + len > ptr->output_alloc) {
116                if (ptr->output_alloc == 0) {
117                        ptr->output_alloc = 8192;
118                        ptr->output = emalloc(ptr->output_alloc);
119                } else {
120                        ptr->output_alloc += 8192;
121                        ptr->output = erealloc(ptr->output, ptr->output_alloc);
122                }
123        }
124
125        strncpy(ptr->output + ptr->output_size, data, len);
126        ptr->output_size += len;
127}
128
129int psex_push_obj(php_syck_emitter_xtra *ptr, zval *obj)
130{
131        if (ptr->level == 255)
132                return 0;
133
134        ptr->stack[++(ptr->level)] = obj;
135        return 1;
136}
137
138zval * psex_pop_obj(php_syck_emitter_xtra *ptr)
139{
140        if (ptr->level == 0)
141                return NULL;
142
143        return ptr->stack[(ptr->level)--];
144}
145
146
147
148
149/**
150 * returns
151 *   0 for sequential numeric arrays
152 *   1 for insequential or associative arrays
153**/
154static int psex_determine_array_type(HashTable *myht TSRMLS_DC) /* {{{ */
155{
156        int i = myht ? zend_hash_num_elements(myht) : 0;
157
158        if (i > 0) {
159                char *key = NULL;
160                uint key_len;
161                HashPosition pos;
162                ulong index, idx = 0;
163
164                zend_hash_internal_pointer_reset_ex(myht, &pos);
165
166                for (;; zend_hash_move_forward_ex(myht, &pos)) {
167                        i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos);
168
169                        if (i == HASH_KEY_NON_EXISTANT)
170                                break;
171
172                        if (i == HASH_KEY_IS_STRING) {
173                                return 1;
174                        } else {
175                                if (index != idx) {
176                                        return 1;
177                                }
178                        }
179
180                        idx++;
181                }
182        }
183
184        return 0;
185}
186
187
188
189
190function_entry syck_functions[] = {
191        PHP_FE(syck_load, NULL)
192        PHP_FE(syck_dump, NULL)
193        {NULL, NULL, NULL}      /* Must be the last line in syck_functions[] */
194};
195
196static zend_module_dep syck_deps[] = {
197        ZEND_MOD_REQUIRED("spl")
198        ZEND_MOD_REQUIRED("hash")
199        ZEND_MOD_REQUIRED("date")
200        {NULL, NULL, NULL}
201};
202
203zend_module_entry syck_module_entry = {
204        STANDARD_MODULE_HEADER_EX, NULL,
205        syck_deps,
206        "syck",
207        syck_functions,
208        PHP_MINIT(syck),        /* module init function */
209        NULL,                           /* module shutdown function */
210        NULL,                           /* request init function */
211        NULL,                           /* request shutdown function */
212        PHP_MINFO(syck),        /* module info function */
213        PHP_SYCK_VERSION,       /* Replace with version number for your extension */
214        STANDARD_MODULE_PROPERTIES
215};
216
217
218#ifdef COMPILE_DL_SYCK
219ZEND_GET_MODULE(syck)
220#endif
221
222PHP_MINIT_FUNCTION(syck)
223{
224        zend_class_entry ce;
225
226        INIT_CLASS_ENTRY(ce, PHP_SYCK_EXCEPTION_NAME, NULL);
227        syck_exception_entry = zend_register_internal_class_ex(&ce, php_syck_get_exception_base(), PHP_SYCK_EXCEPTION_PARENT TSRMLS_CC);
228        return SUCCESS;
229}
230
231PHP_MINFO_FUNCTION(syck)
232{
233        php_info_print_table_start();
234        php_info_print_table_header(2, "Extension version", PHP_SYCK_VERSION);
235        php_info_print_table_header(2, "Syck library version", SYCK_VERSION);
236        php_info_print_table_end();
237}
238
239
240SYMID php_syck_handler(SyckParser *p, SyckNode *n)
241{
242        zval *o = NULL;
243
244        MAKE_STD_ZVAL(o);
245
246        switch (n->kind) {
247                case syck_str_kind:
248                        if (n->type_id == NULL || strcmp(n->type_id, "str") == 0) {
249                                ZVAL_STRINGL(o, n->data.str->ptr, n->data.str->len, 1);
250                        } else if (strcmp(n->type_id, "null") == 0) {
251                                ZVAL_NULL(o);
252                        } else if (strcmp(n->type_id, "bool#yes") == 0) {
253                                ZVAL_BOOL(o, 1);
254                        } else if (strcmp(n->type_id, "bool#no") == 0) {
255                                ZVAL_BOOL(o, 0);
256                        } else if (strcmp(n->type_id, "bool") == 0) {
257                                /* implicit boolean */
258                                char *ptr = n->data.str->ptr;
259                                size_t len = n->data.str->len;
260                                TSRMLS_FETCH();
261
262                                if (len == 1) {
263                                        if (ptr[0] == 'y' || ptr[0] == 'Y') {
264                                                ZVAL_BOOL(o, 1);
265                                        } else if (ptr[0] == 'n' || ptr[0] == 'N') {
266                                                ZVAL_BOOL(o, 0);
267                                        }
268                                } else if (len == 2) {
269                                        if (strncmp(ptr, "on", len) == 0 || strncmp(ptr, "On", len) == 0 || strncmp(ptr, "ON", len) == 0) {
270                                                ZVAL_BOOL(o, 1);
271                                        } else if (strncmp(ptr, "no", len) == 0 || strncmp(ptr, "No", len) == 0 || strncmp(ptr, "NO", len) == 0) {
272                                                ZVAL_BOOL(o, 0);
273                                        }
274                                } else if (len == 3) {
275                                        if (strncmp(ptr, "yes", len) == 0 || strncmp(ptr, "Yes", len) == 0 || strncmp(ptr, "YES", len) == 0) {
276                                                ZVAL_BOOL(o, 1);
277                                        } else if (strncmp(ptr, "off", len) == 0 || strncmp(ptr, "Off", len) == 0 || strncmp(ptr, "OFF", len) == 0) {
278                                                ZVAL_BOOL(o, 0);
279                                        }
280                                } else if (len == 4) {
281                                        if (strncmp(ptr, "true", len) == 0 || strncmp(ptr, "True", len) == 0 || strncmp(ptr, "TRUE", len) == 0) {
282                                                ZVAL_BOOL(o, 1);
283                                        }
284                                } else if (len == 3) {
285                                        if (strncmp(ptr, "false", len) == 0 || strncmp(ptr, "False", len) == 0 || strncmp(ptr, "FALSE", len) == 0) {
286                                                ZVAL_BOOL(o, 1);
287                                        }
288                                }
289
290                                if (Z_TYPE_P(o) != IS_BOOL) {
291                                        zend_throw_exception_ex(syck_exception_entry, 0 TSRMLS_CC, "!bool specified, but value \"%s\" (len=%d) is incorrect on line %d, col %d: '%s'", ptr, len, p->linect + 1, p->cursor - p->lineptr, p->lineptr);
292                                }
293                        } else if (strcmp(n->type_id, "int#hex") == 0) {
294                                long intVal;
295                                syck_str_blow_away_commas(n);
296                                intVal = strtol(n->data.str->ptr, NULL, 16);
297                                ZVAL_LONG(o, intVal);
298                        } else if (strcmp(n->type_id, "int#base60") == 0) {
299                                char *ptr = NULL, *end = NULL;
300                                long sixty = 1;
301                                long total = 0;
302
303                                syck_str_blow_away_commas(n);
304                                ptr = n->data.str->ptr;
305                                end = n->data.str->ptr + n->data.str->len;
306
307                                while (end > ptr) {
308                                        long bnum = 0;
309                                        char *colon = end - 1;
310                                        while (colon >= ptr && *colon != ':') {
311                                                colon--;
312                                        }
313
314                                        if (colon >= ptr && *colon == ':')
315                                                *colon = '\0';
316
317                                        bnum = strtol(colon + 1, NULL, 10);
318                                        total += bnum * sixty;
319                                        sixty *= 60;
320                                        end = colon;
321                                }
322
323                                ZVAL_LONG(o, total);
324                        } else if (strcmp(n->type_id, "int#oct") == 0) {
325                                long intVal;
326                                syck_str_blow_away_commas(n);
327                                intVal = strtol(n->data.str->ptr, NULL, 8);
328                                ZVAL_LONG(o, intVal);
329                        } else if (strcmp(n->type_id, "int") == 0) {
330                                long intVal;
331                                syck_str_blow_away_commas(n);
332                                intVal = strtol(n->data.str->ptr, NULL, 10);
333                                ZVAL_LONG(o, intVal);
334                        } else if (strcmp(n->type_id, "float") == 0 || strcmp(n->type_id, "float#fix") == 0 || strcmp(n->type_id, "float#exp") == 0) {
335                                double f;
336                                syck_str_blow_away_commas(n);
337                                f = strtod(n->data.str->ptr, NULL);
338
339                                ZVAL_DOUBLE(o, f);
340                        } else if (strcmp(n->type_id, "float#base60") == 0) {
341                                char *ptr = NULL, *end = NULL;
342                                long multiplier = 1;
343                                double total = 0;
344
345                                syck_str_blow_away_commas(n);
346                                ptr = n->data.str->ptr;
347                                end = n->data.str->ptr + n->data.str->len;
348
349                                while (end > ptr) {
350                                        double bnum = 0;
351                                        char *colon = end - 1;
352                                        while (colon >= ptr && *colon != ':') {
353                                                colon--;
354                                        }
355
356                                        if (colon >= ptr && *colon == ':')
357                                                *colon = '\0';
358
359                                        bnum = strtod(colon + 1, NULL);
360                                        total += bnum * multiplier;
361                                        multiplier *= 60;
362                                        end = colon;
363                                }
364
365                                ZVAL_DOUBLE(o, total);
366                        } else if (strcmp(n->type_id, "float#nan") == 0) {
367                                ZVAL_DOUBLE(o, nanphp());
368                        } else if (strcmp(n->type_id, "float#inf") == 0) {
369                                ZVAL_DOUBLE(o, inf());
370                        } else if (strcmp(n->type_id, "float#neginf") == 0) {
371                                ZVAL_DOUBLE(o, -inf());
372                        } else if (strncmp(n->type_id, "timestamp", 9) == 0 || strcmp(n->type_id, "php/object::Datetime") == 0) {
373                                zval fname, param, *params[1];
374                                TSRMLS_FETCH();
375
376                                ZVAL_STRING(&fname, "date_create", 1);
377                                INIT_ZVAL(param);
378                                params[0] = &param;
379                                ZVAL_STRINGL(params[0], n->data.str->ptr, n->data.str->len, 1);
380
381                                call_user_function(EG(function_table), NULL, &fname, o, 1, params TSRMLS_CC);
382
383                                zval_dtor(&fname);
384                                zval_dtor(params[0]);
385                        } else if (strncmp(n->type_id, "php/object::", 12) == 0) {
386                                /* Some custom php-class packed in a string. Only ones implementing Serializable ar supported */
387                                size_t classname_len = strlen(n->type_id) - 12;
388                                char *classname = emalloc(classname_len + 1);
389                                zend_class_entry **ce;
390                                zval *param = NULL;
391                                TSRMLS_FETCH();
392
393                                strncpy(classname, n->type_id + 12, classname_len + 1);
394
395                                if (FAILURE == zend_lookup_class_ex(classname, classname_len, 1, &ce TSRMLS_CC)) {
396                                        zend_throw_exception_ex(syck_exception_entry, 0 TSRMLS_CC, "Couldn't find %s class on line %d, col %d: '%s'", classname, p->linect + 1, p->cursor - p->lineptr, p->lineptr);
397                                        efree(classname);
398                                        break;
399                                }
400
401                                if (0 == instanceof_function_ex(*ce, zend_ce_serializable, 1 TSRMLS_CC)) {
402                                        zend_throw_exception_ex(syck_exception_entry, 0 TSRMLS_CC, "Class %s doesn't implement Serializable on line %d, col %d: '%s'", classname, p->linect + 1, p->cursor - p->lineptr, p->lineptr);
403                                        efree(classname);
404                                        break;
405                                }
406                                efree(classname);
407
408                                object_init_ex(o, *ce);
409                                MAKE_STD_ZVAL(param);
410                                ZVAL_STRINGL(param, n->data.str->ptr, n->data.str->len, 1);
411                                zend_call_method_with_1_params(&o, *ce, NULL, "unserialize", NULL, param);
412                                zval_ptr_dtor(&param);
413                        } else {
414                                php_error(E_NOTICE, "syck extension didn't handle %s type => treating as a string", n->type_id);
415                                ZVAL_STRINGL(o, n->data.str->ptr, n->data.str->len, 1);
416                        }
417                break;
418
419                case syck_seq_kind:
420                {
421                        if (NULL != n->type_id && strncmp(n->type_id, "php/array::", 11) == 0) {
422                                /* some class which implements ArrayAccess */
423                                size_t i;
424                                size_t classname_len = strlen(n->type_id) - 11;
425                                char *classname = emalloc(classname_len + 1);
426                                zend_class_entry **ce;
427                                TSRMLS_FETCH();
428
429                                strncpy(classname, n->type_id + 11, classname_len + 1);
430
431                                if (FAILURE == zend_lookup_class_ex(classname, classname_len, 1, &ce TSRMLS_CC)) {
432                                        zend_throw_exception_ex(syck_exception_entry, 0 TSRMLS_CC, "Couldn't find %s class on line %d, col %d: '%s'", classname, p->linect + 1, p->cursor - p->lineptr, p->lineptr);
433                                        efree(classname);
434                                        break;
435                                }
436
437                                if (0 != instanceof_function_ex(*ce, zend_ce_arrayaccess, 1 TSRMLS_CC)) {
438                                        object_init_ex(o, *ce);
439
440                                        for (i = 0; i < n->data.list->idx; i++) {
441                                                SYMID oid = syck_seq_read(n, i);
442                                                zval *o2 = NULL;
443                                                zval *key = NULL;
444
445                                                syck_lookup_sym(p, oid, (char **) &o2); /* retrieving child-node */
446
447                                                MAKE_STD_ZVAL(key);
448                                                ZVAL_LONG(key, i);
449
450                                                zend_call_method_with_2_params(&o, *ce, NULL, "offsetset", NULL, key, o2);
451                                                zval_ptr_dtor(&o2);
452                                                efree(key);
453                                        }
454
455                                        if (zend_hash_exists(&(*ce)->function_table, "__wakeup", 9)) {
456                                                zend_call_method_with_0_params(&o, *ce, NULL, "__wakeup", NULL);
457                                        }
458                                } else {
459                                        zend_throw_exception_ex(syck_exception_entry, 0 TSRMLS_CC, "Class %s doesn't implement ArrayAccess on line %d, col %d: '%s'", classname, p->linect + 1, p->cursor - p->lineptr, p->lineptr);
460                                        efree(classname);
461                                        break;
462                                }
463
464
465
466                                efree(classname);
467                        } else {
468                                size_t i;
469
470                                if (NULL != n->type_id && strcmp(n->type_id, "php/array") != 0) {
471                                        php_error(E_NOTICE, "syck extension didn't handle sequence of %s type. treating as php/array", n->type_id);
472                                }
473
474                                /* Just an array */
475                                array_init(o);
476
477                                for (i = 0; i < n->data.list->idx; i++) {
478                                        SYMID oid = syck_seq_read(n, i);
479                                        zval *o2 = NULL;
480
481                                        syck_lookup_sym(p, oid, (char **) &o2); /* retrieving child-node */
482
483                                        add_index_zval(o, i, o2);
484                                }
485                        }
486                }
487                break;
488
489                case syck_map_kind:
490                {
491                        if (NULL != n->type_id && strncmp(n->type_id, "php/hash::", 10) == 0) {
492                                /* some class which implements ArrayAccess */
493                                SYMID oid;
494                                size_t i;
495                                zval *o2 = NULL, *o3 = NULL;
496                                size_t classname_len = strlen(n->type_id) - 10;
497                                char *classname = emalloc(classname_len + 1);
498                                zend_class_entry **ce;
499                                TSRMLS_FETCH();
500
501                                strncpy(classname, n->type_id + 10, classname_len + 1);
502
503                                if (FAILURE == zend_lookup_class_ex(classname, classname_len, 1, &ce TSRMLS_CC)) {
504                                        zend_throw_exception_ex(syck_exception_entry, 0 TSRMLS_CC, "Couldn't find %s class on line %d, col %d: '%s'", classname, p->linect + 1, p->cursor - p->lineptr, p->lineptr);
505                                        efree(classname);
506                                        break;
507                                }
508
509                                if (0 == instanceof_function_ex(*ce, zend_ce_arrayaccess, 1 TSRMLS_CC)) {
510                                        zend_throw_exception_ex(syck_exception_entry, 0 TSRMLS_CC, "Class %s doesn't implement ArrayAccess on line %d, col %d: '%s'", classname, p->linect + 1, p->cursor - p->lineptr, p->lineptr);
511                                        efree(classname);
512                                        break;
513                                }
514
515                                object_init_ex(o, *ce);
516
517                                for (i = 0; i < n->data.pairs->idx; i++) {
518                                        oid = syck_map_read(n, map_key, i);
519                                        syck_lookup_sym(p, oid, (char **) &o2); /* retrieving key-node */
520
521                                        if (o2->type == IS_STRING || o2->type == IS_LONG) {
522                                                oid = syck_map_read(n, map_value, i);
523                                                syck_lookup_sym(p, oid, (char **) &o3); /* retrieving value-node */
524
525                                                zend_call_method_with_2_params(&o, *ce, NULL, "offsetset", NULL, o2, o3);
526                                                zval_ptr_dtor(&o3);
527                                        }
528
529                                        zval_ptr_dtor(&o2);
530                                }
531
532                                if (zend_hash_exists(&(*ce)->function_table, "__wakeup", 9)) {
533                                        zend_call_method_with_0_params(&o, *ce, NULL, "__wakeup", NULL);
534                                }
535
536                                efree(classname);
537                        } else {
538                                SYMID oid;
539                                size_t i;
540                                zval *o2 = NULL, *o3 = NULL;
541                                zval *res = NULL;
542
543                                if (NULL != n->type_id && strcmp(n->type_id, "php/hash") != 0) {
544                                        php_error(E_NOTICE, "syck extension didn't handle map of %s type. treating as php/hash", n->type_id);
545                                }
546
547                                array_init(o);
548
549                                for (i = 0; i < n->data.pairs->idx; i++) {
550                                        oid = syck_map_read(n, map_key, i);
551                                        syck_lookup_sym(p, oid, (char **) &o2); /* retrieving key-node */
552
553                                        if (o2->type == IS_STRING || o2->type == IS_LONG) {
554                                                oid = syck_map_read(n, map_value, i);
555                                                syck_lookup_sym(p, oid, (char **) &o3); /* retrieving value-node */
556
557                                                if (o2->type == IS_LONG) {
558                                                        add_index_zval(o, Z_LVAL_P(o2), o3);
559                                                } else {
560                                                        add_assoc_zval(o, Z_STRVAL_P(o2), o3);
561                                                }
562                                        }
563
564                                        zval_ptr_dtor(&o2);
565                                }
566                        }
567                }
568                break;
569        }
570
571        return syck_add_sym(p, (char *)o); /* storing node */
572}
573
574SyckNode * php_syck_badanchor_handler(SyckParser *p, const char *str)
575{
576        SyckNode *res;
577        char *endl = p->cursor;
578        TSRMLS_FETCH();
579
580        while (*endl != '\0' && *endl != '\n')
581                endl++;
582
583        endl[0] = '\0';
584
585        res = syck_alloc_str();
586
587        zend_throw_exception_ex(syck_exception_entry, 0 TSRMLS_CC, "bad anchor \"%s\" on line %d, col %d", str, p->linect + 1, p->cursor - p->lineptr - strlen(str));
588
589        return res;
590}
591
592enum st_retval my_cleaner(char *key, char *value, char *smth)
593{
594        zval *ptr = (zval *)value;
595
596        zval_ptr_dtor(&ptr);
597        return ST_DELETE;
598}
599
600void php_syck_ehandler(SyckParser *p, const char *str)
601{
602        char *endl = p->cursor;
603        zval *exc;
604        TSRMLS_FETCH();
605
606        while (*endl != '\0' && *endl != '\n')
607                endl++;
608
609        endl[0] = '\0';
610
611        if (NULL == p->bonus) {
612                /* silence second exceptions */
613                exc = zend_throw_exception_ex(syck_exception_entry, 0 TSRMLS_CC, "%s on line %d, col %d: '%s'", str, p->linect + 1, p->cursor - p->lineptr, p->lineptr);
614
615                Z_SET_REFCOUNT_P(exc, 2);
616
617                p->bonus = exc;
618        }
619
620        if (p->syms)
621                st_foreach(p->syms, my_cleaner, NULL);
622}
623
624void php_syck_emitter_handler(SyckEmitter *e, st_data_t id)
625{
626        php_syck_emitter_xtra *bonus = (php_syck_emitter_xtra *) e->bonus;
627        zval *data = bonus->stack[id];
628        TSRMLS_FETCH();
629
630        switch (Z_TYPE_P(data)) {
631                case IS_NULL:
632                        syck_emit_scalar(e, "null", scalar_none, 0, 0, 0, "", 0);
633                break;
634
635                case IS_BOOL:
636                {
637                        char *bool_s = Z_BVAL_P(data) ? "true" : "false";
638                        syck_emit_scalar(e, "boolean", scalar_none, 0, 0, 0, bool_s, strlen(bool_s));
639                }
640                break;
641
642                case IS_LONG:
643                {
644                        size_t res_size;
645                        char *res = NULL;
646
647                        res_size = snprintf(res, 0, "%ld", Z_LVAL_P(data)); /* getting size ("0" doesn't let output) */
648                        res = emalloc(res_size + 1);
649                        snprintf(res, res_size + 1, "%ld", Z_LVAL_P(data));
650
651                        syck_emit_scalar(e, "number", scalar_none, 0, 0, 0, res, res_size);
652                        efree(res);
653                }
654                break;
655
656                case IS_DOUBLE:
657                {
658                        size_t res_size;
659                        char *res = NULL;
660
661                        res_size = snprintf(res, 0, "%f", Z_DVAL_P(data)); /* getting size ("0" doesn't let output) */
662                        res = emalloc(res_size + 1);
663                        snprintf(res, res_size + 1, "%f", Z_DVAL_P(data));
664
665                        syck_emit_scalar(e, "number", scalar_none, 0, 0, 0, res, res_size);
666                        efree(res);
667                }
668                break;
669
670                case IS_STRING:
671                {
672                        enum scalar_style style = scalar_2quote;
673                        const char *ptr;
674
675                        for (ptr = Z_STRVAL_P(data); ptr != Z_STRVAL_P(data) + Z_STRLEN_P(data); ptr++) {
676                                if (*ptr == '\n') {
677                                        style = scalar_fold;
678                                }
679                        }
680
681                        syck_emit_scalar(e, "str", style, 0, 0, 0, Z_STRVAL_P(data), Z_STRLEN_P(data));
682                }
683                break;
684
685                case IS_ARRAY:
686                {
687                        HashTable *tbl = Z_ARRVAL_P(data);
688                        int flat_and_short = false;
689
690                        /* we try to detect if array is short and flat */
691                        if (tbl->nNumOfElements <= 6) {
692                                flat_and_short = true;
693
694                                for (zend_hash_internal_pointer_reset(tbl); zend_hash_has_more_elements(tbl) == SUCCESS; zend_hash_move_forward(tbl)) {
695                                        zval **ppzval = NULL;
696
697                                        zend_hash_get_current_data(tbl, (void **)&ppzval);
698
699                                        if (Z_TYPE_P(*ppzval) == IS_ARRAY || Z_TYPE_P(*ppzval) == IS_OBJECT) {
700                                                flat_and_short = false;
701                                        }
702                                }
703                        }
704
705                        if (0 == psex_determine_array_type(tbl TSRMLS_CC)) {
706                                /* indexed array */
707                                if (flat_and_short)
708                                        syck_emit_seq(e, "table", seq_inline);
709                                else
710                                        syck_emit_seq(e, "table", seq_none);
711
712                                for (zend_hash_internal_pointer_reset(tbl); zend_hash_has_more_elements(tbl) == SUCCESS; zend_hash_move_forward(tbl)) {
713                                        zval **ppzval = NULL;
714
715                                        zend_hash_get_current_data(tbl, (void **)&ppzval);
716                                        if (psex_push_obj(bonus, *ppzval)) {
717                                                syck_emit_item(e, bonus->level);
718                                                psex_pop_obj(bonus);
719                                        }
720                                }
721
722                                syck_emit_end(e);
723                        } else {
724                                /* associative array */
725                                if (flat_and_short)
726                                        syck_emit_map(e, "table", map_inline);
727                                else
728                                        syck_emit_map(e, "table", map_none);
729
730                                for (zend_hash_internal_pointer_reset(tbl); zend_hash_has_more_elements(tbl) == SUCCESS; zend_hash_move_forward(tbl)) {
731                                        zval **ppzval = NULL, kzval;
732                                        uint key_len;
733                                        ulong idx;
734                                        size_t key_type;
735
736                                        key_type = zend_hash_get_current_key_type_ex(tbl, NULL);
737
738                                        if (key_type == HASH_KEY_IS_LONG) {
739                                                zend_hash_get_current_key_ex(tbl, NULL, NULL, &idx, 0, NULL);
740                                                ZVAL_LONG(&kzval, idx);
741                                        } else {
742                                                char *skey = NULL;
743                                                zend_hash_get_current_key_ex(tbl, (char **)&skey, &key_len, NULL, 0, NULL);
744                                                ZVAL_STRINGL(&kzval, skey, key_len - 1, 1);
745                                        }
746
747                                        zend_hash_get_current_data(tbl, (void **)&ppzval);
748
749                                        if (psex_push_obj(bonus, &kzval)) {
750                                                syck_emit_item(e, bonus->level);
751                                                psex_pop_obj(bonus);
752
753                                                if (psex_push_obj(bonus, *ppzval)) {
754                                                        syck_emit_item(e, bonus->level);
755                                                        psex_pop_obj(bonus);
756                                                }
757                                        }
758
759                                        zval_dtor(&kzval);
760                                }
761
762                                syck_emit_end(e);
763                        }
764                }
765                break;
766
767                case IS_OBJECT:
768                {
769                        char *name = NULL;
770                        zend_uint name_len;
771                        zend_class_entry *ce;
772                        TSRMLS_FETCH();
773
774                        ce = Z_OBJCE_P(data);
775                        zend_get_object_classname(data, &name, &name_len TSRMLS_CC);
776
777                        if (strncmp(name, "DateTime", name_len) == 0) {
778                                /* DateTime is encoded as timestamp */
779                                zval *retval = NULL;
780                                zval constant;
781
782#if ZEND_MODULE_API_NO >= 20071006
783                                zend_get_constant_ex("DateTime::ISO8601", 17, &constant, ce, 0 TSRMLS_CC);
784#else
785                                zend_get_constant_ex("DateTime::ISO8601", 17, &constant, ce TSRMLS_CC);
786#endif
787                                zend_call_method_with_1_params(&data, ce, NULL, "format", &retval, &constant);
788
789                                zval_dtor(&constant);
790
791                                syck_emit_scalar(e, "tag:yaml.org,2002:timestamp#iso8601", scalar_none, 0, 0, 0, Z_STRVAL_P(retval), Z_STRLEN_P(retval));
792                                zval_dtor(retval);
793                                efree(retval);
794                        } else {
795                                if (0 != instanceof_function_ex(ce, zend_ce_serializable, 1 TSRMLS_CC)) {
796                                        /* Some class which implements Serializable interface */
797                                        char *prefix = "tag:php:object::";
798                                        size_t prefix_len = strlen(prefix) + 1;
799                                        char *tagname = emalloc(name_len + prefix_len);
800                                        zval *serialized = NULL;
801
802                                        snprintf(tagname, name_len + prefix_len, "%s%s", prefix, name);
803
804                                        zend_call_method_with_0_params(&data, ce, NULL, "serialize", &serialized);
805                                        syck_emit_scalar(e, tagname, scalar_2quote, 0, 0, 0, Z_STRVAL_P(serialized), Z_STRLEN_P(serialized));
806
807                                        efree(tagname);
808                                }
809                        }
810
811                        efree(name);
812                }
813                break;
814
815                case IS_RESOURCE:
816                default:
817                        /* something unknown */
818                break;
819        }
820}
821
822void php_syck_output_handler(SyckEmitter *e, const char *str, long len)
823{
824        php_syck_emitter_xtra *bonus = (php_syck_emitter_xtra *) e->bonus;
825        psex_add_output(bonus, str, (size_t)len);
826}
827
828/* {{{ proto object syck_load(string arg)
829   Return PHP object from a YAML string */
830PHP_FUNCTION(syck_load)
831{
832        char *arg = NULL;
833        int arg_len;
834        SYMID v;
835        zval *obj = NULL;
836        SyckParser *parser;
837
838        if (ZEND_NUM_ARGS() != 1) {
839                WRONG_PARAM_COUNT;
840        }
841
842        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) {
843                return;
844        }
845
846        parser = syck_new_parser();
847
848        syck_parser_handler(parser, php_syck_handler);
849        syck_parser_bad_anchor_handler(parser, php_syck_badanchor_handler);
850        syck_parser_error_handler(parser, php_syck_ehandler);
851
852        syck_parser_implicit_typing(parser, 1);
853        syck_parser_taguri_expansion(parser, 0);
854
855        syck_parser_str(parser, arg, arg_len, NULL);
856
857        v = syck_parse(parser);
858
859        if (parser->bonus) {
860                *return_value = *((zval *)parser->bonus);
861                zval_copy_ctor(return_value);
862                // zval_ptr_dtor(&obj);
863        } else {
864                if (1 == syck_lookup_sym(parser, v, (char **) &obj)) {
865                        if (NULL != &obj && NULL != obj) {
866                                *return_value = *obj;
867                                zval_copy_ctor(return_value);
868                                zval_ptr_dtor(&obj);
869                        }
870                }
871        }
872
873        syck_free_parser(parser);
874}
875/* }}} */
876
877
878/* {{{ proto object syck_load(string arg)
879   Convert PHP object into YAML string */
880PHP_FUNCTION(syck_dump)
881{
882        SyckEmitter *emitter;
883        php_syck_emitter_xtra *extra;
884        zval *ptr;
885
886        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &ptr) == FAILURE) {
887                return;
888        }
889
890        extra = emalloc(sizeof(php_syck_emitter_xtra));
891        psex_init(extra);
892        psex_push_obj(extra, ptr);
893
894        emitter = syck_new_emitter();
895        emitter->bonus = extra;
896        emitter->use_header = 1;
897        emitter->use_version = 1;
898
899        syck_emitter_handler(emitter, php_syck_emitter_handler);
900        syck_output_handler(emitter, php_syck_output_handler);
901
902        syck_emit(emitter, extra->level);
903        syck_emitter_flush(emitter, 0);
904
905        ZVAL_STRINGL(return_value, extra->output, extra->output_size, 1);
906
907        psex_free(extra);
908        efree(extra);
909        syck_free_emitter(emitter);
910}
911/* }}} */
912
913
914/*
915 * Local variables:
916 * tab-width: 4
917 * c-basic-offset: 4
918 * End:
919 * vim600: noet sw=4 ts=4 fdm=marker
920 * vim<600: noet sw=4 ts=4
921 */
Note: See TracBrowser for help on using the browser.