1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
|
/**
* TraverseIterator.h
*
* When an object from PHP userspace implements its own iterator methods,
* and the Php::Value object is used to iterate over its properties, this
* TraversableIterator class is used to iterate over the properties
*
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace Php {
/**
* Class definition
*/
class TraverseIterator : public ValueIteratorImpl
{
public:
/**
* Constructor
* @param object
* @param begin
* @param tsrm_ls
*/
TraverseIterator(zval *object, bool begin TSRMLS_DC) : _object(object)
{
// leap out if this iterator starts at the end
if (!begin) return;
// we need the class entry
auto *entry = zend_get_class_entry(object TSRMLS_CC);
// create the iterator
_iter = entry->get_iterator(entry, object, false TSRMLS_CC);
// rewind the iterator
_iter->funcs->rewind(_iter TSRMLS_CC);
// read the first key/value pair
read(TSRMLS_C);
}
/**
* Copy constructor
* @param that
* @param tsrm_ls
*/
TraverseIterator(const TraverseIterator &that TSRMLS_DC) : TraverseIterator(that._object, that._iter != nullptr TSRMLS_CC)
{
// @todo this is a broken implementation, the copy is at the start
// position, while we'd like to be at the same position
}
/**
* Destructor
*/
virtual ~TraverseIterator()
{
// do nothing if iterator is already invalid
if (!_iter) return;
// we need the tsrm pointer
TSRMLS_FETCH();
// call the iterator destructor
if (_iter) _iter->funcs->dtor(_iter TSRMLS_CC);
}
/**
* Clone the object
* @param tsrm_ls
* @return ValueIteratorImpl*
*/
virtual ValueIteratorImpl *clone() override
{
// we need the tsrm_ls variable
TSRMLS_FETCH();
// construct iterator
return new TraverseIterator(*this TSRMLS_CC);
}
/**
* Increment position (pre-increment)
* @param tsrm_ls
* @return bool
*/
virtual bool increment() override
{
// do we still have an iterator?
if (!_iter) return false;
// we need the tsrm_ls variable
TSRMLS_FETCH();
// movw it forward
_iter->funcs->move_forward(_iter TSRMLS_CC);
// and read current data
read(TSRMLS_C);
}
/**
* Decrement position (pre-decrement)
* @return bool
*/
virtual bool decrement() override
{
// not possible with PHP iterators
throw Exception("Impossible to iterate backwards");
}
/**
* Compare with other iterator
* @param that
* @return bool
*/
virtual bool equals(const ValueIteratorImpl *that) const override
{
// of course if the objects are identical
if (this == that) return true;
// cast to traverse-iterator
TraverseIterator *other = (TraverseIterator *)that;
// if both objects are in an invalid state we consider them to be identical
if (!_iter && !other->_iter) return true;
// although the iterators could be at the same pos, for simplicity
// we consider them different here
return false;
}
/**
* Derefecence, this returns a std::pair with the current key and value
* @return std::pair
*/
virtual const std::pair<Value,Value> ¤t() const
{
return _data;
}
private:
/**
* The object that is iterated over
* @var _val
*/
zval *_object = nullptr;
/**
* The iterator from Zend
* @var zend_object_iterator
*/
struct _zend_object_iterator *_iter = nullptr;
/**
* Current data
* @var pair
*/
std::pair<Value,Value> _data;
/**
* Read current data
* @param tsrm_ls
* @return bool
*/
bool read(TSRMLS_D)
{
// not possible when no iterator exists
if (!_iter) return false;
// is the iterator at a valid position?
if (_iter->funcs->valid(_iter TSRMLS_CC) == FAILURE) return invalidate(TSRMLS_C);
#if PHP_VERSION_ID >= 50500
// create a value object
Value val;
// call the function to get the key
_iter->funcs->get_current_key(_iter, val._val TSRMLS_CC);
// store the key
_data.first = val;
#else
// variable we need for fetching the key, and that will be assigned by
// the PHP engine (this is php 5.3 code)
char *str_key; unsigned int str_key_len; unsigned long int_key;
// php 5.4 or php 5.3 code, fetch the current key
int type = _iter->funcs->get_current_key(_iter, &str_key, &str_key_len, &int_key TSRMLS_CC);
// what sort of key do we have?
if (type == HASH_KEY_IS_LONG)
{
// we have an int key
_data.first = (int64_t)int_key;
}
else
{
// we have a string key that is already allocated
_data.first = str_key;
// deallocate the data
efree(str_key);
}
#endif
// now we're going to fetch the value, for this we need a strange zval
// it is strange that this is a pointer-to-pointer, but that is how
// the Zend engine implements this. It is going to be filled with
// a pointer to a memory address that is guaranteed to hold a valid
// zval.
zval **zval;
// get the current value
_iter->funcs->get_current_data(_iter, &zval TSRMLS_CC);
// wrap the zval in a value object
_data.second = Value(*zval);
// done
return true;
}
/**
* Invalidate the object
* @param tsrm_ls
* @return bool
*/
bool invalidate(TSRMLS_D)
{
// skip if already invalid
if (!_iter) return false;
// reset the iterator
_iter->funcs->dtor(_iter TSRMLS_CC);
// set back to null
_iter = nullptr;
// done
return false;
}
};
/**
* End namespace
*/
}
|