improved type validation
[m6w6/ext-psi] / src / marshal.c
1 /*******************************************************************************
2 Copyright (c) 2016, Michael Wallner <mike@php.net>.
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8 * Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
18 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *******************************************************************************/
25
26 #include "php_psi_stdinc.h"
27 #include "data.h"
28 #include "calc.h"
29
30 #include "php.h"
31 #include "php_psi.h"
32
33 #include "Zend/zend_interfaces.h"
34 #include "ext/spl/spl_iterators.h"
35
36 zend_long psi_zval_count(zval *zvalue)
37 {
38 /* mimic PHP count() */
39 zend_long count;
40 zval retval;
41
42 switch (Z_TYPE_P(zvalue)) {
43 default:
44 count = 1;
45 break;
46 case IS_NULL:
47 count = 0;
48 break;
49 case IS_ARRAY:
50 count = zend_array_count(Z_ARRVAL_P(zvalue));
51 break;
52 case IS_OBJECT:
53 count = 1;
54 if (Z_OBJ_HT_P(zvalue)->count_elements) {
55 if (SUCCESS == Z_OBJ_HT_P(zvalue)->count_elements(zvalue, &count)) {
56 break;
57 }
58 }
59
60 if (instanceof_function(Z_OBJCE_P(zvalue), spl_ce_Countable)) {
61 zend_call_method_with_0_params(zvalue, NULL, NULL, "count", &retval);
62 if (Z_TYPE(retval) != IS_UNDEF) {
63 count = zval_get_long(&retval);
64 zval_ptr_dtor(&retval);
65 }
66 }
67 break;
68 }
69
70 return count;
71 }
72
73 int psi_internal_type(struct psi_impl_type *type)
74 {
75 switch (type->type) {
76 case PSI_T_BOOL:
77 return _IS_BOOL;
78 case PSI_T_INT:
79 return IS_LONG;
80 case PSI_T_FLOAT:
81 case PSI_T_DOUBLE:
82 return IS_DOUBLE;
83 case PSI_T_STRING:
84 return IS_STRING;
85 case PSI_T_ARRAY:
86 return IS_ARRAY;
87 default:
88 return 0;
89 }
90 }
91
92 zend_internal_arg_info *psi_internal_arginfo(struct psi_impl *impl)
93 {
94 size_t i = 0, argc = psi_plist_count(impl->func->args);
95 zend_internal_arg_info *aip;
96 zend_internal_function_info *fi;
97 struct psi_impl_arg *iarg;
98
99 aip = calloc(argc + 1 + !!impl->func->vararg, sizeof(*aip));
100
101 fi = (zend_internal_function_info *) &aip[0];
102 #ifdef ZEND_TYPE_ENCODE
103 fi->type = ZEND_TYPE_ENCODE(psi_internal_type(impl->func->return_type), 1);
104 #else
105 fi->allow_null = 1;
106 fi->type_hint = psi_internal_type(impl->func->return_type);
107 #endif
108 fi->required_num_args = psi_impl_num_min_args(impl);
109 fi->return_reference = impl->func->return_reference;
110
111 if (impl->func->vararg) {
112 struct psi_impl_arg *vararg = impl->func->vararg;
113 zend_internal_arg_info *ai = &aip[argc];
114
115 ai->name = vararg->var->name;
116 #ifdef ZEND_TYPE_ENCODE
117 ai->type = ZEND_TYPE_ENCODE(psi_internal_type(vararg->type), 1);
118 #else
119 ai->allow_null = 1;
120 ai->type_hint = psi_internal_type(vararg->type);
121 #endif
122 if (vararg->var->reference) {
123 ai->pass_by_reference = 1;
124 }
125 ai->is_variadic = 1;
126 }
127
128 while (psi_plist_get(impl->func->args, i++, &iarg)) {
129 zend_internal_arg_info *ai = &aip[i];
130
131 ai->name = iarg->var->name;
132 #ifdef ZEND_TYPE_ENCODE
133 ai->type = ZEND_TYPE_ENCODE(psi_internal_type(iarg->type), 1);
134 #else
135 ai->allow_null = 1;
136 ai->type_hint = psi_internal_type(iarg->type);
137 #endif
138 if (iarg->var->reference) {
139 ai->pass_by_reference = 1;
140 }
141 }
142
143 return aip;
144 }
145
146 /*
147 * return void(dvar)
148 */
149 void psi_set_void(zval *return_value, struct psi_set_exp *set, impl_val *ret_val, struct psi_call_frame *frame)
150 {
151 RETVAL_NULL();
152 }
153
154 /*
155 * ?
156 */
157 impl_val *psi_let_void(impl_val *tmp, struct psi_decl_arg *spec, token_t impl_type, impl_val *ival, zval *zvalue, void **to_free)
158 {
159 return tmp;
160 }
161
162 /*
163 * set $ivar = zval(dvar)
164 */
165 void psi_set_zval(zval *return_value, struct psi_set_exp *set, impl_val *ret_val, struct psi_call_frame *frame) {
166 RETVAL_ZVAL(ret_val->ptr, 1, 0);
167 }
168
169 /*
170 * let dvar = zval($ivar)
171 */
172 impl_val *psi_let_zval(impl_val *tmp, struct psi_decl_arg *spec, token_t impl_type, impl_val *ival, zval *zvalue, void **to_free)
173 {
174 *to_free = tmp->ptr = emalloc(sizeof(zval));
175 ZVAL_COPY_VALUE(tmp->ptr, zvalue);
176 return tmp;
177 }
178
179 /*
180 * return to_bool(dvar)
181 */
182 void psi_set_to_bool(zval *return_value, struct psi_set_exp *set, impl_val *ret_val, struct psi_call_frame *frame)
183 {
184 psi_set_to_int(return_value, set, ret_val, frame);
185 convert_to_boolean(return_value);
186 }
187
188 static inline impl_val *psi_val_boolval(impl_val *tmp, token_t real_type, zend_bool boolval) {
189 switch (real_type) {
190 case PSI_T_INT8: tmp->i8 = boolval; break;
191 case PSI_T_UINT8: tmp->u8 = boolval; break;
192 case PSI_T_INT16: tmp->i16 = boolval; break;
193 case PSI_T_UINT16: tmp->u16 = boolval; break;
194 case PSI_T_INT32: tmp->i32 = boolval; break;
195 case PSI_T_UINT32: tmp->u32 = boolval; break;
196 case PSI_T_INT64: tmp->i64 = boolval; break;
197 case PSI_T_UINT64: tmp->u64 = boolval; break;
198 case PSI_T_FLOAT: tmp->fval = boolval; break;
199 case PSI_T_DOUBLE: tmp->dval = boolval; break;
200 #ifdef HAVE_LONG_DOUBLE
201 case PSI_T_LONG_DOUBLE: tmp->ldval = boolval; break;
202 #endif
203 EMPTY_SWITCH_DEFAULT_CASE();
204 }
205 return tmp;
206 }
207
208 /*
209 * let dvar = boolval($ivar)
210 */
211 impl_val *psi_let_boolval(impl_val *tmp, struct psi_decl_arg *spec, token_t impl_type, impl_val *ival, zval *zvalue, void **to_free)
212 {
213 zend_bool boolval;
214 token_t real_type = spec ? psi_decl_type_get_real(spec->type)->type : PSI_T_UINT8;
215
216 if (ival && impl_type == PSI_T_BOOL) {
217 boolval = ival->zend.bval;
218 } else {
219 boolval = zend_is_true(zvalue);
220 }
221
222 return psi_val_boolval(tmp, real_type, boolval);
223 }
224
225 # define RETVAL_LONG_U64(V) \
226 if (V > ZEND_LONG_MAX) { \
227 char d[24] = {0}; \
228 RETVAL_STRING(zend_print_ulong_to_buf(&d[22], V)); \
229 } else { \
230 RETVAL_LONG(V); \
231 }
232
233 /*
234 * set $ivar = to_int(*dvar)
235 */
236 void psi_set_to_int(zval *return_value, struct psi_set_exp *set, impl_val *ret_val, struct psi_call_frame *frame)
237 {
238 struct psi_decl_var *var = psi_set_exp_get_decl_var(set);
239 token_t t = psi_decl_type_get_real(var->arg->type)->type;
240 impl_val *v = deref_impl_val(ret_val, var);
241
242 switch (t) {
243 case PSI_T_INT8: RETVAL_LONG(v->i8); break;
244 case PSI_T_UINT8: RETVAL_LONG(v->u8); break;
245 case PSI_T_INT16: RETVAL_LONG(v->i16); break;
246 case PSI_T_UINT16: RETVAL_LONG(v->u16); break;
247 case PSI_T_INT32: RETVAL_LONG(v->i32); break;
248 case PSI_T_UINT32: RETVAL_LONG(v->u32); break;
249 case PSI_T_INT64: RETVAL_LONG(v->i64); break;
250 case PSI_T_UINT64: RETVAL_LONG_U64(v->u64); break;
251 case PSI_T_FLOAT:
252 RETVAL_DOUBLE((double) v->fval);
253 convert_to_long(return_value);
254 break;
255 case PSI_T_DOUBLE:
256 RETVAL_DOUBLE(v->dval);
257 convert_to_long(return_value);
258 break;
259 #ifdef HAVE_LONG_DOUBLE
260 case PSI_T_LONG_DOUBLE:
261 RETVAL_DOUBLE((double) v->ldval);
262 convert_to_long(return_value);
263 break;
264 #endif
265 EMPTY_SWITCH_DEFAULT_CASE();
266 }
267 }
268
269 static inline impl_val *psi_val_intval(impl_val *tmp, token_t real_type, zend_long intval) {
270 switch (real_type) {
271 case PSI_T_INT8: tmp->i8 = intval; break;
272 case PSI_T_UINT8: tmp->u8 = intval; break;
273 case PSI_T_INT16: tmp->i16 = intval; break;
274 case PSI_T_UINT16: tmp->u16 = intval; break;
275 case PSI_T_INT32: tmp->i32 = intval; break;
276 case PSI_T_UINT32: tmp->u32 = intval; break;
277 case PSI_T_INT64: tmp->i64 = intval; break;
278 case PSI_T_UINT64: tmp->u64 = intval; break;
279 case PSI_T_INT: tmp->ival = intval; break;
280 case PSI_T_LONG: tmp->lval = intval; break;
281 case PSI_T_FLOAT: tmp->fval = intval; break;
282 case PSI_T_DOUBLE: tmp->dval = intval; break;
283 #ifdef HAVE_LONG_DOUBLE
284 case PSI_T_LONG_DOUBLE: tmp->ldval = intval; break;
285 #endif
286 EMPTY_SWITCH_DEFAULT_CASE();
287 }
288
289 return tmp;
290 }
291
292 /*
293 * let dvar = intval($ivar)
294 */
295 impl_val *psi_let_intval(impl_val *tmp, struct psi_decl_arg *spec, token_t impl_type, impl_val *ival, zval *zvalue, void **to_free)
296 {
297 zend_long intval;
298 token_t real_type = spec ? psi_decl_type_get_real(spec->type)->type : PSI_T_LONG;
299
300 if (ival && impl_type == PSI_T_INT) {
301 intval = ival->zend.lval;
302 } else {
303 intval = zval_get_long(zvalue);
304 }
305
306 return psi_val_intval(tmp, real_type, intval);
307 }
308
309 /*
310 * set $ivar = to_float(dvar)
311 */
312 void psi_set_to_float(zval *return_value, struct psi_set_exp *set, impl_val *ret_val, struct psi_call_frame *frame)
313 {
314 struct psi_decl_var *var = psi_set_exp_get_decl_var(set);
315 token_t t = psi_decl_type_get_real(var->arg->type)->type;
316 impl_val *v = deref_impl_val(ret_val, var);
317
318 switch (t) {
319 case PSI_T_FLOAT: RETVAL_DOUBLE((double) v->fval); break;
320 case PSI_T_DOUBLE: RETVAL_DOUBLE(v->dval); break;
321 #ifdef HAVE_LONG_DOUBLE
322 case PSI_T_LONG_DOUBLE: RETVAL_DOUBLE((double) v->ldval); break;
323 #endif
324 case PSI_T_INT8: RETVAL_DOUBLE((double) v->i8); break;
325 case PSI_T_UINT8: RETVAL_DOUBLE((double) v->u8); break;
326 case PSI_T_INT16: RETVAL_DOUBLE((double) v->i16); break;
327 case PSI_T_UINT16: RETVAL_DOUBLE((double) v->u16); break;
328 case PSI_T_INT32: RETVAL_DOUBLE((double) v->i32); break;
329 case PSI_T_UINT32: RETVAL_DOUBLE((double) v->u32); break;
330 case PSI_T_INT64: RETVAL_DOUBLE((double) v->i64); break;
331 case PSI_T_UINT64: RETVAL_DOUBLE((double) v->u64); break;
332 EMPTY_SWITCH_DEFAULT_CASE();
333 }
334 }
335
336 static inline impl_val *psi_val_floatval(impl_val *tmp, token_t real_type, double floatval) {
337 switch (real_type) {
338 case PSI_T_INT8: tmp->i8 = floatval; break;
339 case PSI_T_UINT8: tmp->u8 = floatval; break;
340 case PSI_T_INT16: tmp->i16 = floatval; break;
341 case PSI_T_UINT16: tmp->u16 = floatval; break;
342 case PSI_T_INT32: tmp->i32 = floatval; break;
343 case PSI_T_UINT32: tmp->u32 = floatval; break;
344 case PSI_T_INT64: tmp->i64 = floatval; break;
345 case PSI_T_UINT64: tmp->u64 = floatval; break;
346 case PSI_T_FLOAT: tmp->fval = floatval; break;
347 case PSI_T_DOUBLE: tmp->dval = floatval; break;
348 #ifdef HAVE_LONG_DOUBLE
349 case PSI_T_LONG_DOUBLE: tmp->ldval = floatval; break;
350 #endif
351 EMPTY_SWITCH_DEFAULT_CASE();
352 }
353
354 return tmp;
355 }
356
357 /*
358 * let dvar = floatval($ivar)
359 */
360 impl_val *psi_let_floatval(impl_val *tmp, struct psi_decl_arg *spec, token_t impl_type, impl_val *ival, zval *zvalue, void **to_free)
361 {
362 double floatval;
363 token_t real_type = spec ? psi_decl_type_get_real(spec->type)->type : PSI_T_DOUBLE;
364
365 if (ival && (impl_type == PSI_T_FLOAT || impl_type == PSI_T_DOUBLE)) {
366 floatval = ival->dval;
367 } else {
368 floatval = zval_get_double(zvalue);
369 }
370
371 return psi_val_floatval(tmp, real_type, floatval);
372 }
373
374 /*
375 * set $ivar = to_string(dvar)
376 */
377 void psi_set_to_string(zval *return_value, struct psi_set_exp *set, impl_val *ret_val, struct psi_call_frame *frame)
378 {
379 struct psi_decl_var *var = psi_set_exp_get_decl_var(set);
380 impl_val *ptr = deref_impl_val(ret_val, var);
381 char *str;
382
383 if (var->arg->var->array_size) {
384 str = (char *) ptr;
385 } else {
386 str = ptr->ptr;
387 }
388
389 if (str) {
390 RETVAL_STRING(str);
391 } else {
392 RETVAL_EMPTY_STRING();
393 }
394 }
395
396 /*
397 * set $ivar = to_string(dvar, num_exp)
398 */
399 void psi_set_to_stringl(zval *return_value, struct psi_set_exp *set, impl_val *ret_val, struct psi_call_frame *frame)
400 {
401 struct psi_decl_var *var = psi_set_exp_get_decl_var(set);
402 char *str = deref_impl_val(ret_val, var)->ptr;
403
404 if (str) {
405 struct psi_set_exp *sub_exp;
406
407 psi_plist_get(set->inner, 0, &sub_exp);
408 RETVAL_STRINGL(str, psi_long_num_exp(sub_exp->data.num, frame, NULL));
409 } else {
410 RETVAL_EMPTY_STRING();
411 }
412 }
413
414 /*
415 * let dvar = strval($ivar)
416 */
417 impl_val *psi_let_strval(impl_val *tmp, struct psi_decl_arg *spec, token_t impl_type, impl_val *ival, zval *zvalue, void **to_free)
418 {
419 if (ival && impl_type == PSI_T_STRING) {
420 if (ival->zend.str) {
421 tmp->ptr = ival->zend.str->val;
422 } else {
423 tmp->ptr = "";
424 }
425 } else {
426 zend_string *zs = zval_get_string(zvalue);
427 tmp->ptr = estrdup(zs->val);
428 *to_free = tmp->ptr;
429 zend_string_release(zs);
430 }
431
432 return tmp;
433 }
434
435 /*
436 * let dvar = pathval($ivar)
437 */
438 impl_val *psi_let_pathval(impl_val *tmp, struct psi_decl_arg *spec, token_t impl_type, impl_val *ival, zval *zvalue, void **to_free)
439 {
440 tmp = psi_let_strval(tmp, spec, impl_type, ival, zvalue, to_free);
441 if (SUCCESS != php_check_open_basedir(tmp->ptr)) {
442 efree(tmp->ptr);
443 tmp->ptr = NULL;
444 return *to_free = NULL;
445 }
446 return tmp;
447 }
448
449 /*
450 * let dvar = strlen($ivar)
451 */
452 impl_val *psi_let_strlen(impl_val *tmp, struct psi_decl_arg *spec, token_t impl_type, impl_val *ival, zval *zvalue, void **to_free)
453 {
454 if (ival && impl_type == PSI_T_STRING) {
455 if (ival->zend.str) {
456 tmp->lval = ival->zend.str->len;
457 } else {
458 tmp->lval = 0;
459 }
460 } else {
461 zend_string *zs = zval_get_string(zvalue);
462 tmp->lval = zs->len;
463 zend_string_release(zs);
464 }
465
466 return tmp;
467 }
468
469 #if 0
470 static impl_val *iterate(impl_val *val, size_t size, unsigned i, impl_val *tmp)
471 {
472 memset(tmp, 0, sizeof(*tmp));
473 memcpy(tmp, ((char *) val) + size * i, size);
474 return tmp;
475 }
476 #endif
477
478 /*
479 * set $ivar = to_array(dvar,
480 * $foo = to_int(d_foo),
481 * $bar = to_string(d_bar),
482 * $baz = to_array(*d_next, ...)
483 */
484 void psi_set_to_recursive(zval *return_value, struct psi_set_exp *set, impl_val *r_val, struct psi_call_frame *frame) {
485 set->outer->data.func->handler(return_value, set, r_val, frame);
486 }
487
488 /*
489 * set $ivar = to_array(dvar, to_string(*dvar));
490 */
491 void psi_set_to_array_simple(zval *return_value, struct psi_set_exp *set, impl_val *r_val, struct psi_call_frame *frame)
492 {
493 struct psi_set_exp *sub_exp;
494 struct psi_decl_var *var;
495 impl_val *ret_val;
496 char *ptr;
497 size_t size;
498
499 array_init(return_value);
500
501 var = psi_set_exp_get_decl_var(set);
502 ret_val = deref_impl_val(r_val, var);
503 if ((intptr_t) ret_val <= (intptr_t) 0) {
504 return;
505 }
506
507 psi_plist_get(set->inner, 0, &sub_exp);
508
509 size = psi_decl_arg_get_size(var->arg);
510 for (ptr = ret_val->ptr; *(void **) ptr; ptr += size) {
511 zval ele;
512
513 ZVAL_NULL(&ele);
514 sub_exp->data.func->handler(&ele, sub_exp, (void *) ptr, frame);
515 add_next_index_zval(return_value, &ele);
516 }
517 }
518
519 /*
520 * set $ivar = to_array(dvar, num_exp, to_string(*dvar));
521 */
522 void psi_set_to_array_counted(zval *return_value, struct psi_set_exp *set, impl_val *r_val, struct psi_call_frame *frame)
523 {
524 struct psi_set_exp *sub_exp;
525 struct psi_decl_var *var;
526 impl_val *ret_val;
527 char *ptr;
528 size_t size;
529 zend_long count;
530
531 array_init(return_value);
532
533 var = psi_set_exp_get_decl_var(set);
534 ret_val = deref_impl_val(r_val, var);
535 if ((intptr_t) ret_val <= (intptr_t) 0) {
536 return;
537 }
538
539 psi_plist_get(set->inner, 0, &sub_exp);
540 count = psi_long_num_exp(sub_exp->data.num, frame, NULL);
541 psi_plist_get(set->inner, 1, &sub_exp);
542
543 for (ptr = (char *) ret_val; 0 < count--; ptr += size) {
544 size = psi_decl_var_get_size(psi_set_exp_get_decl_var(sub_exp));
545 zval ele;
546
547 ZVAL_NULL(&ele);
548 sub_exp->data.func->handler(&ele, sub_exp, (void *) &ptr, frame);
549 add_next_index_zval(return_value, &ele);
550 }
551 }
552
553 #include "call.h"
554
555 /*
556 * set $ivar = to_array(dvar,
557 * $foo = to_int(d_foo),
558 * $bar = to_string(d_bar));
559 */
560 void psi_set_to_array(zval *return_value, struct psi_set_exp *set, impl_val *r_val, struct psi_call_frame *frame)
561 {
562 struct psi_set_exp *sub_exp;
563 struct psi_decl_var *var;
564 impl_val *ret_val;
565 size_t i = 0;
566
567 array_init(return_value);
568
569 var = psi_set_exp_get_decl_var(set);
570 ret_val = deref_impl_val(r_val, var);
571 if ((intptr_t) ret_val <= (intptr_t) 0) {
572 return;
573 }
574
575 while (psi_plist_get(set->inner, i++, &sub_exp)) {
576 zval ele;
577 struct psi_decl_var *dvar = psi_set_exp_get_decl_var(sub_exp);
578 struct psi_impl_var *ivar = psi_set_exp_get_impl_var(sub_exp);
579 struct psi_call_frame_symbol *sym;
580
581 sym = psi_call_frame_fetch_symbol(frame, dvar);
582 sym->ptr = ((char *) ret_val) + dvar->arg->layout->pos;
583
584 ZVAL_NULL(&ele);
585 psi_set_exp_exec_ex(sub_exp, &ele, sym->ptr, frame);
586 add_assoc_zval(return_value, ivar->name + 1, &ele);
587 }
588 }
589
590 /*
591 * let dvar = count($ivar)
592 */
593 impl_val *psi_let_count(impl_val *tmp, struct psi_decl_arg *spec, token_t impl_type, impl_val *ival, zval *zvalue, void **to_free)
594 {
595 return psi_val_intval(tmp, psi_decl_type_get_real(spec->type)->type, psi_zval_count(zvalue));
596 }
597
598 /*
599 * set $ivar = to_object(dvar)
600 */
601 void psi_set_to_object(zval *return_value, struct psi_set_exp *set, impl_val *r_val, struct psi_call_frame *frame)
602 {
603 struct psi_decl_var *var = psi_set_exp_get_decl_var(set);
604 impl_val *ret_val = deref_impl_val(r_val, var);
605
606 if ((intptr_t) ret_val->ptr > (intptr_t) 0) {
607 object_init_ex(return_value, psi_object_get_class_entry());
608 PSI_OBJ(return_value, NULL)->data = ret_val->ptr;
609 } else {
610 RETVAL_NULL();
611 }
612 }
613
614 /*
615 * let dvar = objval($ivar)
616 */
617 impl_val *psi_let_objval(impl_val *tmp, struct psi_decl_arg *spec, token_t impl_type, impl_val *ival, zval *zvalue, void **to_free)
618 {
619 psi_object *obj;
620
621 if (Z_TYPE_P(zvalue) != IS_OBJECT
622 || !instanceof_function(Z_OBJCE_P(zvalue), psi_object_get_class_entry())) {
623 return NULL;
624 }
625
626 obj = PSI_OBJ(zvalue, NULL);
627 tmp->ptr = obj->data;
628
629 return tmp;
630 }
631