Skip to content

Commit a5cf828

Browse files
committed
Make various failure conditions in PDO unconditional errors
This includes TypeErrors, ValueErrors, Error for uninitialized objects and invalid user classes/callable instanciation Closes GH-6212
1 parent 24e2ba2 commit a5cf828

18 files changed

+674
-542
lines changed

ext/pdo/pdo_dbh.c

Lines changed: 76 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -425,12 +425,10 @@ PHP_METHOD(PDO, __construct)
425425
static zval *pdo_stmt_instantiate(pdo_dbh_t *dbh, zval *object, zend_class_entry *dbstmt_ce, zval *ctor_args) /* {{{ */
426426
{
427427
if (!Z_ISUNDEF_P(ctor_args)) {
428-
if (Z_TYPE_P(ctor_args) != IS_ARRAY) {
429-
pdo_raise_impl_error(dbh, NULL, "HY000", "constructor arguments must be passed as an array");
430-
return NULL;
431-
}
428+
/* This implies an error within PDO if this does not hold */
429+
ZEND_ASSERT(Z_TYPE_P(ctor_args) == IS_ARRAY);
432430
if (!dbstmt_ce->constructor) {
433-
pdo_raise_impl_error(dbh, NULL, "HY000", "user-supplied statement does not accept constructor arguments");
431+
zend_throw_error(NULL, "User-supplied statement does not accept constructor arguments");
434432
return NULL;
435433
}
436434
}
@@ -487,7 +485,7 @@ PHP_METHOD(PDO, prepare)
487485
pdo_stmt_t *stmt;
488486
char *statement;
489487
size_t statement_len;
490-
zval *options = NULL, *opt, *item, ctor_args;
488+
zval *options = NULL, *value, *item, ctor_args;
491489
zend_class_entry *dbstmt_ce, *pce;
492490
pdo_dbh_object_t *dbh_obj = Z_PDO_OBJECT_P(ZEND_THIS);
493491
pdo_dbh_t *dbh = dbh_obj->inner;
@@ -498,42 +496,44 @@ PHP_METHOD(PDO, prepare)
498496
Z_PARAM_ARRAY(options)
499497
ZEND_PARSE_PARAMETERS_END();
500498

501-
PDO_DBH_CLEAR_ERR();
502499
PDO_CONSTRUCT_CHECK;
503500

504-
if (ZEND_NUM_ARGS() > 1 && (opt = zend_hash_index_find(Z_ARRVAL_P(options), PDO_ATTR_STATEMENT_CLASS)) != NULL) {
505-
if (Z_TYPE_P(opt) != IS_ARRAY || (item = zend_hash_index_find(Z_ARRVAL_P(opt), 0)) == NULL
506-
|| Z_TYPE_P(item) != IS_STRING
507-
|| (pce = zend_lookup_class(Z_STR_P(item))) == NULL
508-
) {
509-
pdo_raise_impl_error(dbh, NULL, "HY000",
510-
"PDO::ATTR_STATEMENT_CLASS requires format array(classname, array(ctor_args)); "
511-
"the classname must be a string specifying an existing class"
512-
);
513-
PDO_HANDLE_DBH_ERR();
514-
RETURN_FALSE;
501+
if (statement_len == 0) {
502+
zend_argument_value_error(1, "cannot be empty");
503+
RETURN_THROWS();
504+
}
505+
506+
PDO_DBH_CLEAR_ERR();
507+
508+
if (options && (value = zend_hash_index_find(Z_ARRVAL_P(options), PDO_ATTR_STATEMENT_CLASS)) != NULL) {
509+
if (Z_TYPE_P(value) != IS_ARRAY) {
510+
zend_type_error("PDO::ATTR_STATEMENT_CLASS value must be of type array, %s given",
511+
zend_zval_type_name(value));
512+
RETURN_THROWS();
513+
}
514+
if ((item = zend_hash_index_find(Z_ARRVAL_P(value), 0)) == NULL) {
515+
zend_value_error("PDO::ATTR_STATEMENT_CLASS value must be an array with the format "
516+
"array(classname, array(ctor_args))");
517+
RETURN_THROWS();
518+
}
519+
if (Z_TYPE_P(item) != IS_STRING || (pce = zend_lookup_class(Z_STR_P(item))) == NULL) {
520+
zend_type_error("PDO::ATTR_STATEMENT_CLASS class must be a valid class");
521+
RETURN_THROWS();
515522
}
516523
dbstmt_ce = pce;
517524
if (!instanceof_function(dbstmt_ce, pdo_dbstmt_ce)) {
518-
pdo_raise_impl_error(dbh, NULL, "HY000",
519-
"user-supplied statement class must be derived from PDOStatement");
520-
PDO_HANDLE_DBH_ERR();
521-
RETURN_FALSE;
525+
zend_type_error("PDO::ATTR_STATEMENT_CLASS class must be derived from PDOStatement");
526+
RETURN_THROWS();
522527
}
523528
if (dbstmt_ce->constructor && !(dbstmt_ce->constructor->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED))) {
524-
pdo_raise_impl_error(dbh, NULL, "HY000",
525-
"user-supplied statement class cannot have a public constructor");
526-
PDO_HANDLE_DBH_ERR();
527-
RETURN_FALSE;
529+
zend_type_error("User-supplied statement class cannot have a public constructor");
530+
RETURN_THROWS();
528531
}
529-
if ((item = zend_hash_index_find(Z_ARRVAL_P(opt), 1)) != NULL) {
532+
if ((item = zend_hash_index_find(Z_ARRVAL_P(value), 1)) != NULL) {
530533
if (Z_TYPE_P(item) != IS_ARRAY) {
531-
pdo_raise_impl_error(dbh, NULL, "HY000",
532-
"PDO::ATTR_STATEMENT_CLASS requires format array(classname, ctor_args); "
533-
"ctor_args must be an array"
534-
);
535-
PDO_HANDLE_DBH_ERR();
536-
RETURN_FALSE;
534+
zend_type_error("PDO::ATTR_STATEMENT_CLASS ctor_args must be of type ?array, %s given",
535+
zend_zval_type_name(value));
536+
RETURN_THROWS();
537537
}
538538
ZVAL_COPY_VALUE(&ctor_args, item);
539539
} else {
@@ -544,11 +544,10 @@ PHP_METHOD(PDO, prepare)
544544
ZVAL_COPY_VALUE(&ctor_args, &dbh->def_stmt_ctor_args);
545545
}
546546

547+
/* Need to check if pdo_stmt_instantiate() throws an exception unconditionally to see if can change the RETURN_FALSE; */
547548
if (!pdo_stmt_instantiate(dbh, return_value, dbstmt_ce, &ctor_args)) {
548549
if (EXPECTED(!EG(exception))) {
549-
pdo_raise_impl_error(dbh, NULL, "HY000",
550-
"failed to instantiate user-supplied statement class"
551-
);
550+
zend_throw_error(NULL, "Cannot instantiate user-supplied statement class");
552551
}
553552
PDO_HANDLE_DBH_ERR();
554553
RETURN_FALSE;
@@ -679,10 +678,10 @@ static int pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value) /*
679678
{
680679
zend_long lval;
681680

681+
/* TODO: Make distinction between numeric and non-numeric strings */
682682
#define PDO_LONG_PARAM_CHECK \
683683
if (Z_TYPE_P(value) != IS_LONG && Z_TYPE_P(value) != IS_STRING && Z_TYPE_P(value) != IS_FALSE && Z_TYPE_P(value) != IS_TRUE) { \
684-
pdo_raise_impl_error(dbh, NULL, "HY000", "attribute value must be an integer"); \
685-
PDO_HANDLE_DBH_ERR(); \
684+
zend_type_error("Attribute value must be of type int for selected attribute, %s given", zend_zval_type_name(value)); \
686685
return FAILURE; \
687686
} \
688687

@@ -697,8 +696,7 @@ static int pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value) /*
697696
dbh->error_mode = lval;
698697
return SUCCESS;
699698
default:
700-
pdo_raise_impl_error(dbh, NULL, "HY000", "invalid error mode");
701-
PDO_HANDLE_DBH_ERR();
699+
zend_value_error("Error mode must be one of the PDO::ERRMODE_* constants");
702700
return FAILURE;
703701
}
704702
return FAILURE;
@@ -713,8 +711,7 @@ static int pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value) /*
713711
dbh->desired_case = lval;
714712
return SUCCESS;
715713
default:
716-
pdo_raise_impl_error(dbh, NULL, "HY000", "invalid case folding mode");
717-
PDO_HANDLE_DBH_ERR();
714+
zend_value_error("Case folding mode must be one of the PDO::CASE_* constants");
718715
return FAILURE;
719716
}
720717
return FAILURE;
@@ -729,7 +726,7 @@ static int pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value) /*
729726
zval *tmp;
730727
if ((tmp = zend_hash_index_find(Z_ARRVAL_P(value), 0)) != NULL && Z_TYPE_P(tmp) == IS_LONG) {
731728
if (Z_LVAL_P(tmp) == PDO_FETCH_INTO || Z_LVAL_P(tmp) == PDO_FETCH_CLASS) {
732-
pdo_raise_impl_error(dbh, NULL, "HY000", "FETCH_INTO and FETCH_CLASS are not yet supported as default fetch modes");
729+
zend_value_error("PDO::FETCH_INTO and PDO::FETCH_CLASS cannot be set as the default fetch mode");
733730
return FAILURE;
734731
}
735732
}
@@ -738,7 +735,7 @@ static int pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value) /*
738735
}
739736
lval = zval_get_long(value);
740737
if (lval == PDO_FETCH_USE_DEFAULT) {
741-
pdo_raise_impl_error(dbh, NULL, "HY000", "invalid fetch mode type");
738+
zend_value_error("Fetch mode must be a bitmask of PDO::FETCH_* constants");
742739
return FAILURE;
743740
}
744741
dbh->default_fetch_type = lval;
@@ -761,28 +758,26 @@ static int pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value) /*
761758
PDO_HANDLE_DBH_ERR();
762759
return FAILURE;
763760
}
764-
if (Z_TYPE_P(value) != IS_ARRAY
765-
|| (item = zend_hash_index_find(Z_ARRVAL_P(value), 0)) == NULL
766-
|| Z_TYPE_P(item) != IS_STRING
767-
|| (pce = zend_lookup_class(Z_STR_P(item))) == NULL
768-
) {
769-
pdo_raise_impl_error(dbh, NULL, "HY000",
770-
"PDO::ATTR_STATEMENT_CLASS requires format array(classname, array(ctor_args)); "
771-
"the classname must be a string specifying an existing class"
772-
);
773-
PDO_HANDLE_DBH_ERR();
761+
if (Z_TYPE_P(value) != IS_ARRAY) {
762+
zend_type_error("PDO::ATTR_STATEMENT_CLASS value must be of type array, %s given",
763+
zend_zval_type_name(value));
764+
return FAILURE;
765+
}
766+
if ((item = zend_hash_index_find(Z_ARRVAL_P(value), 0)) == NULL) {
767+
zend_value_error("PDO::ATTR_STATEMENT_CLASS value must be an array with the format "
768+
"array(classname, array(ctor_args))");
769+
return FAILURE;
770+
}
771+
if (Z_TYPE_P(item) != IS_STRING || (pce = zend_lookup_class(Z_STR_P(item))) == NULL) {
772+
zend_type_error("PDO::ATTR_STATEMENT_CLASS class must be a valid class");
774773
return FAILURE;
775774
}
776775
if (!instanceof_function(pce, pdo_dbstmt_ce)) {
777-
pdo_raise_impl_error(dbh, NULL, "HY000",
778-
"user-supplied statement class must be derived from PDOStatement");
779-
PDO_HANDLE_DBH_ERR();
776+
zend_type_error("PDO::ATTR_STATEMENT_CLASS class must be derived from PDOStatement");
780777
return FAILURE;
781778
}
782779
if (pce->constructor && !(pce->constructor->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED))) {
783-
pdo_raise_impl_error(dbh, NULL, "HY000",
784-
"user-supplied statement class cannot have a public constructor");
785-
PDO_HANDLE_DBH_ERR();
780+
zend_type_error("User-supplied statement class cannot have a public constructor");
786781
return FAILURE;
787782
}
788783
dbh->def_stmt_ce = pce;
@@ -792,11 +787,8 @@ static int pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value) /*
792787
}
793788
if ((item = zend_hash_index_find(Z_ARRVAL_P(value), 1)) != NULL) {
794789
if (Z_TYPE_P(item) != IS_ARRAY) {
795-
pdo_raise_impl_error(dbh, NULL, "HY000",
796-
"PDO::ATTR_STATEMENT_CLASS requires format array(classname, array(ctor_args)); "
797-
"ctor_args must be an array"
798-
);
799-
PDO_HANDLE_DBH_ERR();
790+
zend_type_error("PDO::ATTR_STATEMENT_CLASS ctor_args must be of type ?array, %s given",
791+
zend_zval_type_name(value));
800792
return FAILURE;
801793
}
802794
ZVAL_COPY(&dbh->def_stmt_ctor_args, item);
@@ -927,10 +919,11 @@ PHP_METHOD(PDO, exec)
927919
Z_PARAM_STRING(statement, statement_len)
928920
ZEND_PARSE_PARAMETERS_END();
929921

930-
if (!statement_len) {
931-
pdo_raise_impl_error(dbh, NULL, "HY000", "trying to execute an empty query");
932-
RETURN_FALSE;
922+
if (statement_len == 0) {
923+
zend_argument_value_error(1, "cannot be empty");
924+
RETURN_THROWS();
933925
}
926+
934927
PDO_DBH_CLEAR_ERR();
935928
PDO_CONSTRUCT_CHECK;
936929
ret = dbh->methods->doer(dbh, statement, statement_len);
@@ -955,8 +948,10 @@ PHP_METHOD(PDO, lastInsertId)
955948
Z_PARAM_STRING_OR_NULL(name, namelen)
956949
ZEND_PARSE_PARAMETERS_END();
957950

958-
PDO_DBH_CLEAR_ERR();
959951
PDO_CONSTRUCT_CHECK;
952+
953+
PDO_DBH_CLEAR_ERR();
954+
960955
if (!dbh->methods->last_id) {
961956
pdo_raise_impl_error(dbh, NULL, "IM001", "driver does not support lastInsertId()");
962957
RETURN_FALSE;
@@ -1060,13 +1055,20 @@ PHP_METHOD(PDO, query)
10601055
pdo_dbh_object_t *dbh_obj = Z_PDO_OBJECT_P(ZEND_THIS);
10611056
pdo_dbh_t *dbh = dbh_obj->inner;
10621057

1063-
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s|l!*", &statement, &statement_len, &fetch_mode, &fetch_mode_is_null, &args, &num_args)) {
1058+
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s|l!*", &statement, &statement_len,
1059+
&fetch_mode, &fetch_mode_is_null, &args, &num_args)) {
10641060
RETURN_THROWS();
10651061
}
10661062

1067-
PDO_DBH_CLEAR_ERR();
10681063
PDO_CONSTRUCT_CHECK;
10691064

1065+
if (statement_len == 0) {
1066+
zend_argument_value_error(1, "cannot be empty");
1067+
RETURN_THROWS();
1068+
}
1069+
1070+
PDO_DBH_CLEAR_ERR();
1071+
10701072
if (!pdo_stmt_instantiate(dbh, return_value, dbh->def_stmt_ce, &dbh->def_stmt_ctor_args)) {
10711073
if (EXPECTED(!EG(exception))) {
10721074
pdo_raise_impl_error(dbh, NULL, "HY000", "failed to instantiate user supplied statement class");
@@ -1090,8 +1092,7 @@ PHP_METHOD(PDO, query)
10901092

10911093
if (dbh->methods->preparer(dbh, statement, statement_len, stmt, NULL)) {
10921094
PDO_STMT_CLEAR_ERR();
1093-
if (fetch_mode_is_null || SUCCESS == pdo_stmt_setup_fetch_mode(stmt, fetch_mode, args, num_args)) {
1094-
1095+
if (fetch_mode_is_null || pdo_stmt_setup_fetch_mode(stmt, fetch_mode, 2, args, num_args)) {
10951096
/* now execute the statement */
10961097
PDO_STMT_CLEAR_ERR();
10971098
if (stmt->methods->executer(stmt)) {
@@ -1139,8 +1140,9 @@ PHP_METHOD(PDO, quote)
11391140
Z_PARAM_LONG(paramtype)
11401141
ZEND_PARSE_PARAMETERS_END();
11411142

1142-
PDO_DBH_CLEAR_ERR();
11431143
PDO_CONSTRUCT_CHECK;
1144+
1145+
PDO_DBH_CLEAR_ERR();
11441146
if (!dbh->methods->quoter) {
11451147
pdo_raise_impl_error(dbh, NULL, "IM001", "driver does not support quoting");
11461148
RETURN_FALSE;

0 commit comments

Comments
 (0)