1 /*
2    +----------------------------------------------------------------------+
3    | This source file is subject to version 3.01 of the PHP license,      |
4    | that is bundled with this package in the file LICENSE, and is        |
5    | available through the world-wide-web at the following url:           |
6    | https://www.php.net/license/3_01.txt                                 |
7    | If you did not receive a copy of the PHP license and are unable to   |
8    | obtain it through the world-wide-web, please send a note to          |
9    | license@php.net so we can mail you a copy immediately.               |
10    +----------------------------------------------------------------------+
11    | Authors: Hans-Peter Oeri (University of St.Gallen) <hp@oeri.ch>      |
12    +----------------------------------------------------------------------+
13  */
14 
15 #include <php.h>
16 #include <zend.h>
17 #include <zend_API.h>
18 
19 #include "resourcebundle/resourcebundle.h"
20 #include "resourcebundle/resourcebundle_class.h"
21 #include "resourcebundle/resourcebundle_iterator.h"
22 
23 /*
24  * Although libicu offers iterator functions, they are not used here: libicu does iterate
25  * irrespective of array indices. Those cannot be recreated afterwards. Arrays as well as tables
26  * can however be accessed by numerical index, with table keys readable ex post.
27  */
28 
29 /* {{{ resourcebundle_iterator_read */
resourcebundle_iterator_read(ResourceBundle_iterator * iterator)30 static void resourcebundle_iterator_read( ResourceBundle_iterator *iterator )
31 {
32 	UErrorCode icuerror = U_ZERO_ERROR;
33 	ResourceBundle_object *rb = iterator->subject;
34 
35 	rb->child = ures_getByIndex( rb->me, iterator->i, rb->child, &icuerror );
36 
37 	if (U_SUCCESS(icuerror)) {
38 		/* ATTN: key extraction must be the first thing to do... rb->child might be reset in read! */
39 		if (iterator->is_table) {
40 			iterator->currentkey = estrdup( ures_getKey( rb->child ) );
41 		}
42 		resourcebundle_extract_value( &iterator->current, rb );
43 	}
44 	else {
45 		// zend_throw_exception( spl_ce_OutOfRangeException, "Running past end of ResourceBundle", 0);
46 		ZVAL_UNDEF(&iterator->current);
47 	}
48 }
49 /* }}} */
50 
51 /* {{{ resourcebundle_iterator_invalidate */
resourcebundle_iterator_invalidate(zend_object_iterator * iter)52 static void resourcebundle_iterator_invalidate( zend_object_iterator *iter )
53 {
54 	ResourceBundle_iterator *iterator = (ResourceBundle_iterator *) iter;
55 
56 	if (!Z_ISUNDEF(iterator->current)) {
57 		zval_ptr_dtor( &iterator->current );
58 		ZVAL_UNDEF(&iterator->current);
59 	}
60 	if (iterator->currentkey) {
61 		efree( iterator->currentkey );
62 		iterator->currentkey = NULL;
63 	}
64 }
65 /* }}} */
66 
67 /* {{{ resourcebundle_iterator_dtor */
resourcebundle_iterator_dtor(zend_object_iterator * iter)68 static void resourcebundle_iterator_dtor( zend_object_iterator *iter )
69 {
70 	ResourceBundle_iterator *iterator = (ResourceBundle_iterator *) iter;
71 	zval                    *object = &iterator->intern.data;
72 
73 	resourcebundle_iterator_invalidate( iter );
74 
75 	zval_ptr_dtor(object);
76 }
77 /* }}} */
78 
79 /* {{{ resourcebundle_iterator_has_more */
resourcebundle_iterator_has_more(zend_object_iterator * iter)80 static int resourcebundle_iterator_has_more( zend_object_iterator *iter )
81 {
82 	ResourceBundle_iterator *iterator = (ResourceBundle_iterator *) iter;
83 	return (iterator->i < iterator->length) ? SUCCESS : FAILURE;
84 }
85 /* }}} */
86 
87 /* {{{ resourcebundle_iterator_current */
resourcebundle_iterator_current(zend_object_iterator * iter)88 static zval *resourcebundle_iterator_current( zend_object_iterator *iter )
89 {
90 	ResourceBundle_iterator *iterator = (ResourceBundle_iterator *) iter;
91 	if (Z_ISUNDEF(iterator->current)) {
92 		resourcebundle_iterator_read( iterator);
93 	}
94 	return &iterator->current;
95 }
96 /* }}} */
97 
98 /* {{{ resourcebundle_iterator_key */
resourcebundle_iterator_key(zend_object_iterator * iter,zval * key)99 static void resourcebundle_iterator_key( zend_object_iterator *iter, zval *key )
100 {
101 	ResourceBundle_iterator *iterator = (ResourceBundle_iterator *) iter;
102 
103 	if (Z_ISUNDEF(iterator->current)) {
104 		resourcebundle_iterator_read( iterator);
105 	}
106 
107 	if (iterator->is_table) {
108 		ZVAL_STRING(key, iterator->currentkey);
109 	} else {
110 		ZVAL_LONG(key, iterator->i);
111 	}
112 }
113 /* }}} */
114 
115 /* {{{ resourcebundle_iterator_step */
resourcebundle_iterator_step(zend_object_iterator * iter)116 static void resourcebundle_iterator_step( zend_object_iterator *iter )
117 {
118 	ResourceBundle_iterator *iterator = (ResourceBundle_iterator *) iter;
119 
120 	iterator->i++;
121 	resourcebundle_iterator_invalidate( iter );
122 }
123 /* }}} */
124 
125 /* {{{ resourcebundle_iterator_has_reset */
resourcebundle_iterator_reset(zend_object_iterator * iter)126 static void resourcebundle_iterator_reset( zend_object_iterator *iter )
127 {
128 	ResourceBundle_iterator *iterator = (ResourceBundle_iterator *) iter;
129 
130 	iterator->i = 0;
131 	resourcebundle_iterator_invalidate( iter );
132 }
133 /* }}} */
134 
135 /* {{{ resourcebundle_iterator_funcs */
136 static const zend_object_iterator_funcs resourcebundle_iterator_funcs = {
137 	resourcebundle_iterator_dtor,
138 	resourcebundle_iterator_has_more,
139 	resourcebundle_iterator_current,
140 	resourcebundle_iterator_key,
141 	resourcebundle_iterator_step,
142 	resourcebundle_iterator_reset,
143 	resourcebundle_iterator_invalidate,
144 	NULL, /* get_gc */
145 };
146 /* }}} */
147 
148 /* {{{ resourcebundle_get_iterator */
resourcebundle_get_iterator(zend_class_entry * ce,zval * object,int byref)149 zend_object_iterator *resourcebundle_get_iterator( zend_class_entry *ce, zval *object, int byref )
150 {
151 	ResourceBundle_object   *rb = Z_INTL_RESOURCEBUNDLE_P(object );
152 	ResourceBundle_iterator *iterator = emalloc( sizeof( ResourceBundle_iterator ) );
153 
154 	if (byref) {
155 	     php_error( E_ERROR, "ResourceBundle does not support writable iterators" );
156 	}
157 
158 	zend_iterator_init(&iterator->intern);
159 	Z_ADDREF_P(object);
160 	ZVAL_OBJ(&iterator->intern.data, Z_OBJ_P(object));
161 	iterator->intern.funcs = &resourcebundle_iterator_funcs;
162 
163 	iterator->subject = rb;
164 
165 	/* The iterated rb can only be either URES_TABLE or URES_ARRAY
166 	 * All other types are returned as php primitives!
167 	 */
168 	iterator->is_table = (ures_getType( rb->me ) == URES_TABLE);
169 	iterator->length = ures_getSize( rb->me );
170 
171 	ZVAL_UNDEF(&iterator->current);
172 	iterator->currentkey = NULL;
173 	iterator->i = 0;
174 
175 	return (zend_object_iterator *) iterator;
176 }
177 /* }}} */
178