1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2016 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 | Author: Wez Furlong <wez@php.net> |
16 | Marcus Boerger <helly@php.net> |
17 | Sterling Hughes <sterling@php.net> |
18 +----------------------------------------------------------------------+
19 */
20
21 /* $Id$ */
22
23 /* The PDO Statement Handle Class */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include "php.h"
30 #include "php_ini.h"
31 #include "ext/standard/info.h"
32 #include "ext/standard/php_var.h"
33 #include "php_pdo.h"
34 #include "php_pdo_driver.h"
35 #include "php_pdo_int.h"
36 #include "zend_exceptions.h"
37 #include "zend_interfaces.h"
38 #include "php_memory_streams.h"
39
40 /* {{{ arginfo */
41 ZEND_BEGIN_ARG_INFO(arginfo_pdostatement__void, 0)
ZEND_END_ARG_INFO()42 ZEND_END_ARG_INFO()
43
44 ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_execute, 0, 0, 0)
45 ZEND_ARG_INFO(0, bound_input_params) /* array */
46 ZEND_END_ARG_INFO()
47
48 ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_fetch, 0, 0, 0)
49 ZEND_ARG_INFO(0, how)
50 ZEND_ARG_INFO(0, orientation)
51 ZEND_ARG_INFO(0, offset)
52 ZEND_END_ARG_INFO()
53
54 ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_fetchobject, 0, 0, 0)
55 ZEND_ARG_INFO(0, class_name)
56 ZEND_ARG_INFO(0, ctor_args) /* array */
57 ZEND_END_ARG_INFO()
58
59 ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_fetchcolumn, 0, 0, 0)
60 ZEND_ARG_INFO(0, column_number)
61 ZEND_END_ARG_INFO()
62
63 ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_fetchall, 0, 0, 0)
64 ZEND_ARG_INFO(0, how)
65 ZEND_ARG_INFO(0, class_name)
66 ZEND_ARG_INFO(0, ctor_args) /* array */
67 ZEND_END_ARG_INFO()
68
69 ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_bindvalue, 0, 0, 2)
70 ZEND_ARG_INFO(0, paramno)
71 ZEND_ARG_INFO(0, param)
72 ZEND_ARG_INFO(0, type)
73 ZEND_END_ARG_INFO()
74
75 ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_bindparam, 0, 0, 2)
76 ZEND_ARG_INFO(0, paramno)
77 ZEND_ARG_INFO(1, param)
78 ZEND_ARG_INFO(0, type)
79 ZEND_ARG_INFO(0, maxlen)
80 ZEND_ARG_INFO(0, driverdata)
81 ZEND_END_ARG_INFO()
82
83 ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_bindcolumn, 0, 0, 2)
84 ZEND_ARG_INFO(0, column)
85 ZEND_ARG_INFO(1, param)
86 ZEND_ARG_INFO(0, type)
87 ZEND_ARG_INFO(0, maxlen)
88 ZEND_ARG_INFO(0, driverdata)
89 ZEND_END_ARG_INFO()
90
91 ZEND_BEGIN_ARG_INFO(arginfo_pdostatement_setattribute, 0)
92 ZEND_ARG_INFO(0, attribute)
93 ZEND_ARG_INFO(0, value)
94 ZEND_END_ARG_INFO()
95
96 ZEND_BEGIN_ARG_INFO(arginfo_pdostatement_getattribute, 0)
97 ZEND_ARG_INFO(0, attribute)
98 ZEND_END_ARG_INFO()
99
100 ZEND_BEGIN_ARG_INFO(arginfo_pdostatement_getcolumnmeta, 0)
101 ZEND_ARG_INFO(0, column)
102 ZEND_END_ARG_INFO()
103
104 ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_setfetchmode, 0, 0, 1)
105 ZEND_ARG_INFO(0, mode)
106 ZEND_ARG_INFO(0, params)
107 ZEND_END_ARG_INFO()
108 /* }}} */
109
110 #define PHP_STMT_GET_OBJ \
111 pdo_stmt_t *stmt = (pdo_stmt_t*)zend_object_store_get_object(getThis() TSRMLS_CC); \
112 if (!stmt->dbh) { \
113 RETURN_FALSE; \
114 } \
115
116 static PHP_FUNCTION(dbstmt_constructor) /* {{{ */
117 {
118 php_error_docref(NULL TSRMLS_CC, E_ERROR, "You should not create a PDOStatement manually");
119 }
120 /* }}} */
121
rewrite_name_to_position(pdo_stmt_t * stmt,struct pdo_bound_param_data * param TSRMLS_DC)122 static inline int rewrite_name_to_position(pdo_stmt_t *stmt, struct pdo_bound_param_data *param TSRMLS_DC) /* {{{ */
123 {
124 if (stmt->bound_param_map) {
125 /* rewriting :name to ? style.
126 * We need to fixup the parameter numbers on the parameters.
127 * If we find that a given named parameter has been used twice,
128 * we will raise an error, as we can't be sure that it is safe
129 * to bind multiple parameters onto the same zval in the underlying
130 * driver */
131 char *name;
132 int position = 0;
133
134 if (stmt->named_rewrite_template) {
135 /* this is not an error here */
136 return 1;
137 }
138 if (!param->name) {
139 /* do the reverse; map the parameter number to the name */
140 if (SUCCESS == zend_hash_index_find(stmt->bound_param_map, param->paramno, (void**)&name)) {
141 param->name = estrdup(name);
142 param->namelen = strlen(param->name);
143 return 1;
144 }
145 pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined" TSRMLS_CC);
146 return 0;
147 }
148
149 zend_hash_internal_pointer_reset(stmt->bound_param_map);
150 while (SUCCESS == zend_hash_get_current_data(stmt->bound_param_map, (void**)&name)) {
151 if (strcmp(name, param->name)) {
152 position++;
153 zend_hash_move_forward(stmt->bound_param_map);
154 continue;
155 }
156 if (param->paramno >= 0) {
157 pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "PDO refuses to handle repeating the same :named parameter for multiple positions with this driver, as it might be unsafe to do so. Consider using a separate name for each parameter instead" TSRMLS_CC);
158 return -1;
159 }
160 param->paramno = position;
161 return 1;
162 }
163 pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined" TSRMLS_CC);
164 return 0;
165 }
166 return 1;
167 }
168 /* }}} */
169
170 /* trigger callback hook for parameters */
dispatch_param_event(pdo_stmt_t * stmt,enum pdo_param_event event_type TSRMLS_DC)171 static int dispatch_param_event(pdo_stmt_t *stmt, enum pdo_param_event event_type TSRMLS_DC) /* {{{ */
172 {
173 int ret = 1, is_param = 1;
174 struct pdo_bound_param_data *param;
175 HashTable *ht;
176
177 if (!stmt->methods->param_hook) {
178 return 1;
179 }
180
181 ht = stmt->bound_params;
182
183 iterate:
184 if (ht) {
185 zend_hash_internal_pointer_reset(ht);
186 while (SUCCESS == zend_hash_get_current_data(ht, (void**)¶m)) {
187 if (!stmt->methods->param_hook(stmt, param, event_type TSRMLS_CC)) {
188 ret = 0;
189 break;
190 }
191
192 zend_hash_move_forward(ht);
193 }
194 }
195 if (ret && is_param) {
196 ht = stmt->bound_columns;
197 is_param = 0;
198 goto iterate;
199 }
200
201 return ret;
202 }
203 /* }}} */
204
pdo_stmt_describe_columns(pdo_stmt_t * stmt TSRMLS_DC)205 int pdo_stmt_describe_columns(pdo_stmt_t *stmt TSRMLS_DC) /* {{{ */
206 {
207 int col;
208
209 stmt->columns = ecalloc(stmt->column_count, sizeof(struct pdo_column_data));
210
211 for (col = 0; col < stmt->column_count; col++) {
212 if (!stmt->methods->describer(stmt, col TSRMLS_CC)) {
213 return 0;
214 }
215
216 /* if we are applying case conversions on column names, do so now */
217 if (stmt->dbh->native_case != stmt->dbh->desired_case && stmt->dbh->desired_case != PDO_CASE_NATURAL) {
218 char *s = stmt->columns[col].name;
219
220 switch (stmt->dbh->desired_case) {
221 case PDO_CASE_UPPER:
222 while (*s != '\0') {
223 *s = toupper(*s);
224 s++;
225 }
226 break;
227 case PDO_CASE_LOWER:
228 while (*s != '\0') {
229 *s = tolower(*s);
230 s++;
231 }
232 break;
233 default:
234 ;
235 }
236 }
237
238 #if 0
239 /* update the column index on named bound parameters */
240 if (stmt->bound_params) {
241 struct pdo_bound_param_data *param;
242
243 if (SUCCESS == zend_hash_find(stmt->bound_params, stmt->columns[col].name,
244 stmt->columns[col].namelen, (void**)¶m)) {
245 param->paramno = col;
246 }
247 }
248 #endif
249 if (stmt->bound_columns) {
250 struct pdo_bound_param_data *param;
251
252 if (SUCCESS == zend_hash_find(stmt->bound_columns, stmt->columns[col].name,
253 stmt->columns[col].namelen, (void**)¶m)) {
254 param->paramno = col;
255 }
256 }
257
258 }
259 return 1;
260 }
261 /* }}} */
262
get_lazy_object(pdo_stmt_t * stmt,zval * return_value TSRMLS_DC)263 static void get_lazy_object(pdo_stmt_t *stmt, zval *return_value TSRMLS_DC) /* {{{ */
264 {
265 if (Z_TYPE(stmt->lazy_object_ref) == IS_NULL) {
266 Z_TYPE(stmt->lazy_object_ref) = IS_OBJECT;
267 Z_OBJ_HANDLE(stmt->lazy_object_ref) = zend_objects_store_put(stmt, (zend_objects_store_dtor_t)zend_objects_destroy_object, (zend_objects_free_object_storage_t)pdo_row_free_storage, NULL TSRMLS_CC);
268 Z_OBJ_HT(stmt->lazy_object_ref) = &pdo_row_object_handlers;
269 stmt->refcount++;
270 }
271 Z_TYPE_P(return_value) = IS_OBJECT;
272 Z_OBJ_HANDLE_P(return_value) = Z_OBJ_HANDLE(stmt->lazy_object_ref);
273 Z_OBJ_HT_P(return_value) = Z_OBJ_HT(stmt->lazy_object_ref);
274 zend_objects_store_add_ref(return_value TSRMLS_CC);
275 }
276 /* }}} */
277
param_dtor(void * data)278 static void param_dtor(void *data) /* {{{ */
279 {
280 struct pdo_bound_param_data *param = (struct pdo_bound_param_data *)data;
281 TSRMLS_FETCH();
282
283 /* tell the driver that it is going away */
284 if (param->stmt->methods->param_hook) {
285 param->stmt->methods->param_hook(param->stmt, param, PDO_PARAM_EVT_FREE TSRMLS_CC);
286 }
287
288 if (param->name) {
289 efree(param->name);
290 }
291
292 if (param->parameter) {
293 zval_ptr_dtor(&(param->parameter));
294 param->parameter = NULL;
295 }
296 if (param->driver_params) {
297 zval_ptr_dtor(&(param->driver_params));
298 }
299 }
300 /* }}} */
301
really_register_bound_param(struct pdo_bound_param_data * param,pdo_stmt_t * stmt,int is_param TSRMLS_DC)302 static int really_register_bound_param(struct pdo_bound_param_data *param, pdo_stmt_t *stmt, int is_param TSRMLS_DC) /* {{{ */
303 {
304 HashTable *hash;
305 struct pdo_bound_param_data *pparam = NULL;
306
307 hash = is_param ? stmt->bound_params : stmt->bound_columns;
308
309 if (!hash) {
310 ALLOC_HASHTABLE(hash);
311 zend_hash_init(hash, 13, NULL, param_dtor, 0);
312
313 if (is_param) {
314 stmt->bound_params = hash;
315 } else {
316 stmt->bound_columns = hash;
317 }
318 }
319
320 if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_STR && param->max_value_len <= 0 && ! ZVAL_IS_NULL(param->parameter)) {
321 if (Z_TYPE_P(param->parameter) == IS_DOUBLE) {
322 char *p;
323 int len = spprintf(&p, 0, "%.*H", (int) EG(precision), Z_DVAL_P(param->parameter));
324 ZVAL_STRINGL(param->parameter, p, len, 0);
325 } else {
326 convert_to_string(param->parameter);
327 }
328 } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_INT && Z_TYPE_P(param->parameter) == IS_BOOL) {
329 convert_to_long(param->parameter);
330 } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_BOOL && Z_TYPE_P(param->parameter) == IS_LONG) {
331 convert_to_boolean(param->parameter);
332 }
333
334 param->stmt = stmt;
335 param->is_param = is_param;
336
337 if (param->driver_params) {
338 Z_ADDREF_P(param->driver_params);
339 }
340
341 if (!is_param && param->name && stmt->columns) {
342 /* try to map the name to the column */
343 int i;
344
345 for (i = 0; i < stmt->column_count; i++) {
346 if (strcmp(stmt->columns[i].name, param->name) == 0) {
347 param->paramno = i;
348 break;
349 }
350 }
351
352 /* if you prepare and then execute passing an array of params keyed by names,
353 * then this will trigger, and we don't want that */
354 if (param->paramno == -1) {
355 char *tmp;
356 spprintf(&tmp, 0, "Did not find column name '%s' in the defined columns; it will not be bound", param->name);
357 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", tmp TSRMLS_CC);
358 efree(tmp);
359 }
360 }
361
362 if (param->name) {
363 if (is_param && param->name[0] != ':') {
364 char *temp = emalloc(++param->namelen + 1);
365 temp[0] = ':';
366 memmove(temp+1, param->name, param->namelen);
367 param->name = temp;
368 } else {
369 param->name = estrndup(param->name, param->namelen);
370 }
371 }
372
373 if (is_param && !rewrite_name_to_position(stmt, param TSRMLS_CC)) {
374 if (param->name) {
375 efree(param->name);
376 param->name = NULL;
377 }
378 return 0;
379 }
380
381 /* ask the driver to perform any normalization it needs on the
382 * parameter name. Note that it is illegal for the driver to take
383 * a reference to param, as it resides in transient storage only
384 * at this time. */
385 if (stmt->methods->param_hook) {
386 if (!stmt->methods->param_hook(stmt, param, PDO_PARAM_EVT_NORMALIZE
387 TSRMLS_CC)) {
388 if (param->name) {
389 efree(param->name);
390 param->name = NULL;
391 }
392 return 0;
393 }
394 }
395
396 /* delete any other parameter registered with this number.
397 * If the parameter is named, it will be removed and correctly
398 * disposed of by the hash_update call that follows */
399 if (param->paramno >= 0) {
400 zend_hash_index_del(hash, param->paramno);
401 }
402
403 /* allocate storage for the parameter, keyed by its "canonical" name */
404 if (param->name) {
405 zend_hash_update(hash, param->name, param->namelen, param,
406 sizeof(*param), (void**)&pparam);
407 } else {
408 zend_hash_index_update(hash, param->paramno, param, sizeof(*param),
409 (void**)&pparam);
410 }
411
412 /* tell the driver we just created a parameter */
413 if (stmt->methods->param_hook) {
414 if (!stmt->methods->param_hook(stmt, pparam, PDO_PARAM_EVT_ALLOC
415 TSRMLS_CC)) {
416 /* undo storage allocation; the hash will free the parameter
417 * name if required */
418 if (pparam->name) {
419 zend_hash_del(hash, pparam->name, pparam->namelen);
420 } else {
421 zend_hash_index_del(hash, pparam->paramno);
422 }
423 /* param->parameter is freed by hash dtor */
424 param->parameter = NULL;
425 return 0;
426 }
427 }
428 return 1;
429 }
430 /* }}} */
431
432 /* {{{ proto bool PDOStatement::execute([array $bound_input_params])
433 Execute a prepared statement, optionally binding parameters */
PHP_METHOD(PDOStatement,execute)434 static PHP_METHOD(PDOStatement, execute)
435 {
436 zval *input_params = NULL;
437 int ret = 1;
438 PHP_STMT_GET_OBJ;
439
440 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!", &input_params)) {
441 RETURN_FALSE;
442 }
443
444 PDO_STMT_CLEAR_ERR();
445
446 if (input_params) {
447 struct pdo_bound_param_data param;
448 zval **tmp;
449 uint str_length;
450 ulong num_index;
451
452 if (stmt->bound_params) {
453 zend_hash_destroy(stmt->bound_params);
454 FREE_HASHTABLE(stmt->bound_params);
455 stmt->bound_params = NULL;
456 }
457
458 zend_hash_internal_pointer_reset(Z_ARRVAL_P(input_params));
459 while (SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(input_params), (void*)&tmp)) {
460 memset(¶m, 0, sizeof(param));
461
462 if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(Z_ARRVAL_P(input_params),
463 ¶m.name, &str_length, &num_index, 0, NULL)) {
464 /* yes this is correct. we don't want to count the null byte. ask wez */
465 param.namelen = str_length - 1;
466 param.paramno = -1;
467 } else {
468 /* we're okay to be zero based here */
469 if (num_index < 0) {
470 pdo_raise_impl_error(stmt->dbh, stmt, "HY093", NULL TSRMLS_CC);
471 RETURN_FALSE;
472 }
473 param.paramno = num_index;
474 }
475
476 param.param_type = PDO_PARAM_STR;
477 MAKE_STD_ZVAL(param.parameter);
478 MAKE_COPY_ZVAL(tmp, param.parameter);
479
480 if (!really_register_bound_param(¶m, stmt, 1 TSRMLS_CC)) {
481 if (param.parameter) {
482 zval_ptr_dtor(¶m.parameter);
483 }
484 RETURN_FALSE;
485 }
486
487 zend_hash_move_forward(Z_ARRVAL_P(input_params));
488 }
489 }
490
491 if (PDO_PLACEHOLDER_NONE == stmt->supports_placeholders) {
492 /* handle the emulated parameter binding,
493 * stmt->active_query_string holds the query with binds expanded and
494 * quoted.
495 */
496
497 ret = pdo_parse_params(stmt, stmt->query_string, stmt->query_stringlen,
498 &stmt->active_query_string, &stmt->active_query_stringlen TSRMLS_CC);
499
500 if (ret == 0) {
501 /* no changes were made */
502 stmt->active_query_string = stmt->query_string;
503 stmt->active_query_stringlen = stmt->query_stringlen;
504 ret = 1;
505 } else if (ret == -1) {
506 /* something broke */
507 PDO_HANDLE_STMT_ERR();
508 RETURN_FALSE;
509 }
510 } else if (!dispatch_param_event(stmt, PDO_PARAM_EVT_EXEC_PRE TSRMLS_CC)) {
511 PDO_HANDLE_STMT_ERR();
512 RETURN_FALSE;
513 }
514 if (stmt->methods->executer(stmt TSRMLS_CC)) {
515 if (stmt->active_query_string && stmt->active_query_string != stmt->query_string) {
516 efree(stmt->active_query_string);
517 }
518 stmt->active_query_string = NULL;
519 if (!stmt->executed) {
520 /* this is the first execute */
521
522 if (stmt->dbh->alloc_own_columns && !stmt->columns) {
523 /* for "big boy" drivers, we need to allocate memory to fetch
524 * the results into, so lets do that now */
525 ret = pdo_stmt_describe_columns(stmt TSRMLS_CC);
526 }
527
528 stmt->executed = 1;
529 }
530
531 if (ret && !dispatch_param_event(stmt, PDO_PARAM_EVT_EXEC_POST TSRMLS_CC)) {
532 RETURN_FALSE;
533 }
534
535 RETURN_BOOL(ret);
536 }
537 if (stmt->active_query_string && stmt->active_query_string != stmt->query_string) {
538 efree(stmt->active_query_string);
539 }
540 stmt->active_query_string = NULL;
541 PDO_HANDLE_STMT_ERR();
542 RETURN_FALSE;
543 }
544 /* }}} */
545
fetch_value(pdo_stmt_t * stmt,zval * dest,int colno,int * type_override TSRMLS_DC)546 static inline void fetch_value(pdo_stmt_t *stmt, zval *dest, int colno, int *type_override TSRMLS_DC) /* {{{ */
547 {
548 struct pdo_column_data *col;
549 char *value = NULL;
550 unsigned long value_len = 0;
551 int caller_frees = 0;
552 int type, new_type;
553
554 col = &stmt->columns[colno];
555 type = PDO_PARAM_TYPE(col->param_type);
556 new_type = type_override ? PDO_PARAM_TYPE(*type_override) : type;
557
558 value = NULL;
559 value_len = 0;
560
561 stmt->methods->get_col(stmt, colno, &value, &value_len, &caller_frees TSRMLS_CC);
562
563 switch (type) {
564 case PDO_PARAM_ZVAL:
565 if (value && value_len == sizeof(zval)) {
566 int need_copy = (new_type != PDO_PARAM_ZVAL || stmt->dbh->stringify) ? 1 : 0;
567 zval *zv = *(zval**)value;
568 ZVAL_ZVAL(dest, zv, need_copy, 1);
569 } else {
570 ZVAL_NULL(dest);
571 }
572
573 if (Z_TYPE_P(dest) == IS_NULL) {
574 type = new_type;
575 }
576 break;
577
578 case PDO_PARAM_INT:
579 if (value && value_len == sizeof(long)) {
580 ZVAL_LONG(dest, *(long*)value);
581 break;
582 }
583 ZVAL_NULL(dest);
584 break;
585
586 case PDO_PARAM_BOOL:
587 if (value && value_len == sizeof(zend_bool)) {
588 ZVAL_BOOL(dest, *(zend_bool*)value);
589 break;
590 }
591 ZVAL_NULL(dest);
592 break;
593
594 case PDO_PARAM_LOB:
595 if (value == NULL) {
596 ZVAL_NULL(dest);
597 } else if (value_len == 0) {
598 /* Warning, empty strings need to be passed as stream */
599 if (stmt->dbh->stringify || new_type == PDO_PARAM_STR) {
600 char *buf = NULL;
601 size_t len;
602 len = php_stream_copy_to_mem((php_stream*)value, &buf, PHP_STREAM_COPY_ALL, 0);
603 if(buf == NULL) {
604 ZVAL_EMPTY_STRING(dest);
605 } else {
606 ZVAL_STRINGL(dest, buf, len, 0);
607 }
608 php_stream_close((php_stream*)value);
609 } else {
610 php_stream_to_zval((php_stream*)value, dest);
611 }
612 } else if (!stmt->dbh->stringify && new_type != PDO_PARAM_STR) {
613 /* they gave us a string, but LOBs are represented as streams in PDO */
614 php_stream *stm;
615 #ifdef TEMP_STREAM_TAKE_BUFFER
616 if (caller_frees) {
617 stm = php_stream_memory_open(TEMP_STREAM_TAKE_BUFFER, value, value_len);
618 if (stm) {
619 caller_frees = 0;
620 }
621 } else
622 #endif
623 {
624 stm = php_stream_memory_open(TEMP_STREAM_READONLY, value, value_len);
625 }
626 if (stm) {
627 php_stream_to_zval(stm, dest);
628 } else {
629 ZVAL_NULL(dest);
630 }
631 } else {
632 ZVAL_STRINGL(dest, value, value_len, !caller_frees);
633 if (caller_frees) {
634 caller_frees = 0;
635 }
636 }
637 break;
638
639 case PDO_PARAM_STR:
640 if (value && !(value_len == 0 && stmt->dbh->oracle_nulls == PDO_NULL_EMPTY_STRING)) {
641 ZVAL_STRINGL(dest, value, value_len, !caller_frees);
642 if (caller_frees) {
643 caller_frees = 0;
644 }
645 break;
646 }
647 default:
648 ZVAL_NULL(dest);
649 }
650
651 if (type != new_type) {
652 switch (new_type) {
653 case PDO_PARAM_INT:
654 convert_to_long_ex(&dest);
655 break;
656 case PDO_PARAM_BOOL:
657 convert_to_boolean_ex(&dest);
658 break;
659 case PDO_PARAM_STR:
660 convert_to_string_ex(&dest);
661 break;
662 case PDO_PARAM_NULL:
663 convert_to_null_ex(&dest);
664 break;
665 default:
666 ;
667 }
668 }
669
670 if (caller_frees && value) {
671 efree(value);
672 }
673
674 if (stmt->dbh->stringify) {
675 switch (Z_TYPE_P(dest)) {
676 case IS_LONG:
677 case IS_DOUBLE:
678 convert_to_string(dest);
679 break;
680 }
681 }
682
683 if (Z_TYPE_P(dest) == IS_NULL && stmt->dbh->oracle_nulls == PDO_NULL_TO_STRING) {
684 ZVAL_EMPTY_STRING(dest);
685 }
686 }
687 /* }}} */
688
do_fetch_common(pdo_stmt_t * stmt,enum pdo_fetch_orientation ori,long offset,int do_bind TSRMLS_DC)689 static int do_fetch_common(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori,
690 long offset, int do_bind TSRMLS_DC) /* {{{ */
691 {
692 if (!stmt->executed) {
693 return 0;
694 }
695
696 if (!dispatch_param_event(stmt, PDO_PARAM_EVT_FETCH_PRE TSRMLS_CC)) {
697 return 0;
698 }
699
700 if (!stmt->methods->fetcher(stmt, ori, offset TSRMLS_CC)) {
701 return 0;
702 }
703
704 /* some drivers might need to describe the columns now */
705 if (!stmt->columns && !pdo_stmt_describe_columns(stmt TSRMLS_CC)) {
706 return 0;
707 }
708
709 if (!dispatch_param_event(stmt, PDO_PARAM_EVT_FETCH_POST TSRMLS_CC)) {
710 return 0;
711 }
712
713 if (do_bind && stmt->bound_columns) {
714 /* update those bound column variables now */
715 struct pdo_bound_param_data *param;
716
717 zend_hash_internal_pointer_reset(stmt->bound_columns);
718 while (SUCCESS == zend_hash_get_current_data(stmt->bound_columns, (void**)¶m)) {
719 if (param->paramno >= 0) {
720 convert_to_string(param->parameter);
721
722 /* delete old value */
723 zval_dtor(param->parameter);
724
725 /* set new value */
726 fetch_value(stmt, param->parameter, param->paramno, (int *)¶m->param_type TSRMLS_CC);
727
728 /* TODO: some smart thing that avoids duplicating the value in the
729 * general loop below. For now, if you're binding output columns,
730 * it's better to use LAZY or BOUND fetches if you want to shave
731 * off those cycles */
732 }
733
734 zend_hash_move_forward(stmt->bound_columns);
735 }
736 }
737
738 return 1;
739 }
740 /* }}} */
741
do_fetch_class_prepare(pdo_stmt_t * stmt TSRMLS_DC)742 static int do_fetch_class_prepare(pdo_stmt_t *stmt TSRMLS_DC) /* {{{ */
743 {
744 zend_class_entry * ce = stmt->fetch.cls.ce;
745 zend_fcall_info * fci = &stmt->fetch.cls.fci;
746 zend_fcall_info_cache * fcc = &stmt->fetch.cls.fcc;
747
748 fci->size = sizeof(zend_fcall_info);
749
750 if (!ce) {
751 stmt->fetch.cls.ce = ZEND_STANDARD_CLASS_DEF_PTR;
752 ce = ZEND_STANDARD_CLASS_DEF_PTR;
753 }
754
755 if (ce->constructor) {
756 fci->function_table = &ce->function_table;
757 fci->function_name = NULL;
758 fci->symbol_table = NULL;
759 fci->retval_ptr_ptr = &stmt->fetch.cls.retval_ptr;
760 fci->params = NULL;
761 fci->no_separation = 1;
762
763 zend_fcall_info_args(fci, stmt->fetch.cls.ctor_args TSRMLS_CC);
764
765 fcc->initialized = 1;
766 fcc->function_handler = ce->constructor;
767 fcc->calling_scope = EG(scope);
768 fcc->called_scope = ce;
769 return 1;
770 } else if (stmt->fetch.cls.ctor_args) {
771 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "user-supplied class does not have a constructor, use NULL for the ctor_params parameter, or simply omit it" TSRMLS_CC);
772 return 0;
773 } else {
774 return 1; /* no ctor no args is also ok */
775 }
776 }
777 /* }}} */
778
make_callable_ex(pdo_stmt_t * stmt,zval * callable,zend_fcall_info * fci,zend_fcall_info_cache * fcc,int num_args TSRMLS_DC)779 static int make_callable_ex(pdo_stmt_t *stmt, zval *callable, zend_fcall_info * fci, zend_fcall_info_cache * fcc, int num_args TSRMLS_DC) /* {{{ */
780 {
781 char *is_callable_error = NULL;
782
783 if (zend_fcall_info_init(callable, 0, fci, fcc, NULL, &is_callable_error TSRMLS_CC) == FAILURE) {
784 if (is_callable_error) {
785 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", is_callable_error TSRMLS_CC);
786 efree(is_callable_error);
787 } else {
788 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "user-supplied function must be a valid callback" TSRMLS_CC);
789 }
790 return 0;
791 }
792 if (is_callable_error) {
793 /* Possible E_STRICT error message */
794 efree(is_callable_error);
795 }
796
797 fci->param_count = num_args; /* probably less */
798 fci->params = safe_emalloc(sizeof(zval**), num_args, 0);
799
800 return 1;
801 }
802 /* }}} */
803
do_fetch_func_prepare(pdo_stmt_t * stmt TSRMLS_DC)804 static int do_fetch_func_prepare(pdo_stmt_t *stmt TSRMLS_DC) /* {{{ */
805 {
806 zend_fcall_info * fci = &stmt->fetch.cls.fci;
807 zend_fcall_info_cache * fcc = &stmt->fetch.cls.fcc;
808
809 if (!make_callable_ex(stmt, stmt->fetch.func.function, fci, fcc, stmt->column_count TSRMLS_CC)) {
810 return 0;
811 } else {
812 stmt->fetch.func.values = safe_emalloc(sizeof(zval*), stmt->column_count, 0);
813 return 1;
814 }
815 }
816 /* }}} */
817
do_fetch_opt_finish(pdo_stmt_t * stmt,int free_ctor_agrs TSRMLS_DC)818 static int do_fetch_opt_finish(pdo_stmt_t *stmt, int free_ctor_agrs TSRMLS_DC) /* {{{ */
819 {
820 /* fci.size is used to check if it is valid */
821 if (stmt->fetch.cls.fci.size && stmt->fetch.cls.fci.params) {
822 efree(stmt->fetch.cls.fci.params);
823 stmt->fetch.cls.fci.params = NULL;
824 }
825 stmt->fetch.cls.fci.size = 0;
826 if (stmt->fetch.cls.ctor_args && free_ctor_agrs) {
827 zval_ptr_dtor(&stmt->fetch.cls.ctor_args);
828 stmt->fetch.cls.ctor_args = NULL;
829 stmt->fetch.cls.fci.param_count = 0;
830 }
831 if (stmt->fetch.func.values) {
832 efree(stmt->fetch.func.values);
833 stmt->fetch.func.values = NULL;
834 }
835 return 1;
836 }
837 /* }}} */
838
839 /* perform a fetch. If do_bind is true, update any bound columns.
840 * If return_value is not null, store values into it according to HOW. */
do_fetch(pdo_stmt_t * stmt,int do_bind,zval * return_value,enum pdo_fetch_type how,enum pdo_fetch_orientation ori,long offset,zval * return_all TSRMLS_DC)841 static int do_fetch(pdo_stmt_t *stmt, int do_bind, zval *return_value,
842 enum pdo_fetch_type how, enum pdo_fetch_orientation ori, long offset, zval *return_all TSRMLS_DC) /* {{{ */
843 {
844 int flags, idx, old_arg_count = 0;
845 zend_class_entry *ce = NULL, *old_ce = NULL;
846 zval grp_val, *grp, **pgrp, *retval, *old_ctor_args = NULL;
847 int colno;
848
849 if (how == PDO_FETCH_USE_DEFAULT) {
850 how = stmt->default_fetch_type;
851 }
852 flags = how & PDO_FETCH_FLAGS;
853 how = how & ~PDO_FETCH_FLAGS;
854
855 if (!do_fetch_common(stmt, ori, offset, do_bind TSRMLS_CC)) {
856 return 0;
857 }
858
859 if (how == PDO_FETCH_BOUND) {
860 RETVAL_TRUE;
861 return 1;
862 }
863
864 if (flags & PDO_FETCH_GROUP && stmt->fetch.column == -1) {
865 colno = 1;
866 } else {
867 colno = stmt->fetch.column;
868 }
869
870 if (return_value) {
871 int i = 0;
872
873 if (how == PDO_FETCH_LAZY) {
874 get_lazy_object(stmt, return_value TSRMLS_CC);
875 return 1;
876 }
877
878 RETVAL_FALSE;
879
880 switch (how) {
881 case PDO_FETCH_USE_DEFAULT:
882 case PDO_FETCH_ASSOC:
883 case PDO_FETCH_BOTH:
884 case PDO_FETCH_NUM:
885 case PDO_FETCH_NAMED:
886 if (!return_all) {
887 ALLOC_HASHTABLE(return_value->value.ht);
888 zend_hash_init(return_value->value.ht, stmt->column_count, NULL, ZVAL_PTR_DTOR, 0);
889 Z_TYPE_P(return_value) = IS_ARRAY;
890 } else {
891 array_init(return_value);
892 }
893 break;
894
895 case PDO_FETCH_KEY_PAIR:
896 if (stmt->column_count != 2) {
897 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_KEY_PAIR fetch mode requires the result set to contain extactly 2 columns." TSRMLS_CC);
898 return 0;
899 }
900 if (!return_all) {
901 array_init(return_value);
902 }
903 break;
904
905 case PDO_FETCH_COLUMN:
906 if (colno >= 0 && colno < stmt->column_count) {
907 if (flags == PDO_FETCH_GROUP && stmt->fetch.column == -1) {
908 fetch_value(stmt, return_value, 1, NULL TSRMLS_CC);
909 } else if (flags == PDO_FETCH_GROUP && colno) {
910 fetch_value(stmt, return_value, 0, NULL TSRMLS_CC);
911 } else {
912 fetch_value(stmt, return_value, colno, NULL TSRMLS_CC);
913 }
914 if (!return_all) {
915 return 1;
916 } else {
917 break;
918 }
919 } else {
920 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Invalid column index" TSRMLS_CC);
921 }
922 return 0;
923
924 case PDO_FETCH_OBJ:
925 object_init_ex(return_value, ZEND_STANDARD_CLASS_DEF_PTR);
926 break;
927
928 case PDO_FETCH_CLASS:
929 if (flags & PDO_FETCH_CLASSTYPE) {
930 zval val;
931 zend_class_entry **cep;
932
933 old_ce = stmt->fetch.cls.ce;
934 old_ctor_args = stmt->fetch.cls.ctor_args;
935 old_arg_count = stmt->fetch.cls.fci.param_count;
936 do_fetch_opt_finish(stmt, 0 TSRMLS_CC);
937
938 INIT_PZVAL(&val);
939 fetch_value(stmt, &val, i++, NULL TSRMLS_CC);
940 if (Z_TYPE(val) != IS_NULL) {
941 convert_to_string(&val);
942 if (zend_lookup_class(Z_STRVAL(val), Z_STRLEN(val), &cep TSRMLS_CC) == FAILURE) {
943 stmt->fetch.cls.ce = ZEND_STANDARD_CLASS_DEF_PTR;
944 } else {
945 stmt->fetch.cls.ce = *cep;
946 }
947 }
948
949 do_fetch_class_prepare(stmt TSRMLS_CC);
950 zval_dtor(&val);
951 }
952 ce = stmt->fetch.cls.ce;
953 if (!ce) {
954 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "No fetch class specified" TSRMLS_CC);
955 return 0;
956 }
957 if ((flags & PDO_FETCH_SERIALIZE) == 0) {
958 object_init_ex(return_value, ce);
959 if (!stmt->fetch.cls.fci.size) {
960 if (!do_fetch_class_prepare(stmt TSRMLS_CC))
961 {
962 return 0;
963 }
964 }
965 if (ce->constructor && (flags & PDO_FETCH_PROPS_LATE)) {
966 stmt->fetch.cls.fci.object_ptr = return_value;
967 stmt->fetch.cls.fcc.object_ptr = return_value;
968 if (zend_call_function(&stmt->fetch.cls.fci, &stmt->fetch.cls.fcc TSRMLS_CC) == FAILURE) {
969 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not call class constructor" TSRMLS_CC);
970 return 0;
971 } else {
972 if (stmt->fetch.cls.retval_ptr) {
973 zval_ptr_dtor(&stmt->fetch.cls.retval_ptr);
974 }
975 }
976 }
977 }
978 break;
979
980 case PDO_FETCH_INTO:
981 if (!stmt->fetch.into) {
982 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "No fetch-into object specified." TSRMLS_CC);
983 return 0;
984 break;
985 }
986
987 Z_TYPE_P(return_value) = IS_OBJECT;
988 Z_OBJ_HANDLE_P(return_value) = Z_OBJ_HANDLE_P(stmt->fetch.into);
989 Z_OBJ_HT_P(return_value) = Z_OBJ_HT_P(stmt->fetch.into);
990 zend_objects_store_add_ref(stmt->fetch.into TSRMLS_CC);
991
992 if (zend_get_class_entry(return_value TSRMLS_CC) == ZEND_STANDARD_CLASS_DEF_PTR) {
993 how = PDO_FETCH_OBJ;
994 }
995 break;
996
997 case PDO_FETCH_FUNC:
998 if (!stmt->fetch.func.function) {
999 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "No fetch function specified" TSRMLS_CC);
1000 return 0;
1001 }
1002 if (!stmt->fetch.func.fci.size) {
1003 if (!do_fetch_func_prepare(stmt TSRMLS_CC))
1004 {
1005 return 0;
1006 }
1007 }
1008 break;
1009
1010
1011 default:
1012 /* shouldn't happen */
1013 return 0;
1014 }
1015
1016 if (return_all && how != PDO_FETCH_KEY_PAIR) {
1017 INIT_PZVAL(&grp_val);
1018 if (flags == PDO_FETCH_GROUP && how == PDO_FETCH_COLUMN && stmt->fetch.column > 0) {
1019 fetch_value(stmt, &grp_val, colno, NULL TSRMLS_CC);
1020 } else {
1021 fetch_value(stmt, &grp_val, i, NULL TSRMLS_CC);
1022 }
1023 convert_to_string(&grp_val);
1024 if (how == PDO_FETCH_COLUMN) {
1025 i = stmt->column_count; /* no more data to fetch */
1026 } else {
1027 i++;
1028 }
1029 }
1030
1031 for (idx = 0; i < stmt->column_count; i++, idx++) {
1032 zval *val;
1033 MAKE_STD_ZVAL(val);
1034 fetch_value(stmt, val, i, NULL TSRMLS_CC);
1035
1036 switch (how) {
1037 case PDO_FETCH_ASSOC:
1038 add_assoc_zval(return_value, stmt->columns[i].name, val);
1039 break;
1040
1041 case PDO_FETCH_KEY_PAIR:
1042 {
1043 zval *tmp;
1044 MAKE_STD_ZVAL(tmp);
1045 fetch_value(stmt, tmp, ++i, NULL TSRMLS_CC);
1046
1047 if (Z_TYPE_P(val) == IS_LONG) {
1048 zend_hash_index_update((return_all ? Z_ARRVAL_P(return_all) : Z_ARRVAL_P(return_value)), Z_LVAL_P(val), &tmp, sizeof(zval *), NULL);
1049 } else {
1050 convert_to_string(val);
1051 zend_symtable_update((return_all ? Z_ARRVAL_P(return_all) : Z_ARRVAL_P(return_value)), Z_STRVAL_P(val), Z_STRLEN_P(val) + 1, &tmp, sizeof(zval *), NULL);
1052 }
1053 zval_ptr_dtor(&val);
1054 return 1;
1055 }
1056 break;
1057
1058 case PDO_FETCH_USE_DEFAULT:
1059 case PDO_FETCH_BOTH:
1060 add_assoc_zval(return_value, stmt->columns[i].name, val);
1061 Z_ADDREF_P(val);
1062 add_next_index_zval(return_value, val);
1063 break;
1064
1065 case PDO_FETCH_NAMED:
1066 /* already have an item with this name? */
1067 {
1068 zval **curr_val = NULL;
1069 if (zend_hash_find(Z_ARRVAL_P(return_value), stmt->columns[i].name,
1070 strlen(stmt->columns[i].name)+1,
1071 (void**)&curr_val) == SUCCESS) {
1072 zval *arr;
1073 if (Z_TYPE_PP(curr_val) != IS_ARRAY) {
1074 /* a little bit of black magic here:
1075 * we're creating a new array and swapping it for the
1076 * zval that's already stored in the hash under the name
1077 * we want. We then add that zval to the array.
1078 * This is effectively the same thing as:
1079 * if (!is_array($hash[$name])) {
1080 * $hash[$name] = array($hash[$name]);
1081 * }
1082 * */
1083 zval *cur;
1084
1085 MAKE_STD_ZVAL(arr);
1086 array_init(arr);
1087
1088 cur = *curr_val;
1089 *curr_val = arr;
1090
1091 add_next_index_zval(arr, cur);
1092 } else {
1093 arr = *curr_val;
1094 }
1095 add_next_index_zval(arr, val);
1096 } else {
1097 add_assoc_zval(return_value, stmt->columns[i].name, val);
1098 }
1099 }
1100 break;
1101
1102 case PDO_FETCH_NUM:
1103 add_next_index_zval(return_value, val);
1104 break;
1105
1106 case PDO_FETCH_OBJ:
1107 case PDO_FETCH_INTO:
1108 zend_update_property(NULL, return_value,
1109 stmt->columns[i].name, stmt->columns[i].namelen,
1110 val TSRMLS_CC);
1111 zval_ptr_dtor(&val);
1112 break;
1113
1114 case PDO_FETCH_CLASS:
1115 if ((flags & PDO_FETCH_SERIALIZE) == 0 || idx) {
1116 zend_update_property(ce, return_value,
1117 stmt->columns[i].name, stmt->columns[i].namelen,
1118 val TSRMLS_CC);
1119 zval_ptr_dtor(&val);
1120 } else {
1121 #ifdef MBO_0
1122 php_unserialize_data_t var_hash;
1123
1124 PHP_VAR_UNSERIALIZE_INIT(var_hash);
1125 if (php_var_unserialize(&return_value, (const unsigned char**)&Z_STRVAL_P(val), Z_STRVAL_P(val)+Z_STRLEN_P(val), NULL TSRMLS_CC) == FAILURE) {
1126 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "cannot unserialize data" TSRMLS_CC);
1127 PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1128 return 0;
1129 }
1130 PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1131 #endif
1132 if (!ce->unserialize) {
1133 zval_ptr_dtor(&val);
1134 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "cannot unserialize class" TSRMLS_CC);
1135 return 0;
1136 } else if (ce->unserialize(&return_value, ce, (unsigned char *)(Z_TYPE_P(val) == IS_STRING ? Z_STRVAL_P(val) : ""), Z_TYPE_P(val) == IS_STRING ? Z_STRLEN_P(val) : 0, NULL TSRMLS_CC) == FAILURE) {
1137 zval_ptr_dtor(&val);
1138 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "cannot unserialize class" TSRMLS_CC);
1139 zval_dtor(return_value);
1140 ZVAL_NULL(return_value);
1141 return 0;
1142 } else {
1143 zval_ptr_dtor(&val);
1144 }
1145 }
1146 break;
1147
1148 case PDO_FETCH_FUNC:
1149 stmt->fetch.func.values[idx] = val;
1150 stmt->fetch.cls.fci.params[idx] = &stmt->fetch.func.values[idx];
1151 break;
1152
1153 default:
1154 zval_ptr_dtor(&val);
1155 pdo_raise_impl_error(stmt->dbh, stmt, "22003", "mode is out of range" TSRMLS_CC);
1156 return 0;
1157 break;
1158 }
1159 }
1160
1161 switch (how) {
1162 case PDO_FETCH_CLASS:
1163 if (ce->constructor && !(flags & (PDO_FETCH_PROPS_LATE | PDO_FETCH_SERIALIZE))) {
1164 stmt->fetch.cls.fci.object_ptr = return_value;
1165 stmt->fetch.cls.fcc.object_ptr = return_value;
1166 if (zend_call_function(&stmt->fetch.cls.fci, &stmt->fetch.cls.fcc TSRMLS_CC) == FAILURE) {
1167 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not call class constructor" TSRMLS_CC);
1168 return 0;
1169 } else {
1170 if (stmt->fetch.cls.retval_ptr) {
1171 zval_ptr_dtor(&stmt->fetch.cls.retval_ptr);
1172 }
1173 }
1174 }
1175 if (flags & PDO_FETCH_CLASSTYPE) {
1176 do_fetch_opt_finish(stmt, 0 TSRMLS_CC);
1177 stmt->fetch.cls.ce = old_ce;
1178 stmt->fetch.cls.ctor_args = old_ctor_args;
1179 stmt->fetch.cls.fci.param_count = old_arg_count;
1180 }
1181 break;
1182
1183 case PDO_FETCH_FUNC:
1184 stmt->fetch.func.fci.param_count = idx;
1185 stmt->fetch.func.fci.retval_ptr_ptr = &retval;
1186 if (zend_call_function(&stmt->fetch.func.fci, &stmt->fetch.func.fcc TSRMLS_CC) == FAILURE) {
1187 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not call user-supplied function" TSRMLS_CC);
1188 return 0;
1189 } else {
1190 if (return_all) {
1191 zval_ptr_dtor(&return_value); /* we don't need that */
1192 return_value = retval;
1193 } else if (retval) {
1194 MAKE_COPY_ZVAL(&retval, return_value);
1195 zval_ptr_dtor(&retval);
1196 }
1197 }
1198 while(idx--) {
1199 zval_ptr_dtor(&stmt->fetch.func.values[idx]);
1200 }
1201 break;
1202
1203 default:
1204 break;
1205 }
1206
1207 if (return_all) {
1208 if ((flags & PDO_FETCH_UNIQUE) == PDO_FETCH_UNIQUE) {
1209 add_assoc_zval(return_all, Z_STRVAL(grp_val), return_value);
1210 } else {
1211 if (zend_symtable_find(Z_ARRVAL_P(return_all), Z_STRVAL(grp_val), Z_STRLEN(grp_val)+1, (void**)&pgrp) == FAILURE) {
1212 MAKE_STD_ZVAL(grp);
1213 array_init(grp);
1214 add_assoc_zval(return_all, Z_STRVAL(grp_val), grp);
1215 } else {
1216 grp = *pgrp;
1217 }
1218 add_next_index_zval(grp, return_value);
1219 }
1220 zval_dtor(&grp_val);
1221 }
1222
1223 }
1224
1225 return 1;
1226 }
1227 /* }}} */
1228
pdo_stmt_verify_mode(pdo_stmt_t * stmt,long mode,int fetch_all TSRMLS_DC)1229 static int pdo_stmt_verify_mode(pdo_stmt_t *stmt, long mode, int fetch_all TSRMLS_DC) /* {{{ */
1230 {
1231 int flags = mode & PDO_FETCH_FLAGS;
1232
1233 mode = mode & ~PDO_FETCH_FLAGS;
1234
1235 if (mode < 0 || mode > PDO_FETCH__MAX) {
1236 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "invalid fetch mode" TSRMLS_CC);
1237 return 0;
1238 }
1239
1240 if (mode == PDO_FETCH_USE_DEFAULT) {
1241 flags = stmt->default_fetch_type & PDO_FETCH_FLAGS;
1242 mode = stmt->default_fetch_type & ~PDO_FETCH_FLAGS;
1243 }
1244
1245 switch(mode) {
1246 case PDO_FETCH_FUNC:
1247 if (!fetch_all) {
1248 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_FUNC is only allowed in PDOStatement::fetchAll()" TSRMLS_CC);
1249 return 0;
1250 }
1251 return 1;
1252
1253 case PDO_FETCH_LAZY:
1254 if (fetch_all) {
1255 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_LAZY can't be used with PDOStatement::fetchAll()" TSRMLS_CC);
1256 return 0;
1257 }
1258 /* fall through */
1259
1260 default:
1261 if ((flags & PDO_FETCH_SERIALIZE) == PDO_FETCH_SERIALIZE) {
1262 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_SERIALIZE can only be used together with PDO::FETCH_CLASS" TSRMLS_CC);
1263 return 0;
1264 }
1265 if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
1266 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_CLASSTYPE can only be used together with PDO::FETCH_CLASS" TSRMLS_CC);
1267 return 0;
1268 }
1269 if (mode >= PDO_FETCH__MAX) {
1270 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "invalid fetch mode" TSRMLS_CC);
1271 return 0;
1272 }
1273 /* no break; */
1274
1275 case PDO_FETCH_CLASS:
1276 return 1;
1277 }
1278 }
1279 /* }}} */
1280
1281 /* {{{ proto mixed PDOStatement::fetch([int $how = PDO_FETCH_BOTH [, int $orientation [, int $offset]]])
1282 Fetches the next row and returns it, or false if there are no more rows */
PHP_METHOD(PDOStatement,fetch)1283 static PHP_METHOD(PDOStatement, fetch)
1284 {
1285 long how = PDO_FETCH_USE_DEFAULT;
1286 long ori = PDO_FETCH_ORI_NEXT;
1287 long off = 0;
1288 PHP_STMT_GET_OBJ;
1289
1290 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lll", &how,
1291 &ori, &off)) {
1292 RETURN_FALSE;
1293 }
1294
1295 PDO_STMT_CLEAR_ERR();
1296
1297 if (!pdo_stmt_verify_mode(stmt, how, 0 TSRMLS_CC)) {
1298 RETURN_FALSE;
1299 }
1300
1301 if (!do_fetch(stmt, TRUE, return_value, how, ori, off, 0 TSRMLS_CC)) {
1302 PDO_HANDLE_STMT_ERR();
1303 RETURN_FALSE;
1304 }
1305 }
1306 /* }}} */
1307
1308 /* {{{ proto mixed PDOStatement::fetchObject([string class_name [, NULL|array ctor_args]])
1309 Fetches the next row and returns it as an object. */
PHP_METHOD(PDOStatement,fetchObject)1310 static PHP_METHOD(PDOStatement, fetchObject)
1311 {
1312 long how = PDO_FETCH_CLASS;
1313 long ori = PDO_FETCH_ORI_NEXT;
1314 long off = 0;
1315 char *class_name = NULL;
1316 int class_name_len;
1317 zend_class_entry *old_ce;
1318 zval *old_ctor_args, *ctor_args = NULL;
1319 int error = 0, old_arg_count;
1320
1321 PHP_STMT_GET_OBJ;
1322
1323 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!a", &class_name, &class_name_len, &ctor_args)) {
1324 RETURN_FALSE;
1325 }
1326
1327 PDO_STMT_CLEAR_ERR();
1328
1329 if (!pdo_stmt_verify_mode(stmt, how, 0 TSRMLS_CC)) {
1330 RETURN_FALSE;
1331 }
1332
1333 old_ce = stmt->fetch.cls.ce;
1334 old_ctor_args = stmt->fetch.cls.ctor_args;
1335 old_arg_count = stmt->fetch.cls.fci.param_count;
1336
1337 do_fetch_opt_finish(stmt, 0 TSRMLS_CC);
1338
1339 if (ctor_args) {
1340 if (Z_TYPE_P(ctor_args) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(ctor_args))) {
1341 ALLOC_ZVAL(stmt->fetch.cls.ctor_args);
1342 *stmt->fetch.cls.ctor_args = *ctor_args;
1343 zval_copy_ctor(stmt->fetch.cls.ctor_args);
1344 } else {
1345 stmt->fetch.cls.ctor_args = NULL;
1346 }
1347 }
1348 if (class_name && !error) {
1349 stmt->fetch.cls.ce = zend_fetch_class(class_name, class_name_len, ZEND_FETCH_CLASS_AUTO TSRMLS_CC);
1350
1351 if (!stmt->fetch.cls.ce) {
1352 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Could not find user-supplied class" TSRMLS_CC);
1353 error = 1;
1354 }
1355 } else if (!error) {
1356 stmt->fetch.cls.ce = zend_standard_class_def;
1357 }
1358
1359 if (!error && !do_fetch(stmt, TRUE, return_value, how, ori, off, 0 TSRMLS_CC)) {
1360 error = 1;
1361 }
1362 if (error) {
1363 PDO_HANDLE_STMT_ERR();
1364 }
1365 do_fetch_opt_finish(stmt, 1 TSRMLS_CC);
1366
1367 stmt->fetch.cls.ce = old_ce;
1368 stmt->fetch.cls.ctor_args = old_ctor_args;
1369 stmt->fetch.cls.fci.param_count = old_arg_count;
1370 if (error) {
1371 RETURN_FALSE;
1372 }
1373 }
1374 /* }}} */
1375
1376 /* {{{ proto string PDOStatement::fetchColumn([int column_number])
1377 Returns a data of the specified column in the result set. */
PHP_METHOD(PDOStatement,fetchColumn)1378 static PHP_METHOD(PDOStatement, fetchColumn)
1379 {
1380 long col_n = 0;
1381 PHP_STMT_GET_OBJ;
1382
1383 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &col_n)) {
1384 RETURN_FALSE;
1385 }
1386
1387 PDO_STMT_CLEAR_ERR();
1388
1389 if (!do_fetch_common(stmt, PDO_FETCH_ORI_NEXT, 0, TRUE TSRMLS_CC)) {
1390 PDO_HANDLE_STMT_ERR();
1391 RETURN_FALSE;
1392 }
1393
1394 fetch_value(stmt, return_value, col_n, NULL TSRMLS_CC);
1395 }
1396 /* }}} */
1397
1398 /* {{{ proto array PDOStatement::fetchAll([int $how = PDO_FETCH_BOTH [, string class_name [, NULL|array ctor_args]]])
1399 Returns an array of all of the results. */
PHP_METHOD(PDOStatement,fetchAll)1400 static PHP_METHOD(PDOStatement, fetchAll)
1401 {
1402 long how = PDO_FETCH_USE_DEFAULT;
1403 zval *data, *return_all;
1404 zval *arg2;
1405 zend_class_entry *old_ce;
1406 zval *old_ctor_args, *ctor_args = NULL;
1407 int error = 0, flags, old_arg_count;
1408 PHP_STMT_GET_OBJ;
1409
1410 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lzz", &how, &arg2, &ctor_args)) {
1411 RETURN_FALSE;
1412 }
1413
1414 if (!pdo_stmt_verify_mode(stmt, how, 1 TSRMLS_CC)) {
1415 RETURN_FALSE;
1416 }
1417
1418 old_ce = stmt->fetch.cls.ce;
1419 old_ctor_args = stmt->fetch.cls.ctor_args;
1420 old_arg_count = stmt->fetch.cls.fci.param_count;
1421
1422 do_fetch_opt_finish(stmt, 0 TSRMLS_CC);
1423
1424 switch(how & ~PDO_FETCH_FLAGS) {
1425 case PDO_FETCH_CLASS:
1426 switch(ZEND_NUM_ARGS()) {
1427 case 0:
1428 case 1:
1429 stmt->fetch.cls.ce = zend_standard_class_def;
1430 break;
1431 case 3:
1432 if (Z_TYPE_P(ctor_args) != IS_NULL && Z_TYPE_P(ctor_args) != IS_ARRAY) {
1433 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "ctor_args must be either NULL or an array" TSRMLS_CC);
1434 error = 1;
1435 break;
1436 }
1437 if (Z_TYPE_P(ctor_args) != IS_ARRAY || !zend_hash_num_elements(Z_ARRVAL_P(ctor_args))) {
1438 ctor_args = NULL;
1439 }
1440 /* no break */
1441 case 2:
1442 stmt->fetch.cls.ctor_args = ctor_args; /* we're not going to free these */
1443 if (Z_TYPE_P(arg2) != IS_STRING) {
1444 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Invalid class name (should be a string)" TSRMLS_CC);
1445 error = 1;
1446 break;
1447 } else {
1448 stmt->fetch.cls.ce = zend_fetch_class(Z_STRVAL_P(arg2), Z_STRLEN_P(arg2), ZEND_FETCH_CLASS_AUTO TSRMLS_CC);
1449 if (!stmt->fetch.cls.ce) {
1450 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not find user-specified class" TSRMLS_CC);
1451 error = 1;
1452 break;
1453 }
1454 }
1455 }
1456 if (!error) {
1457 do_fetch_class_prepare(stmt TSRMLS_CC);
1458 }
1459 break;
1460
1461 case PDO_FETCH_FUNC:
1462 switch(ZEND_NUM_ARGS()) {
1463 case 0:
1464 case 1:
1465 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "no fetch function specified" TSRMLS_CC);
1466 error = 1;
1467 break;
1468 case 3:
1469 case 2:
1470 stmt->fetch.func.function = arg2;
1471 if (do_fetch_func_prepare(stmt TSRMLS_CC) == 0) {
1472 error = 1;
1473 }
1474 break;
1475 }
1476 break;
1477
1478 case PDO_FETCH_COLUMN:
1479 switch(ZEND_NUM_ARGS()) {
1480 case 0:
1481 case 1:
1482 stmt->fetch.column = how & PDO_FETCH_GROUP ? -1 : 0;
1483 break;
1484 case 2:
1485 convert_to_long(arg2);
1486 stmt->fetch.column = Z_LVAL_P(arg2);
1487 break;
1488 case 3:
1489 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Third parameter not allowed for PDO::FETCH_COLUMN" TSRMLS_CC);
1490 error = 1;
1491 }
1492 break;
1493
1494 default:
1495 if (ZEND_NUM_ARGS() > 1) {
1496 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Extraneous additional parameters" TSRMLS_CC);
1497 error = 1;
1498 }
1499 }
1500
1501 flags = how & PDO_FETCH_FLAGS;
1502
1503 if ((how & ~PDO_FETCH_FLAGS) == PDO_FETCH_USE_DEFAULT) {
1504 flags |= stmt->default_fetch_type & PDO_FETCH_FLAGS;
1505 how |= stmt->default_fetch_type & ~PDO_FETCH_FLAGS;
1506 }
1507
1508 if (!error) {
1509 PDO_STMT_CLEAR_ERR();
1510 MAKE_STD_ZVAL(data);
1511 if ( (how & PDO_FETCH_GROUP) || how == PDO_FETCH_KEY_PAIR ||
1512 (how == PDO_FETCH_USE_DEFAULT && stmt->default_fetch_type == PDO_FETCH_KEY_PAIR)
1513 ) {
1514 array_init(return_value);
1515 return_all = return_value;
1516 } else {
1517 return_all = 0;
1518 }
1519 if (!do_fetch(stmt, TRUE, data, how | flags, PDO_FETCH_ORI_NEXT, 0, return_all TSRMLS_CC)) {
1520 FREE_ZVAL(data);
1521 error = 2;
1522 }
1523 }
1524 if (!error) {
1525 if ((how & PDO_FETCH_GROUP)) {
1526 do {
1527 MAKE_STD_ZVAL(data);
1528 } while (do_fetch(stmt, TRUE, data, how | flags, PDO_FETCH_ORI_NEXT, 0, return_all TSRMLS_CC));
1529 } else if (how == PDO_FETCH_KEY_PAIR || (how == PDO_FETCH_USE_DEFAULT && stmt->default_fetch_type == PDO_FETCH_KEY_PAIR)) {
1530 while (do_fetch(stmt, TRUE, data, how | flags, PDO_FETCH_ORI_NEXT, 0, return_all TSRMLS_CC));
1531 } else {
1532 array_init(return_value);
1533 do {
1534 add_next_index_zval(return_value, data);
1535 MAKE_STD_ZVAL(data);
1536 } while (do_fetch(stmt, TRUE, data, how | flags, PDO_FETCH_ORI_NEXT, 0, 0 TSRMLS_CC));
1537 }
1538 FREE_ZVAL(data);
1539 }
1540
1541 do_fetch_opt_finish(stmt, 0 TSRMLS_CC);
1542
1543 stmt->fetch.cls.ce = old_ce;
1544 stmt->fetch.cls.ctor_args = old_ctor_args;
1545 stmt->fetch.cls.fci.param_count = old_arg_count;
1546
1547 if (error) {
1548 PDO_HANDLE_STMT_ERR();
1549 if (error != 2) {
1550 RETURN_FALSE;
1551 } else { /* on no results, return an empty array */
1552 if (Z_TYPE_P(return_value) != IS_ARRAY) {
1553 array_init(return_value);
1554 }
1555 return;
1556 }
1557 }
1558 }
1559 /* }}} */
1560
register_bound_param(INTERNAL_FUNCTION_PARAMETERS,pdo_stmt_t * stmt,int is_param)1561 static int register_bound_param(INTERNAL_FUNCTION_PARAMETERS, pdo_stmt_t *stmt, int is_param) /* {{{ */
1562 {
1563 struct pdo_bound_param_data param = {0};
1564 long param_type = PDO_PARAM_STR;
1565
1566 param.paramno = -1;
1567
1568 if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC,
1569 "lz|llz!", ¶m.paramno, ¶m.parameter, ¶m_type, ¶m.max_value_len,
1570 ¶m.driver_params)) {
1571 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|llz!", ¶m.name,
1572 ¶m.namelen, ¶m.parameter, ¶m_type, ¶m.max_value_len,
1573 ¶m.driver_params)) {
1574 return 0;
1575 }
1576 }
1577
1578 param.param_type = (int) param_type;
1579
1580 if (param.paramno > 0) {
1581 --param.paramno; /* make it zero-based internally */
1582 } else if (!param.name) {
1583 pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "Columns/Parameters are 1-based" TSRMLS_CC);
1584 return 0;
1585 }
1586
1587 Z_ADDREF_P(param.parameter);
1588 if (!really_register_bound_param(¶m, stmt, is_param TSRMLS_CC)) {
1589 if (param.parameter) {
1590 zval_ptr_dtor(&(param.parameter));
1591 param.parameter = NULL;
1592 }
1593 return 0;
1594 }
1595 return 1;
1596 } /* }}} */
1597
1598 /* {{{ proto bool PDOStatement::bindValue(mixed $paramno, mixed $param [, int $type ])
1599 bind an input parameter to the value of a PHP variable. $paramno is the 1-based position of the placeholder in the SQL statement (but can be the parameter name for drivers that support named placeholders). It should be called prior to execute(). */
PHP_METHOD(PDOStatement,bindValue)1600 static PHP_METHOD(PDOStatement, bindValue)
1601 {
1602 struct pdo_bound_param_data param = {0};
1603 long param_type = PDO_PARAM_STR;
1604 PHP_STMT_GET_OBJ;
1605
1606 param.paramno = -1;
1607
1608 if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC,
1609 "lz/|l", ¶m.paramno, ¶m.parameter, ¶m_type)) {
1610 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz/|l", ¶m.name,
1611 ¶m.namelen, ¶m.parameter, ¶m_type)) {
1612 RETURN_FALSE;
1613 }
1614 }
1615
1616 param.param_type = (int) param_type;
1617
1618 if (param.paramno > 0) {
1619 --param.paramno; /* make it zero-based internally */
1620 } else if (!param.name) {
1621 pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "Columns/Parameters are 1-based" TSRMLS_CC);
1622 RETURN_FALSE;
1623 }
1624
1625 Z_ADDREF_P(param.parameter);
1626 if (!really_register_bound_param(¶m, stmt, TRUE TSRMLS_CC)) {
1627 if (param.parameter) {
1628 zval_ptr_dtor(&(param.parameter));
1629 param.parameter = NULL;
1630 }
1631 RETURN_FALSE;
1632 }
1633 RETURN_TRUE;
1634 }
1635 /* }}} */
1636
1637
1638 /* {{{ proto bool PDOStatement::bindParam(mixed $paramno, mixed &$param [, int $type [, int $maxlen [, mixed $driverdata]]])
1639 bind a parameter to a PHP variable. $paramno is the 1-based position of the placeholder in the SQL statement (but can be the parameter name for drivers that support named placeholders). This isn't supported by all drivers. It should be called prior to execute(). */
PHP_METHOD(PDOStatement,bindParam)1640 static PHP_METHOD(PDOStatement, bindParam)
1641 {
1642 PHP_STMT_GET_OBJ;
1643 RETURN_BOOL(register_bound_param(INTERNAL_FUNCTION_PARAM_PASSTHRU, stmt, TRUE));
1644 }
1645 /* }}} */
1646
1647 /* {{{ proto bool PDOStatement::bindColumn(mixed $column, mixed &$param [, int $type [, int $maxlen [, mixed $driverdata]]])
1648 bind a column to a PHP variable. On each row fetch $param will contain the value of the corresponding column. $column is the 1-based offset of the column, or the column name. For portability, don't call this before execute(). */
PHP_METHOD(PDOStatement,bindColumn)1649 static PHP_METHOD(PDOStatement, bindColumn)
1650 {
1651 PHP_STMT_GET_OBJ;
1652 RETURN_BOOL(register_bound_param(INTERNAL_FUNCTION_PARAM_PASSTHRU, stmt, FALSE));
1653 }
1654 /* }}} */
1655
1656 /* {{{ proto int PDOStatement::rowCount()
1657 Returns the number of rows in a result set, or the number of rows affected by the last execute(). It is not always meaningful. */
PHP_METHOD(PDOStatement,rowCount)1658 static PHP_METHOD(PDOStatement, rowCount)
1659 {
1660 PHP_STMT_GET_OBJ;
1661
1662 RETURN_LONG(stmt->row_count);
1663 }
1664 /* }}} */
1665
1666 /* {{{ proto string PDOStatement::errorCode()
1667 Fetch the error code associated with the last operation on the statement handle */
PHP_METHOD(PDOStatement,errorCode)1668 static PHP_METHOD(PDOStatement, errorCode)
1669 {
1670 PHP_STMT_GET_OBJ;
1671
1672 if (zend_parse_parameters_none() == FAILURE) {
1673 return;
1674 }
1675
1676 if (stmt->error_code[0] == '\0') {
1677 RETURN_NULL();
1678 }
1679
1680 RETURN_STRING(stmt->error_code, 1);
1681 }
1682 /* }}} */
1683
1684 /* {{{ proto array PDOStatement::errorInfo()
1685 Fetch extended error information associated with the last operation on the statement handle */
PHP_METHOD(PDOStatement,errorInfo)1686 static PHP_METHOD(PDOStatement, errorInfo)
1687 {
1688 int error_count;
1689 int error_count_diff = 0;
1690 int error_expected_count = 3;
1691
1692 PHP_STMT_GET_OBJ;
1693
1694 if (zend_parse_parameters_none() == FAILURE) {
1695 return;
1696 }
1697
1698 array_init(return_value);
1699 add_next_index_string(return_value, stmt->error_code, 1);
1700
1701 if (stmt->dbh->methods->fetch_err) {
1702 stmt->dbh->methods->fetch_err(stmt->dbh, stmt, return_value TSRMLS_CC);
1703 }
1704
1705 error_count = zend_hash_num_elements(Z_ARRVAL_P(return_value));
1706
1707 if (error_expected_count > error_count) {
1708 int current_index;
1709
1710 error_count_diff = error_expected_count - error_count;
1711 for (current_index = 0; current_index < error_count_diff; current_index++) {
1712 add_next_index_null(return_value);
1713 }
1714 }
1715 }
1716 /* }}} */
1717
1718 /* {{{ proto bool PDOStatement::setAttribute(long attribute, mixed value)
1719 Set an attribute */
PHP_METHOD(PDOStatement,setAttribute)1720 static PHP_METHOD(PDOStatement, setAttribute)
1721 {
1722 long attr;
1723 zval *value = NULL;
1724 PHP_STMT_GET_OBJ;
1725
1726 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lz!", &attr, &value)) {
1727 RETURN_FALSE;
1728 }
1729
1730 if (!stmt->methods->set_attribute) {
1731 goto fail;
1732 }
1733
1734 PDO_STMT_CLEAR_ERR();
1735 if (stmt->methods->set_attribute(stmt, attr, value TSRMLS_CC)) {
1736 RETURN_TRUE;
1737 }
1738
1739 fail:
1740 if (!stmt->methods->set_attribute) {
1741 pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "This driver doesn't support setting attributes" TSRMLS_CC);
1742 } else {
1743 PDO_HANDLE_STMT_ERR();
1744 }
1745 RETURN_FALSE;
1746 }
1747 /* }}} */
1748
1749 /* {{{ proto mixed PDOStatement::getAttribute(long attribute)
1750 Get an attribute */
1751
generic_stmt_attr_get(pdo_stmt_t * stmt,zval * return_value,long attr)1752 static int generic_stmt_attr_get(pdo_stmt_t *stmt, zval *return_value, long attr)
1753 {
1754 switch (attr) {
1755 case PDO_ATTR_EMULATE_PREPARES:
1756 RETVAL_BOOL(stmt->supports_placeholders == PDO_PLACEHOLDER_NONE);
1757 return 1;
1758 }
1759 return 0;
1760 }
1761
PHP_METHOD(PDOStatement,getAttribute)1762 static PHP_METHOD(PDOStatement, getAttribute)
1763 {
1764 long attr;
1765 PHP_STMT_GET_OBJ;
1766
1767 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &attr)) {
1768 RETURN_FALSE;
1769 }
1770
1771 if (!stmt->methods->get_attribute) {
1772 if (!generic_stmt_attr_get(stmt, return_value, attr)) {
1773 pdo_raise_impl_error(stmt->dbh, stmt, "IM001",
1774 "This driver doesn't support getting attributes" TSRMLS_CC);
1775 RETURN_FALSE;
1776 }
1777 return;
1778 }
1779
1780 PDO_STMT_CLEAR_ERR();
1781 switch (stmt->methods->get_attribute(stmt, attr, return_value TSRMLS_CC)) {
1782 case -1:
1783 PDO_HANDLE_STMT_ERR();
1784 RETURN_FALSE;
1785
1786 case 0:
1787 if (!generic_stmt_attr_get(stmt, return_value, attr)) {
1788 /* XXX: should do something better here */
1789 pdo_raise_impl_error(stmt->dbh, stmt, "IM001",
1790 "driver doesn't support getting that attribute" TSRMLS_CC);
1791 RETURN_FALSE;
1792 }
1793 return;
1794
1795 default:
1796 return;
1797 }
1798 }
1799 /* }}} */
1800
1801 /* {{{ proto int PDOStatement::columnCount()
1802 Returns the number of columns in the result set */
PHP_METHOD(PDOStatement,columnCount)1803 static PHP_METHOD(PDOStatement, columnCount)
1804 {
1805 PHP_STMT_GET_OBJ;
1806 if (zend_parse_parameters_none() == FAILURE) {
1807 return;
1808 }
1809 RETURN_LONG(stmt->column_count);
1810 }
1811 /* }}} */
1812
1813 /* {{{ proto array PDOStatement::getColumnMeta(int $column)
1814 Returns meta data for a numbered column */
PHP_METHOD(PDOStatement,getColumnMeta)1815 static PHP_METHOD(PDOStatement, getColumnMeta)
1816 {
1817 long colno;
1818 struct pdo_column_data *col;
1819 PHP_STMT_GET_OBJ;
1820
1821 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &colno)) {
1822 RETURN_FALSE;
1823 }
1824 if(colno < 0) {
1825 pdo_raise_impl_error(stmt->dbh, stmt, "42P10", "column number must be non-negative" TSRMLS_CC);
1826 RETURN_FALSE;
1827 }
1828
1829 if (!stmt->methods->get_column_meta) {
1830 pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "driver doesn't support meta data" TSRMLS_CC);
1831 RETURN_FALSE;
1832 }
1833
1834 PDO_STMT_CLEAR_ERR();
1835 if (FAILURE == stmt->methods->get_column_meta(stmt, colno, return_value TSRMLS_CC)) {
1836 PDO_HANDLE_STMT_ERR();
1837 RETURN_FALSE;
1838 }
1839
1840 /* add stock items */
1841 col = &stmt->columns[colno];
1842 add_assoc_string(return_value, "name", col->name, 1);
1843 add_assoc_long(return_value, "len", col->maxlen); /* FIXME: unsigned ? */
1844 add_assoc_long(return_value, "precision", col->precision);
1845 if (col->param_type != PDO_PARAM_ZVAL) {
1846 /* if param_type is PDO_PARAM_ZVAL the driver has to provide correct data */
1847 add_assoc_long(return_value, "pdo_type", col->param_type);
1848 }
1849 }
1850 /* }}} */
1851
1852 /* {{{ proto bool PDOStatement::setFetchMode(int mode [mixed* params])
1853 Changes the default fetch mode for subsequent fetches (params have different meaning for different fetch modes) */
1854
pdo_stmt_setup_fetch_mode(INTERNAL_FUNCTION_PARAMETERS,pdo_stmt_t * stmt,int skip)1855 int pdo_stmt_setup_fetch_mode(INTERNAL_FUNCTION_PARAMETERS, pdo_stmt_t *stmt, int skip)
1856 {
1857 long mode = PDO_FETCH_BOTH;
1858 int flags = 0, argc = ZEND_NUM_ARGS() - skip;
1859 zval ***args;
1860 zend_class_entry **cep;
1861 int retval;
1862
1863 do_fetch_opt_finish(stmt, 1 TSRMLS_CC);
1864
1865 switch (stmt->default_fetch_type) {
1866 case PDO_FETCH_INTO:
1867 if (stmt->fetch.into) {
1868 zval_ptr_dtor(&stmt->fetch.into);
1869 stmt->fetch.into = NULL;
1870 }
1871 break;
1872 default:
1873 ;
1874 }
1875
1876 stmt->default_fetch_type = PDO_FETCH_BOTH;
1877
1878 if (argc == 0) {
1879 return SUCCESS;
1880 }
1881
1882 args = safe_emalloc(ZEND_NUM_ARGS(), sizeof(zval*), 0);
1883
1884 retval = zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args);
1885
1886 if (SUCCESS == retval) {
1887 if (Z_TYPE_PP(args[skip]) != IS_LONG) {
1888 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "mode must be an integer" TSRMLS_CC);
1889 retval = FAILURE;
1890 } else {
1891 mode = Z_LVAL_PP(args[skip]);
1892 flags = mode & PDO_FETCH_FLAGS;
1893
1894 retval = pdo_stmt_verify_mode(stmt, mode, 0 TSRMLS_CC);
1895 }
1896 }
1897
1898 if (FAILURE == retval) {
1899 PDO_STMT_CLEAR_ERR();
1900 efree(args);
1901 return FAILURE;
1902 }
1903
1904 retval = FAILURE;
1905 switch (mode & ~PDO_FETCH_FLAGS) {
1906 case PDO_FETCH_USE_DEFAULT:
1907 case PDO_FETCH_LAZY:
1908 case PDO_FETCH_ASSOC:
1909 case PDO_FETCH_NUM:
1910 case PDO_FETCH_BOTH:
1911 case PDO_FETCH_OBJ:
1912 case PDO_FETCH_BOUND:
1913 case PDO_FETCH_NAMED:
1914 case PDO_FETCH_KEY_PAIR:
1915 if (argc != 1) {
1916 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode doesn't allow any extra arguments" TSRMLS_CC);
1917 } else {
1918 retval = SUCCESS;
1919 }
1920 break;
1921
1922 case PDO_FETCH_COLUMN:
1923 if (argc != 2) {
1924 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode requires the colno argument" TSRMLS_CC);
1925 } else if (Z_TYPE_PP(args[skip+1]) != IS_LONG) {
1926 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "colno must be an integer" TSRMLS_CC);
1927 } else {
1928 stmt->fetch.column = Z_LVAL_PP(args[skip+1]);
1929 retval = SUCCESS;
1930 }
1931 break;
1932
1933 case PDO_FETCH_CLASS:
1934 /* Gets its class name from 1st column */
1935 if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
1936 if (argc != 1) {
1937 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode doesn't allow any extra arguments" TSRMLS_CC);
1938 } else {
1939 stmt->fetch.cls.ce = NULL;
1940 retval = SUCCESS;
1941 }
1942 } else {
1943 if (argc < 2) {
1944 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode requires the classname argument" TSRMLS_CC);
1945 } else if (argc > 3) {
1946 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "too many arguments" TSRMLS_CC);
1947 } else if (Z_TYPE_PP(args[skip+1]) != IS_STRING) {
1948 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "classname must be a string" TSRMLS_CC);
1949 } else {
1950 retval = zend_lookup_class(Z_STRVAL_PP(args[skip+1]),
1951 Z_STRLEN_PP(args[skip+1]), &cep TSRMLS_CC);
1952
1953 if (SUCCESS == retval && cep && *cep) {
1954 stmt->fetch.cls.ce = *cep;
1955 }
1956 }
1957 }
1958
1959 if (SUCCESS == retval) {
1960 stmt->fetch.cls.ctor_args = NULL;
1961 #ifdef ilia_0 /* we'll only need this when we have persistent statements, if ever */
1962 if (stmt->dbh->is_persistent) {
1963 php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP might crash if you don't call $stmt->setFetchMode() to reset to defaults on this persistent statement. This will be fixed in a later release");
1964 }
1965 #endif
1966 if (argc == 3) {
1967 if (Z_TYPE_PP(args[skip+2]) != IS_NULL && Z_TYPE_PP(args[skip+2]) != IS_ARRAY) {
1968 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "ctor_args must be either NULL or an array" TSRMLS_CC);
1969 retval = FAILURE;
1970 } else if (Z_TYPE_PP(args[skip+2]) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_PP(args[skip+2]))) {
1971 ALLOC_ZVAL(stmt->fetch.cls.ctor_args);
1972 *stmt->fetch.cls.ctor_args = **args[skip+2];
1973 zval_copy_ctor(stmt->fetch.cls.ctor_args);
1974 }
1975 }
1976
1977 if (SUCCESS == retval) {
1978 do_fetch_class_prepare(stmt TSRMLS_CC);
1979 }
1980 }
1981
1982 break;
1983
1984 case PDO_FETCH_INTO:
1985 if (argc != 2) {
1986 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode requires the object parameter" TSRMLS_CC);
1987 } else if (Z_TYPE_PP(args[skip+1]) != IS_OBJECT) {
1988 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "object must be an object" TSRMLS_CC);
1989 } else {
1990 retval = SUCCESS;
1991 }
1992
1993 if (SUCCESS == retval) {
1994 #ifdef ilia_0 /* we'll only need this when we have persistent statements, if ever */
1995 if (stmt->dbh->is_persistent) {
1996 php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP might crash if you don't call $stmt->setFetchMode() to reset to defaults on this persistent statement. This will be fixed in a later release");
1997 }
1998 #endif
1999 MAKE_STD_ZVAL(stmt->fetch.into);
2000
2001 Z_TYPE_P(stmt->fetch.into) = IS_OBJECT;
2002 Z_OBJ_HANDLE_P(stmt->fetch.into) = Z_OBJ_HANDLE_PP(args[skip+1]);
2003 Z_OBJ_HT_P(stmt->fetch.into) = Z_OBJ_HT_PP(args[skip+1]);
2004 zend_objects_store_add_ref(stmt->fetch.into TSRMLS_CC);
2005 }
2006
2007 break;
2008
2009 default:
2010 pdo_raise_impl_error(stmt->dbh, stmt, "22003", "Invalid fetch mode specified" TSRMLS_CC);
2011 }
2012
2013 if (SUCCESS == retval) {
2014 stmt->default_fetch_type = mode;
2015 }
2016
2017 /*
2018 * PDO error (if any) has already been raised at this point.
2019 *
2020 * The error_code is cleared, otherwise the caller will read the
2021 * last error message from the driver.
2022 *
2023 */
2024 PDO_STMT_CLEAR_ERR();
2025
2026 efree(args);
2027
2028 return retval;
2029 }
2030
PHP_METHOD(PDOStatement,setFetchMode)2031 static PHP_METHOD(PDOStatement, setFetchMode)
2032 {
2033 PHP_STMT_GET_OBJ;
2034
2035 RETVAL_BOOL(
2036 pdo_stmt_setup_fetch_mode(INTERNAL_FUNCTION_PARAM_PASSTHRU,
2037 stmt, 0) == SUCCESS ? 1 : 0
2038 );
2039 }
2040 /* }}} */
2041
2042 /* {{{ proto bool PDOStatement::nextRowset()
2043 Advances to the next rowset in a multi-rowset statement handle. Returns true if it succeeded, false otherwise */
2044
pdo_stmt_do_next_rowset(pdo_stmt_t * stmt TSRMLS_DC)2045 static int pdo_stmt_do_next_rowset(pdo_stmt_t *stmt TSRMLS_DC)
2046 {
2047 /* un-describe */
2048 if (stmt->columns) {
2049 int i;
2050 struct pdo_column_data *cols = stmt->columns;
2051
2052 for (i = 0; i < stmt->column_count; i++) {
2053 efree(cols[i].name);
2054 }
2055 efree(stmt->columns);
2056 stmt->columns = NULL;
2057 stmt->column_count = 0;
2058 }
2059
2060 if (!stmt->methods->next_rowset(stmt TSRMLS_CC)) {
2061 /* Set the executed flag to 0 to reallocate columns on next execute */
2062 stmt->executed = 0;
2063 return 0;
2064 }
2065
2066 pdo_stmt_describe_columns(stmt TSRMLS_CC);
2067
2068 return 1;
2069 }
2070
PHP_METHOD(PDOStatement,nextRowset)2071 static PHP_METHOD(PDOStatement, nextRowset)
2072 {
2073 PHP_STMT_GET_OBJ;
2074
2075 if (!stmt->methods->next_rowset) {
2076 pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "driver does not support multiple rowsets" TSRMLS_CC);
2077 RETURN_FALSE;
2078 }
2079
2080 PDO_STMT_CLEAR_ERR();
2081
2082 if (!pdo_stmt_do_next_rowset(stmt TSRMLS_CC)) {
2083 PDO_HANDLE_STMT_ERR();
2084 RETURN_FALSE;
2085 }
2086
2087 RETURN_TRUE;
2088 }
2089 /* }}} */
2090
2091 /* {{{ proto bool PDOStatement::closeCursor()
2092 Closes the cursor, leaving the statement ready for re-execution. */
PHP_METHOD(PDOStatement,closeCursor)2093 static PHP_METHOD(PDOStatement, closeCursor)
2094 {
2095 PHP_STMT_GET_OBJ;
2096
2097 if (!stmt->methods->cursor_closer) {
2098 /* emulate it by fetching and discarding rows */
2099 do {
2100 while (stmt->methods->fetcher(stmt, PDO_FETCH_ORI_NEXT, 0 TSRMLS_CC))
2101 ;
2102 if (!stmt->methods->next_rowset) {
2103 break;
2104 }
2105
2106 if (!pdo_stmt_do_next_rowset(stmt TSRMLS_CC)) {
2107 break;
2108 }
2109
2110 } while (1);
2111 stmt->executed = 0;
2112 RETURN_TRUE;
2113 }
2114
2115 PDO_STMT_CLEAR_ERR();
2116
2117 if (!stmt->methods->cursor_closer(stmt TSRMLS_CC)) {
2118 PDO_HANDLE_STMT_ERR();
2119 RETURN_FALSE;
2120 }
2121 stmt->executed = 0;
2122 RETURN_TRUE;
2123 }
2124 /* }}} */
2125
2126 /* {{{ proto void PDOStatement::debugDumpParams()
2127 A utility for internals hackers to debug parameter internals */
PHP_METHOD(PDOStatement,debugDumpParams)2128 static PHP_METHOD(PDOStatement, debugDumpParams)
2129 {
2130 php_stream *out = php_stream_open_wrapper("php://output", "w", 0, NULL);
2131 HashPosition pos;
2132 struct pdo_bound_param_data *param;
2133 PHP_STMT_GET_OBJ;
2134
2135 if (out == NULL) {
2136 RETURN_FALSE;
2137 }
2138
2139 php_stream_printf(out TSRMLS_CC, "SQL: [%d] %.*s\n",
2140 stmt->query_stringlen,
2141 stmt->query_stringlen, stmt->query_string);
2142
2143 php_stream_printf(out TSRMLS_CC, "Params: %d\n",
2144 stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0);
2145
2146 if (stmt->bound_params) {
2147 zend_hash_internal_pointer_reset_ex(stmt->bound_params, &pos);
2148 while (SUCCESS == zend_hash_get_current_data_ex(stmt->bound_params,
2149 (void**)¶m, &pos)) {
2150 char *str;
2151 uint len;
2152 ulong num;
2153 int res;
2154
2155 res = zend_hash_get_current_key_ex(stmt->bound_params, &str, &len, &num, 0, &pos);
2156 if (res == HASH_KEY_IS_LONG) {
2157 php_stream_printf(out TSRMLS_CC, "Key: Position #%ld:\n", num);
2158 } else if (res == HASH_KEY_IS_STRING) {
2159 php_stream_printf(out TSRMLS_CC, "Key: Name: [%d] %.*s\n", len, len, str);
2160 }
2161
2162 php_stream_printf(out TSRMLS_CC, "paramno=%ld\nname=[%d] \"%.*s\"\nis_param=%d\nparam_type=%d\n",
2163 param->paramno, param->namelen, param->namelen, param->name ? param->name : "",
2164 param->is_param,
2165 param->param_type);
2166
2167 zend_hash_move_forward_ex(stmt->bound_params, &pos);
2168 }
2169 }
2170
2171 php_stream_close(out);
2172 }
2173 /* }}} */
2174
2175 /* {{{ proto int PDOStatement::__wakeup()
2176 Prevents use of a PDOStatement instance that has been unserialized */
PHP_METHOD(PDOStatement,__wakeup)2177 static PHP_METHOD(PDOStatement, __wakeup)
2178 {
2179 zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "You cannot serialize or unserialize PDOStatement instances");
2180 }
2181 /* }}} */
2182
2183 /* {{{ proto int PDOStatement::__sleep()
2184 Prevents serialization of a PDOStatement instance */
PHP_METHOD(PDOStatement,__sleep)2185 static PHP_METHOD(PDOStatement, __sleep)
2186 {
2187 zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "You cannot serialize or unserialize PDOStatement instances");
2188 }
2189 /* }}} */
2190
2191 const zend_function_entry pdo_dbstmt_functions[] = {
2192 PHP_ME(PDOStatement, execute, arginfo_pdostatement_execute, ZEND_ACC_PUBLIC)
2193 PHP_ME(PDOStatement, fetch, arginfo_pdostatement_fetch, ZEND_ACC_PUBLIC)
2194 PHP_ME(PDOStatement, bindParam, arginfo_pdostatement_bindparam, ZEND_ACC_PUBLIC)
2195 PHP_ME(PDOStatement, bindColumn, arginfo_pdostatement_bindcolumn, ZEND_ACC_PUBLIC)
2196 PHP_ME(PDOStatement, bindValue, arginfo_pdostatement_bindvalue, ZEND_ACC_PUBLIC)
2197 PHP_ME(PDOStatement, rowCount, arginfo_pdostatement__void, ZEND_ACC_PUBLIC)
2198 PHP_ME(PDOStatement, fetchColumn, arginfo_pdostatement_fetchcolumn, ZEND_ACC_PUBLIC)
2199 PHP_ME(PDOStatement, fetchAll, arginfo_pdostatement_fetchall, ZEND_ACC_PUBLIC)
2200 PHP_ME(PDOStatement, fetchObject, arginfo_pdostatement_fetchobject, ZEND_ACC_PUBLIC)
2201 PHP_ME(PDOStatement, errorCode, arginfo_pdostatement__void, ZEND_ACC_PUBLIC)
2202 PHP_ME(PDOStatement, errorInfo, arginfo_pdostatement__void, ZEND_ACC_PUBLIC)
2203 PHP_ME(PDOStatement, setAttribute, arginfo_pdostatement_setattribute, ZEND_ACC_PUBLIC)
2204 PHP_ME(PDOStatement, getAttribute, arginfo_pdostatement_getattribute, ZEND_ACC_PUBLIC)
2205 PHP_ME(PDOStatement, columnCount, arginfo_pdostatement__void, ZEND_ACC_PUBLIC)
2206 PHP_ME(PDOStatement, getColumnMeta, arginfo_pdostatement_getcolumnmeta, ZEND_ACC_PUBLIC)
2207 PHP_ME(PDOStatement, setFetchMode, arginfo_pdostatement_setfetchmode, ZEND_ACC_PUBLIC)
2208 PHP_ME(PDOStatement, nextRowset, arginfo_pdostatement__void, ZEND_ACC_PUBLIC)
2209 PHP_ME(PDOStatement, closeCursor, arginfo_pdostatement__void, ZEND_ACC_PUBLIC)
2210 PHP_ME(PDOStatement, debugDumpParams, arginfo_pdostatement__void, ZEND_ACC_PUBLIC)
2211 PHP_ME(PDOStatement, __wakeup, arginfo_pdostatement__void, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
2212 PHP_ME(PDOStatement, __sleep, arginfo_pdostatement__void, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
2213 {NULL, NULL, NULL}
2214 };
2215
2216 /* {{{ overloaded handlers for PDOStatement class */
dbstmt_prop_write(zval * object,zval * member,zval * value,const zend_literal * key TSRMLS_DC)2217 static void dbstmt_prop_write(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC)
2218 {
2219 pdo_stmt_t * stmt = (pdo_stmt_t *) zend_object_store_get_object(object TSRMLS_CC);
2220
2221 convert_to_string(member);
2222
2223 if(strcmp(Z_STRVAL_P(member), "queryString") == 0) {
2224 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "property queryString is read only" TSRMLS_CC);
2225 } else {
2226 std_object_handlers.write_property(object, member, value, key TSRMLS_CC);
2227 }
2228 }
2229
dbstmt_prop_delete(zval * object,zval * member,const zend_literal * key TSRMLS_DC)2230 static void dbstmt_prop_delete(zval *object, zval *member, const zend_literal *key TSRMLS_DC)
2231 {
2232 pdo_stmt_t * stmt = (pdo_stmt_t *) zend_object_store_get_object(object TSRMLS_CC);
2233
2234 convert_to_string(member);
2235
2236 if(strcmp(Z_STRVAL_P(member), "queryString") == 0) {
2237 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "property queryString is read only" TSRMLS_CC);
2238 } else {
2239 std_object_handlers.unset_property(object, member, key TSRMLS_CC);
2240 }
2241 }
2242
dbstmt_method_get(zval ** object_pp,char * method_name,int method_len,const zend_literal * key TSRMLS_DC)2243 static union _zend_function *dbstmt_method_get(zval **object_pp, char *method_name, int method_len, const zend_literal *key TSRMLS_DC)
2244 {
2245 zend_function *fbc = NULL;
2246 char *lc_method_name;
2247 zval *object = *object_pp;
2248
2249 lc_method_name = emalloc(method_len + 1);
2250 zend_str_tolower_copy(lc_method_name, method_name, method_len);
2251
2252 if (zend_hash_find(&Z_OBJCE_P(object)->function_table, lc_method_name,
2253 method_len+1, (void**)&fbc) == FAILURE) {
2254
2255 pdo_stmt_t *stmt = (pdo_stmt_t*)zend_object_store_get_object(object TSRMLS_CC);
2256 /* instance not created by PDO object */
2257 if (!stmt->dbh) {
2258 goto out;
2259 }
2260 /* not a pre-defined method, nor a user-defined method; check
2261 * the driver specific methods */
2262 if (!stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT]) {
2263 if (!pdo_hash_methods(stmt->dbh,
2264 PDO_DBH_DRIVER_METHOD_KIND_STMT TSRMLS_CC)
2265 || !stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT]) {
2266 goto out;
2267 }
2268 }
2269
2270 if (zend_hash_find(stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT],
2271 lc_method_name, method_len+1, (void**)&fbc) == FAILURE) {
2272 goto out;
2273 }
2274 /* got it */
2275 }
2276
2277 out:
2278 if (!fbc) {
2279 fbc = std_object_handlers.get_method(object_pp, method_name, method_len, key TSRMLS_CC);
2280 }
2281 efree(lc_method_name);
2282 return fbc;
2283 }
2284
dbstmt_compare(zval * object1,zval * object2 TSRMLS_DC)2285 static int dbstmt_compare(zval *object1, zval *object2 TSRMLS_DC)
2286 {
2287 return -1;
2288 }
2289
dbstmt_clone_obj(zval * zobject TSRMLS_DC)2290 static zend_object_value dbstmt_clone_obj(zval *zobject TSRMLS_DC)
2291 {
2292 zend_object_value retval;
2293 pdo_stmt_t *stmt;
2294 pdo_stmt_t *old_stmt;
2295 zend_object_handle handle = Z_OBJ_HANDLE_P(zobject);
2296
2297 stmt = ecalloc(1, sizeof(*stmt));
2298 zend_object_std_init(&stmt->std, Z_OBJCE_P(zobject) TSRMLS_CC);
2299 object_properties_init(&stmt->std, Z_OBJCE_P(zobject));
2300 stmt->refcount = 1;
2301
2302 old_stmt = (pdo_stmt_t *)zend_object_store_get_object(zobject TSRMLS_CC);
2303
2304 retval.handle = zend_objects_store_put(stmt, (zend_objects_store_dtor_t)zend_objects_destroy_object, (zend_objects_free_object_storage_t)pdo_dbstmt_free_storage, (zend_objects_store_clone_t)dbstmt_clone_obj TSRMLS_CC);
2305 retval.handlers = Z_OBJ_HT_P(zobject);
2306
2307 zend_objects_clone_members((zend_object *)stmt, retval, (zend_object *)old_stmt, handle TSRMLS_CC);
2308
2309 zend_objects_store_add_ref(&old_stmt->database_object_handle TSRMLS_CC);
2310 stmt->database_object_handle = old_stmt->database_object_handle;
2311
2312 return retval;
2313 }
2314
2315 zend_object_handlers pdo_dbstmt_object_handlers;
2316 static int pdo_row_serialize(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC);
2317
pdo_stmt_init(TSRMLS_D)2318 void pdo_stmt_init(TSRMLS_D)
2319 {
2320 zend_class_entry ce;
2321
2322 INIT_CLASS_ENTRY(ce, "PDOStatement", pdo_dbstmt_functions);
2323 pdo_dbstmt_ce = zend_register_internal_class(&ce TSRMLS_CC);
2324 pdo_dbstmt_ce->get_iterator = pdo_stmt_iter_get;
2325 pdo_dbstmt_ce->create_object = pdo_dbstmt_new;
2326 zend_class_implements(pdo_dbstmt_ce TSRMLS_CC, 1, zend_ce_traversable);
2327 zend_declare_property_null(pdo_dbstmt_ce, "queryString", sizeof("queryString")-1, ZEND_ACC_PUBLIC TSRMLS_CC);
2328
2329 memcpy(&pdo_dbstmt_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
2330 pdo_dbstmt_object_handlers.write_property = dbstmt_prop_write;
2331 pdo_dbstmt_object_handlers.unset_property = dbstmt_prop_delete;
2332 pdo_dbstmt_object_handlers.get_method = dbstmt_method_get;
2333 pdo_dbstmt_object_handlers.compare_objects = dbstmt_compare;
2334 pdo_dbstmt_object_handlers.clone_obj = dbstmt_clone_obj;
2335
2336 INIT_CLASS_ENTRY(ce, "PDORow", pdo_row_functions);
2337 pdo_row_ce = zend_register_internal_class(&ce TSRMLS_CC);
2338 pdo_row_ce->ce_flags |= ZEND_ACC_FINAL_CLASS; /* when removing this a lot of handlers need to be redone */
2339 pdo_row_ce->create_object = pdo_row_new;
2340 pdo_row_ce->serialize = pdo_row_serialize;
2341 pdo_row_ce->unserialize = zend_class_unserialize_deny;
2342 }
2343
free_statement(pdo_stmt_t * stmt TSRMLS_DC)2344 static void free_statement(pdo_stmt_t *stmt TSRMLS_DC)
2345 {
2346 if (stmt->bound_params) {
2347 zend_hash_destroy(stmt->bound_params);
2348 FREE_HASHTABLE(stmt->bound_params);
2349 stmt->bound_params = NULL;
2350 }
2351 if (stmt->bound_param_map) {
2352 zend_hash_destroy(stmt->bound_param_map);
2353 FREE_HASHTABLE(stmt->bound_param_map);
2354 stmt->bound_param_map = NULL;
2355 }
2356 if (stmt->bound_columns) {
2357 zend_hash_destroy(stmt->bound_columns);
2358 FREE_HASHTABLE(stmt->bound_columns);
2359 stmt->bound_columns = NULL;
2360 }
2361
2362 if (stmt->methods && stmt->methods->dtor) {
2363 stmt->methods->dtor(stmt TSRMLS_CC);
2364 }
2365 if (stmt->query_string) {
2366 efree(stmt->query_string);
2367 }
2368
2369 if (stmt->columns) {
2370 int i;
2371 struct pdo_column_data *cols = stmt->columns;
2372
2373 for (i = 0; i < stmt->column_count; i++) {
2374 if (cols[i].name) {
2375 efree(cols[i].name);
2376 cols[i].name = NULL;
2377 }
2378 }
2379 efree(stmt->columns);
2380 stmt->columns = NULL;
2381 }
2382
2383 if (stmt->fetch.into && stmt->default_fetch_type == PDO_FETCH_INTO) {
2384 FREE_ZVAL(stmt->fetch.into);
2385 stmt->fetch.into = NULL;
2386 }
2387
2388 do_fetch_opt_finish(stmt, 1 TSRMLS_CC);
2389
2390 zend_objects_store_del_ref(&stmt->database_object_handle TSRMLS_CC);
2391 if (stmt->dbh) {
2392 php_pdo_dbh_delref(stmt->dbh TSRMLS_CC);
2393 }
2394 zend_object_std_dtor(&stmt->std TSRMLS_CC);
2395 efree(stmt);
2396 }
2397
php_pdo_stmt_addref(pdo_stmt_t * stmt TSRMLS_DC)2398 PDO_API void php_pdo_stmt_addref(pdo_stmt_t *stmt TSRMLS_DC)
2399 {
2400 stmt->refcount++;
2401 }
2402
php_pdo_stmt_delref(pdo_stmt_t * stmt TSRMLS_DC)2403 PDO_API void php_pdo_stmt_delref(pdo_stmt_t *stmt TSRMLS_DC)
2404 {
2405 if (--stmt->refcount == 0) {
2406 free_statement(stmt TSRMLS_CC);
2407 }
2408 }
2409
pdo_dbstmt_free_storage(pdo_stmt_t * stmt TSRMLS_DC)2410 void pdo_dbstmt_free_storage(pdo_stmt_t *stmt TSRMLS_DC)
2411 {
2412 php_pdo_stmt_delref(stmt TSRMLS_CC);
2413 }
2414
pdo_dbstmt_new(zend_class_entry * ce TSRMLS_DC)2415 zend_object_value pdo_dbstmt_new(zend_class_entry *ce TSRMLS_DC)
2416 {
2417 zend_object_value retval;
2418
2419 pdo_stmt_t *stmt;
2420 stmt = emalloc(sizeof(*stmt));
2421 memset(stmt, 0, sizeof(*stmt));
2422 zend_object_std_init(&stmt->std, ce TSRMLS_CC);
2423 object_properties_init(&stmt->std, ce);
2424 stmt->refcount = 1;
2425
2426 retval.handle = zend_objects_store_put(stmt, (zend_objects_store_dtor_t)zend_objects_destroy_object, (zend_objects_free_object_storage_t)pdo_dbstmt_free_storage, (zend_objects_store_clone_t)dbstmt_clone_obj TSRMLS_CC);
2427 retval.handlers = &pdo_dbstmt_object_handlers;
2428
2429 return retval;
2430 }
2431 /* }}} */
2432
2433 /* {{{ statement iterator */
2434
2435 struct php_pdo_iterator {
2436 zend_object_iterator iter;
2437 pdo_stmt_t *stmt;
2438 ulong key;
2439 zval *fetch_ahead;
2440 };
2441
pdo_stmt_iter_dtor(zend_object_iterator * iter TSRMLS_DC)2442 static void pdo_stmt_iter_dtor(zend_object_iterator *iter TSRMLS_DC)
2443 {
2444 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter->data;
2445
2446 if (--I->stmt->refcount == 0) {
2447 free_statement(I->stmt TSRMLS_CC);
2448 }
2449
2450 if (I->fetch_ahead) {
2451 zval_ptr_dtor(&I->fetch_ahead);
2452 }
2453
2454 efree(I);
2455 }
2456
pdo_stmt_iter_valid(zend_object_iterator * iter TSRMLS_DC)2457 static int pdo_stmt_iter_valid(zend_object_iterator *iter TSRMLS_DC)
2458 {
2459 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter->data;
2460
2461 return I->fetch_ahead ? SUCCESS : FAILURE;
2462 }
2463
pdo_stmt_iter_get_data(zend_object_iterator * iter,zval *** data TSRMLS_DC)2464 static void pdo_stmt_iter_get_data(zend_object_iterator *iter, zval ***data TSRMLS_DC)
2465 {
2466 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter->data;
2467
2468 /* sanity */
2469 if (!I->fetch_ahead) {
2470 *data = NULL;
2471 return;
2472 }
2473
2474 *data = &I->fetch_ahead;
2475 }
2476
pdo_stmt_iter_get_key(zend_object_iterator * iter,zval * key TSRMLS_DC)2477 static void pdo_stmt_iter_get_key(zend_object_iterator *iter, zval *key TSRMLS_DC)
2478 {
2479 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter->data;
2480
2481 if (I->key == (ulong)-1) {
2482 ZVAL_NULL(key);
2483 } else {
2484 ZVAL_LONG(key, I->key);
2485 }
2486 }
2487
pdo_stmt_iter_move_forwards(zend_object_iterator * iter TSRMLS_DC)2488 static void pdo_stmt_iter_move_forwards(zend_object_iterator *iter TSRMLS_DC)
2489 {
2490 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter->data;
2491
2492 if (I->fetch_ahead) {
2493 zval_ptr_dtor(&I->fetch_ahead);
2494 I->fetch_ahead = NULL;
2495 }
2496
2497 MAKE_STD_ZVAL(I->fetch_ahead);
2498
2499 if (!do_fetch(I->stmt, TRUE, I->fetch_ahead, PDO_FETCH_USE_DEFAULT,
2500 PDO_FETCH_ORI_NEXT, 0, 0 TSRMLS_CC)) {
2501 pdo_stmt_t *stmt = I->stmt; /* for PDO_HANDLE_STMT_ERR() */
2502
2503 PDO_HANDLE_STMT_ERR();
2504 I->key = (ulong)-1;
2505 FREE_ZVAL(I->fetch_ahead);
2506 I->fetch_ahead = NULL;
2507
2508 return;
2509 }
2510
2511 I->key++;
2512 }
2513
2514 static zend_object_iterator_funcs pdo_stmt_iter_funcs = {
2515 pdo_stmt_iter_dtor,
2516 pdo_stmt_iter_valid,
2517 pdo_stmt_iter_get_data,
2518 pdo_stmt_iter_get_key,
2519 pdo_stmt_iter_move_forwards,
2520 NULL
2521 };
2522
pdo_stmt_iter_get(zend_class_entry * ce,zval * object,int by_ref TSRMLS_DC)2523 zend_object_iterator *pdo_stmt_iter_get(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC)
2524 {
2525 pdo_stmt_t *stmt = (pdo_stmt_t*)zend_object_store_get_object(object TSRMLS_CC);
2526 struct php_pdo_iterator *I;
2527
2528 if (by_ref) {
2529 zend_error(E_ERROR, "An iterator cannot be used with foreach by reference");
2530 }
2531
2532 I = ecalloc(1, sizeof(*I));
2533 I->iter.funcs = &pdo_stmt_iter_funcs;
2534 I->iter.data = I;
2535 I->stmt = stmt;
2536 stmt->refcount++;
2537
2538 MAKE_STD_ZVAL(I->fetch_ahead);
2539 if (!do_fetch(I->stmt, TRUE, I->fetch_ahead, PDO_FETCH_USE_DEFAULT,
2540 PDO_FETCH_ORI_NEXT, 0, 0 TSRMLS_CC)) {
2541 PDO_HANDLE_STMT_ERR();
2542 I->key = (ulong)-1;
2543 FREE_ZVAL(I->fetch_ahead);
2544 I->fetch_ahead = NULL;
2545 }
2546
2547 return &I->iter;
2548 }
2549
2550 /* }}} */
2551
2552 /* {{{ overloaded handlers for PDORow class (used by PDO_FETCH_LAZY) */
2553
2554 const zend_function_entry pdo_row_functions[] = {
2555 {NULL, NULL, NULL}
2556 };
2557
row_prop_read(zval * object,zval * member,int type,const zend_literal * key TSRMLS_DC)2558 static zval *row_prop_read(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC)
2559 {
2560 zval *return_value;
2561 pdo_stmt_t * stmt = (pdo_stmt_t *) zend_object_store_get_object(object TSRMLS_CC);
2562 int colno = -1;
2563
2564 MAKE_STD_ZVAL(return_value);
2565 RETVAL_NULL();
2566
2567 if (stmt) {
2568 if (Z_TYPE_P(member) == IS_LONG) {
2569 if (Z_LVAL_P(member) >= 0 && Z_LVAL_P(member) < stmt->column_count) {
2570 fetch_value(stmt, return_value, Z_LVAL_P(member), NULL TSRMLS_CC);
2571 }
2572 } else {
2573 convert_to_string(member);
2574 /* TODO: replace this with a hash of available column names to column
2575 * numbers */
2576 for (colno = 0; colno < stmt->column_count; colno++) {
2577 if (strcmp(stmt->columns[colno].name, Z_STRVAL_P(member)) == 0) {
2578 fetch_value(stmt, return_value, colno, NULL TSRMLS_CC);
2579 Z_SET_REFCOUNT_P(return_value, 0);
2580 Z_UNSET_ISREF_P(return_value);
2581 return return_value;
2582 }
2583 }
2584 if (strcmp(Z_STRVAL_P(member), "queryString") == 0) {
2585 zval_ptr_dtor(&return_value);
2586 return std_object_handlers.read_property(object, member, type, key TSRMLS_CC);
2587 }
2588 }
2589 }
2590
2591 Z_SET_REFCOUNT_P(return_value, 0);
2592 Z_UNSET_ISREF_P(return_value);
2593
2594 return return_value;
2595 }
2596
row_dim_read(zval * object,zval * member,int type TSRMLS_DC)2597 static zval *row_dim_read(zval *object, zval *member, int type TSRMLS_DC)
2598 {
2599 return row_prop_read(object, member, type, NULL TSRMLS_CC);
2600 }
2601
row_prop_write(zval * object,zval * member,zval * value,const zend_literal * key TSRMLS_DC)2602 static void row_prop_write(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC)
2603 {
2604 php_error_docref(NULL TSRMLS_CC, E_WARNING, "This PDORow is not from a writable result set");
2605 }
2606
row_dim_write(zval * object,zval * member,zval * value TSRMLS_DC)2607 static void row_dim_write(zval *object, zval *member, zval *value TSRMLS_DC)
2608 {
2609 php_error_docref(NULL TSRMLS_CC, E_WARNING, "This PDORow is not from a writable result set");
2610 }
2611
row_prop_exists(zval * object,zval * member,int check_empty,const zend_literal * key TSRMLS_DC)2612 static int row_prop_exists(zval *object, zval *member, int check_empty, const zend_literal *key TSRMLS_DC)
2613 {
2614 pdo_stmt_t * stmt = (pdo_stmt_t *) zend_object_store_get_object(object TSRMLS_CC);
2615 int colno = -1;
2616
2617 if (stmt) {
2618 if (Z_TYPE_P(member) == IS_LONG) {
2619 return Z_LVAL_P(member) >= 0 && Z_LVAL_P(member) < stmt->column_count;
2620 } else {
2621 convert_to_string(member);
2622
2623 /* TODO: replace this with a hash of available column names to column
2624 * numbers */
2625 for (colno = 0; colno < stmt->column_count; colno++) {
2626 if (strcmp(stmt->columns[colno].name, Z_STRVAL_P(member)) == 0) {
2627 int res;
2628 zval *val;
2629
2630 MAKE_STD_ZVAL(val);
2631 fetch_value(stmt, val, colno, NULL TSRMLS_CC);
2632 res = check_empty ? i_zend_is_true(val) : Z_TYPE_P(val) != IS_NULL;
2633 zval_ptr_dtor(&val);
2634
2635 return res;
2636 }
2637 }
2638 }
2639 }
2640
2641 return 0;
2642 }
2643
row_dim_exists(zval * object,zval * member,int check_empty TSRMLS_DC)2644 static int row_dim_exists(zval *object, zval *member, int check_empty TSRMLS_DC)
2645 {
2646 return row_prop_exists(object, member, check_empty, NULL TSRMLS_CC);
2647 }
2648
row_prop_delete(zval * object,zval * offset,const zend_literal * key TSRMLS_DC)2649 static void row_prop_delete(zval *object, zval *offset, const zend_literal *key TSRMLS_DC)
2650 {
2651 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot delete properties from a PDORow");
2652 }
2653
row_dim_delete(zval * object,zval * offset TSRMLS_DC)2654 static void row_dim_delete(zval *object, zval *offset TSRMLS_DC)
2655 {
2656 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot delete properties from a PDORow");
2657 }
2658
row_get_properties(zval * object TSRMLS_DC)2659 static HashTable *row_get_properties(zval *object TSRMLS_DC)
2660 {
2661 pdo_stmt_t * stmt = (pdo_stmt_t *) zend_object_store_get_object(object TSRMLS_CC);
2662 int i;
2663
2664 if (stmt == NULL) {
2665 return NULL;
2666 }
2667
2668 if (!stmt->std.properties) {
2669 rebuild_object_properties(&stmt->std);
2670 }
2671 for (i = 0; i < stmt->column_count; i++) {
2672 zval *val;
2673 MAKE_STD_ZVAL(val);
2674 fetch_value(stmt, val, i, NULL TSRMLS_CC);
2675
2676 zend_hash_update(stmt->std.properties, stmt->columns[i].name, stmt->columns[i].namelen + 1, (void *)&val, sizeof(zval *), NULL);
2677 }
2678
2679 return stmt->std.properties;
2680 }
2681
row_method_get(zval ** object_pp,char * method_name,int method_len,const zend_literal * key TSRMLS_DC)2682 static union _zend_function *row_method_get(
2683 zval **object_pp,
2684 char *method_name, int method_len, const zend_literal *key TSRMLS_DC)
2685 {
2686 zend_function *fbc;
2687 char *lc_method_name;
2688
2689 lc_method_name = emalloc(method_len + 1);
2690 zend_str_tolower_copy(lc_method_name, method_name, method_len);
2691
2692 if (zend_hash_find(&pdo_row_ce->function_table, lc_method_name, method_len+1, (void**)&fbc) == FAILURE) {
2693 efree(lc_method_name);
2694 return NULL;
2695 }
2696
2697 efree(lc_method_name);
2698 return fbc;
2699 }
2700
row_call_method(const char * method,INTERNAL_FUNCTION_PARAMETERS)2701 static int row_call_method(const char *method, INTERNAL_FUNCTION_PARAMETERS)
2702 {
2703 return FAILURE;
2704 }
2705
row_get_ctor(zval * object TSRMLS_DC)2706 static union _zend_function *row_get_ctor(zval *object TSRMLS_DC)
2707 {
2708 static zend_internal_function ctor = {0};
2709
2710 ctor.type = ZEND_INTERNAL_FUNCTION;
2711 ctor.function_name = "__construct";
2712 ctor.scope = pdo_row_ce;
2713 ctor.handler = ZEND_FN(dbstmt_constructor);
2714 ctor.fn_flags = ZEND_ACC_PUBLIC;
2715
2716 return (union _zend_function*)&ctor;
2717 }
2718
row_get_ce(const zval * object TSRMLS_DC)2719 static zend_class_entry *row_get_ce(const zval *object TSRMLS_DC)
2720 {
2721 return pdo_row_ce;
2722 }
2723
row_get_classname(const zval * object,const char ** class_name,zend_uint * class_name_len,int parent TSRMLS_DC)2724 static int row_get_classname(const zval *object, const char **class_name, zend_uint *class_name_len, int parent TSRMLS_DC)
2725 {
2726 if (parent) {
2727 return FAILURE;
2728 } else {
2729 *class_name = estrndup("PDORow", sizeof("PDORow")-1);
2730 *class_name_len = sizeof("PDORow")-1;
2731 return SUCCESS;
2732 }
2733 }
2734
row_compare(zval * object1,zval * object2 TSRMLS_DC)2735 static int row_compare(zval *object1, zval *object2 TSRMLS_DC)
2736 {
2737 return -1;
2738 }
2739
2740 zend_object_handlers pdo_row_object_handlers = {
2741 zend_objects_store_add_ref,
2742 zend_objects_store_del_ref,
2743 NULL,
2744 row_prop_read,
2745 row_prop_write,
2746 row_dim_read,
2747 row_dim_write,
2748 NULL,
2749 NULL,
2750 NULL,
2751 row_prop_exists,
2752 row_prop_delete,
2753 row_dim_exists,
2754 row_dim_delete,
2755 row_get_properties,
2756 row_method_get,
2757 row_call_method,
2758 row_get_ctor,
2759 row_get_ce,
2760 row_get_classname,
2761 row_compare,
2762 NULL, /* cast */
2763 NULL
2764 };
2765
pdo_row_free_storage(pdo_stmt_t * stmt TSRMLS_DC)2766 void pdo_row_free_storage(pdo_stmt_t *stmt TSRMLS_DC)
2767 {
2768 if (stmt) {
2769 ZVAL_NULL(&stmt->lazy_object_ref);
2770
2771 if (--stmt->refcount == 0) {
2772 free_statement(stmt TSRMLS_CC);
2773 }
2774 }
2775 }
2776
pdo_row_new(zend_class_entry * ce TSRMLS_DC)2777 zend_object_value pdo_row_new(zend_class_entry *ce TSRMLS_DC)
2778 {
2779 zend_object_value retval;
2780
2781 retval.handle = zend_objects_store_put(NULL, (zend_objects_store_dtor_t)zend_objects_destroy_object, (zend_objects_free_object_storage_t)pdo_row_free_storage, NULL TSRMLS_CC);
2782 retval.handlers = &pdo_row_object_handlers;
2783
2784 return retval;
2785 }
2786
pdo_row_serialize(zval * object,unsigned char ** buffer,zend_uint * buf_len,zend_serialize_data * data TSRMLS_DC)2787 static int pdo_row_serialize(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC)
2788 {
2789 php_error_docref(NULL TSRMLS_CC, E_WARNING, "PDORow instances may not be serialized");
2790 return FAILURE;
2791 }
2792 /* }}} */
2793
2794 /*
2795 * Local variables:
2796 * tab-width: 4
2797 * c-basic-offset: 4
2798 * End:
2799 * vim600: noet sw=4 ts=4 fdm=marker
2800 * vim<600: noet sw=4 ts=4
2801 */
2802