Skip to content

Commit 65daa7a

Browse files
authored
Addressed various issues with PHP 7.4 beta1 (#1015)
1 parent 1a2b493 commit 65daa7a

11 files changed

+161
-139
lines changed

source/pdo_sqlsrv/pdo_stmt.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,11 +1129,11 @@ int pdo_sqlsrv_stmt_get_col_meta( _Inout_ pdo_stmt_t *stmt, _In_ zend_long colno
11291129
}
11301130
}
11311131
catch( core::CoreException& ) {
1132-
1132+
zval_ptr_dtor(return_value);
11331133
return FAILURE;
11341134
}
11351135
catch(...) {
1136-
1136+
zval_ptr_dtor(return_value);
11371137
DIE( "pdo_sqlsrv_stmt_get_col_meta: Unknown exception occurred while retrieving metadata." );
11381138
}
11391139

source/shared/core_stmt.cpp

Lines changed: 124 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -988,7 +988,7 @@ void core_sqlsrv_sensitivity_metadata( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
988988
return;
989989
}
990990

991-
if (stmt->current_sensitivity_metadata != NULL) {
991+
if (stmt->current_sensitivity_metadata) {
992992
// Already cached, so return
993993
return;
994994
}
@@ -1873,7 +1873,7 @@ void core_get_field_common( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i
18731873
// be returned as a zval.
18741874
case SQLSRV_PHPTYPE_DATETIME:
18751875
{
1876-
char* field_value_temp = NULL;
1876+
sqlsrv_malloc_auto_ptr<char> field_value_temp;
18771877
SQLLEN field_len_temp = 0;
18781878

18791879
field_value_temp = static_cast<char*>(sqlsrv_malloc(MAX_DATETIME_STRING_LEN));
@@ -1882,8 +1882,7 @@ void core_get_field_common( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i
18821882
SQLRETURN r = stmt->current_results->get_data(field_index + 1, SQL_C_CHAR, field_value_temp, MAX_DATETIME_STRING_LEN, &field_len_temp, true TSRMLS_CC);
18831883

18841884
if (r == SQL_NO_DATA || field_len_temp == SQL_NULL_DATA) {
1885-
sqlsrv_free(field_value_temp);
1886-
field_value_temp = NULL;
1885+
field_value_temp.reset();
18871886
field_len_temp = 0;
18881887
}
18891888

@@ -1892,6 +1891,7 @@ void core_get_field_common( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i
18921891
}
18931892

18941893
field_value = field_value_temp;
1894+
field_value_temp.transferred();
18951895
*field_len = field_len_temp;
18961896

18971897
break;
@@ -2420,142 +2420,151 @@ void format_decimal_numbers(_In_ SQLSMALLINT decimals_places, _In_ SQLSMALLINT f
24202420

24212421
void finalize_output_parameters( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
24222422
{
2423-
if( Z_ISUNDEF(stmt->output_params) )
2423+
if (Z_ISUNDEF(stmt->output_params))
24242424
return;
24252425

2426-
HashTable* params_ht = Z_ARRVAL( stmt->output_params );
2426+
HashTable* params_ht = Z_ARRVAL(stmt->output_params);
24272427
zend_ulong index = -1;
24282428
zend_string* key = NULL;
24292429
void* output_param_temp = NULL;
24302430

2431-
ZEND_HASH_FOREACH_KEY_PTR( params_ht, index, key, output_param_temp ) {
2432-
sqlsrv_output_param* output_param = static_cast<sqlsrv_output_param*>( output_param_temp );
2433-
zval* value_z = Z_REFVAL_P( output_param->param_z );
2434-
switch( Z_TYPE_P( value_z )) {
2435-
case IS_STRING:
2431+
try {
2432+
ZEND_HASH_FOREACH_KEY_PTR(params_ht, index, key, output_param_temp)
24362433
{
2437-
// adjust the length of the string to the value returned by SQLBindParameter in the ind_ptr parameter
2438-
char* str = Z_STRVAL_P( value_z );
2439-
SQLLEN str_len = stmt->param_ind_ptrs[output_param->param_num];
2440-
if( str_len == 0 ) {
2441-
core::sqlsrv_zval_stringl( value_z, "", 0 );
2442-
continue;
2443-
}
2444-
if( str_len == SQL_NULL_DATA ) {
2445-
zend_string_release( Z_STR_P( value_z ));
2446-
ZVAL_NULL( value_z );
2447-
continue;
2448-
}
2449-
2450-
// if there was more to output than buffer size to hold it, then throw a truncation error
2451-
int null_size = 0;
2452-
switch( output_param->encoding ) {
2453-
case SQLSRV_ENCODING_UTF8:
2454-
null_size = sizeof( SQLWCHAR ); // string isn't yet converted to UTF-8, still UTF-16
2455-
break;
2456-
case SQLSRV_ENCODING_SYSTEM:
2457-
null_size = 1;
2458-
break;
2459-
case SQLSRV_ENCODING_BINARY:
2460-
null_size = 0;
2461-
break;
2462-
default:
2463-
SQLSRV_ASSERT( false, "Invalid encoding in output_param structure." );
2464-
break;
2465-
}
2466-
CHECK_CUSTOM_ERROR( str_len > ( output_param->original_buffer_len - null_size ), stmt,
2467-
SQLSRV_ERROR_OUTPUT_PARAM_TRUNCATED, output_param->param_num + 1 ) {
2468-
throw core::CoreException();
2469-
}
2470-
2471-
// For ODBC 11+ see https://msdn.microsoft.com/en-us/library/jj219209.aspx
2472-
// A length value of SQL_NO_TOTAL for SQLBindParameter indicates that the buffer contains up to
2473-
// output_param->original_buffer_len data and is NULL terminated.
2474-
// The IF statement can be true when using connection pooling with unixODBC 2.3.4.
2475-
if ( str_len == SQL_NO_TOTAL )
2434+
sqlsrv_output_param* output_param = static_cast<sqlsrv_output_param*>(output_param_temp);
2435+
zval* value_z = Z_REFVAL_P(output_param->param_z);
2436+
switch (Z_TYPE_P(value_z)) {
2437+
case IS_STRING:
24762438
{
2477-
str_len = output_param->original_buffer_len - null_size;
2478-
}
2439+
// adjust the length of the string to the value returned by SQLBindParameter in the ind_ptr parameter
2440+
char* str = Z_STRVAL_P(value_z);
2441+
SQLLEN str_len = stmt->param_ind_ptrs[output_param->param_num];
2442+
if (str_len == 0) {
2443+
core::sqlsrv_zval_stringl(value_z, "", 0);
2444+
continue;
2445+
}
2446+
if (str_len == SQL_NULL_DATA) {
2447+
zend_string_release(Z_STR_P(value_z));
2448+
ZVAL_NULL(value_z);
2449+
continue;
2450+
}
24792451

2480-
if (output_param->encoding == SQLSRV_ENCODING_BINARY) {
2481-
// ODBC doesn't null terminate binary encodings, but PHP complains if a string isn't null terminated
2482-
// so we do that here if the length of the returned data is less than the original allocation. The
2483-
// original allocation null terminates the buffer already.
2484-
if (str_len < output_param->original_buffer_len) {
2485-
str[str_len] = '\0';
2452+
// if there was more to output than buffer size to hold it, then throw a truncation error
2453+
int null_size = 0;
2454+
switch (output_param->encoding) {
2455+
case SQLSRV_ENCODING_UTF8:
2456+
null_size = sizeof(SQLWCHAR); // string isn't yet converted to UTF-8, still UTF-16
2457+
break;
2458+
case SQLSRV_ENCODING_SYSTEM:
2459+
null_size = 1;
2460+
break;
2461+
case SQLSRV_ENCODING_BINARY:
2462+
null_size = 0;
2463+
break;
2464+
default:
2465+
SQLSRV_ASSERT(false, "Invalid encoding in output_param structure.");
2466+
break;
2467+
}
2468+
CHECK_CUSTOM_ERROR(str_len > (output_param->original_buffer_len - null_size), stmt,
2469+
SQLSRV_ERROR_OUTPUT_PARAM_TRUNCATED, output_param->param_num + 1)
2470+
{
2471+
throw core::CoreException();
24862472
}
2487-
core::sqlsrv_zval_stringl(value_z, str, str_len);
2488-
}
2489-
else {
2490-
param_meta_data metaData = output_param->getMetaData();
24912473

2492-
if (output_param->encoding != SQLSRV_ENCODING_CHAR) {
2493-
char* outString = NULL;
2494-
SQLLEN outLen = 0;
2495-
bool result = convert_string_from_utf16(output_param->encoding, reinterpret_cast<const SQLWCHAR*>(str), int(str_len / sizeof(SQLWCHAR)), &outString, outLen );
2496-
CHECK_CUSTOM_ERROR(!result, stmt, SQLSRV_ERROR_OUTPUT_PARAM_ENCODING_TRANSLATE, get_last_error_message()) {
2497-
throw core::CoreException();
2498-
}
2474+
// For ODBC 11+ see https://msdn.microsoft.com/en-us/library/jj219209.aspx
2475+
// A length value of SQL_NO_TOTAL for SQLBindParameter indicates that the buffer contains up to
2476+
// output_param->original_buffer_len data and is NULL terminated.
2477+
// The IF statement can be true when using connection pooling with unixODBC 2.3.4.
2478+
if (str_len == SQL_NO_TOTAL) {
2479+
str_len = output_param->original_buffer_len - null_size;
2480+
}
24992481

2500-
if (stmt->format_decimals && (metaData.sql_type == SQL_DECIMAL || metaData.sql_type == SQL_NUMERIC)) {
2501-
format_decimal_numbers(NO_CHANGE_DECIMAL_PLACES, metaData.decimal_digits, outString, &outLen);
2482+
if (output_param->encoding == SQLSRV_ENCODING_BINARY) {
2483+
// ODBC doesn't null terminate binary encodings, but PHP complains if a string isn't null terminated
2484+
// so we do that here if the length of the returned data is less than the original allocation. The
2485+
// original allocation null terminates the buffer already.
2486+
if (str_len < output_param->original_buffer_len) {
2487+
str[str_len] = '\0';
25022488
}
2503-
2504-
core::sqlsrv_zval_stringl(value_z, outString, outLen);
2505-
sqlsrv_free(outString);
2489+
core::sqlsrv_zval_stringl(value_z, str, str_len);
25062490
}
25072491
else {
2508-
if (stmt->format_decimals && (metaData.sql_type == SQL_DECIMAL || metaData.sql_type == SQL_NUMERIC)) {
2509-
format_decimal_numbers(NO_CHANGE_DECIMAL_PLACES, metaData.decimal_digits, str, &str_len);
2492+
param_meta_data metaData = output_param->getMetaData();
2493+
2494+
if (output_param->encoding != SQLSRV_ENCODING_CHAR) {
2495+
char* outString = NULL;
2496+
SQLLEN outLen = 0;
2497+
bool result = convert_string_from_utf16(output_param->encoding, reinterpret_cast<const SQLWCHAR*>(str), int(str_len / sizeof(SQLWCHAR)), &outString, outLen);
2498+
CHECK_CUSTOM_ERROR(!result, stmt, SQLSRV_ERROR_OUTPUT_PARAM_ENCODING_TRANSLATE, get_last_error_message())
2499+
{
2500+
throw core::CoreException();
2501+
}
2502+
2503+
if (stmt->format_decimals && (metaData.sql_type == SQL_DECIMAL || metaData.sql_type == SQL_NUMERIC)) {
2504+
format_decimal_numbers(NO_CHANGE_DECIMAL_PLACES, metaData.decimal_digits, outString, &outLen);
2505+
}
2506+
2507+
core::sqlsrv_zval_stringl(value_z, outString, outLen);
2508+
sqlsrv_free(outString);
25102509
}
2510+
else {
2511+
if (stmt->format_decimals && (metaData.sql_type == SQL_DECIMAL || metaData.sql_type == SQL_NUMERIC)) {
2512+
format_decimal_numbers(NO_CHANGE_DECIMAL_PLACES, metaData.decimal_digits, str, &str_len);
2513+
}
25112514

2512-
core::sqlsrv_zval_stringl(value_z, str, str_len);
2515+
core::sqlsrv_zval_stringl(value_z, str, str_len);
2516+
}
25132517
}
25142518
}
2515-
}
2516-
break;
2517-
case IS_LONG:
2518-
// for a long or a float, simply check if NULL was returned and set the parameter to a PHP null if so
2519-
if( stmt->param_ind_ptrs[output_param->param_num] == SQL_NULL_DATA ) {
2520-
ZVAL_NULL( value_z );
2521-
}
2522-
else if( output_param->is_bool ) {
2523-
convert_to_boolean( value_z );
2524-
}
2525-
else {
2526-
ZVAL_LONG( value_z, static_cast<int>( Z_LVAL_P( value_z )));
2527-
}
25282519
break;
2529-
case IS_DOUBLE:
2530-
// for a long or a float, simply check if NULL was returned and set the parameter to a PHP null if so
2531-
if (stmt->param_ind_ptrs[output_param->param_num] == SQL_NULL_DATA) {
2532-
ZVAL_NULL(value_z);
2533-
}
2534-
else if (output_param->php_out_type == SQLSRV_PHPTYPE_INT) {
2535-
// first check if its value is out of range
2536-
double dval = Z_DVAL_P(value_z);
2537-
if (dval > INT_MAX || dval < INT_MIN) {
2538-
CHECK_CUSTOM_ERROR(true, stmt, SQLSRV_ERROR_DOUBLE_CONVERSION_FAILED) {
2539-
throw core::CoreException();
2540-
}
2520+
case IS_LONG:
2521+
// for a long or a float, simply check if NULL was returned and set the parameter to a PHP null if so
2522+
if (stmt->param_ind_ptrs[output_param->param_num] == SQL_NULL_DATA) {
2523+
ZVAL_NULL(value_z);
25412524
}
2542-
// if the output param is a boolean, still convert to
2543-
// a long integer first to take care of rounding
2544-
convert_to_long(value_z);
2545-
if (output_param->is_bool) {
2525+
else if (output_param->is_bool) {
25462526
convert_to_boolean(value_z);
25472527
}
2528+
else {
2529+
ZVAL_LONG(value_z, static_cast<int>(Z_LVAL_P(value_z)));
2530+
}
2531+
break;
2532+
case IS_DOUBLE:
2533+
// for a long or a float, simply check if NULL was returned and set the parameter to a PHP null if so
2534+
if (stmt->param_ind_ptrs[output_param->param_num] == SQL_NULL_DATA) {
2535+
ZVAL_NULL(value_z);
2536+
}
2537+
else if (output_param->php_out_type == SQLSRV_PHPTYPE_INT) {
2538+
// first check if its value is out of range
2539+
double dval = Z_DVAL_P(value_z);
2540+
if (dval > INT_MAX || dval < INT_MIN) {
2541+
CHECK_CUSTOM_ERROR(true, stmt, SQLSRV_ERROR_DOUBLE_CONVERSION_FAILED)
2542+
{
2543+
throw core::CoreException();
2544+
}
2545+
}
2546+
// if the output param is a boolean, still convert to
2547+
// a long integer first to take care of rounding
2548+
convert_to_long(value_z);
2549+
if (output_param->is_bool) {
2550+
convert_to_boolean(value_z);
2551+
}
2552+
}
2553+
break;
2554+
default:
2555+
DIE("Illegal or unknown output parameter type. This should have been caught in core_sqlsrv_bind_parameter.");
2556+
break;
25482557
}
2549-
break;
2550-
default:
2551-
DIE( "Illegal or unknown output parameter type. This should have been caught in core_sqlsrv_bind_parameter." );
2552-
break;
2553-
}
2554-
value_z = NULL;
2555-
} ZEND_HASH_FOREACH_END();
2556-
2558+
value_z = NULL;
2559+
} ZEND_HASH_FOREACH_END();
2560+
}
2561+
catch (core::CoreException&) {
2562+
// empty the hash table due to exception caught
2563+
zend_hash_clean(Z_ARRVAL(stmt->output_params));
2564+
throw;
2565+
}
25572566
// empty the hash table since it's been processed
2558-
zend_hash_clean( Z_ARRVAL( stmt->output_params ));
2567+
zend_hash_clean(Z_ARRVAL(stmt->output_params));
25592568
return;
25602569
}
25612570

source/shared/core_stream.cpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,11 @@ int sqlsrv_stream_close( _Inout_ php_stream* stream, int /*close_handle*/ TSRMLS
4444
// read from a sqlsrv stream into the buffer provided by Zend. The parameters for binary vs. char are
4545
// set when sqlsrv_get_field is called by the user specifying which field type they want.
4646

47-
size_t sqlsrv_stream_read( _Inout_ php_stream* stream, _Out_writes_bytes_(count) char* buf, _Inout_ size_t count TSRMLS_DC )
47+
#if PHP_VERSION_ID >= 70400
48+
ssize_t sqlsrv_stream_read(_Inout_ php_stream* stream, _Out_writes_bytes_(count) char* buf, _Inout_ size_t count TSRMLS_DC)
49+
#else
50+
size_t sqlsrv_stream_read(_Inout_ php_stream* stream, _Out_writes_bytes_(count) char* buf, _Inout_ size_t count TSRMLS_DC)
51+
#endif
4852
{
4953
SQLLEN read = 0;
5054
SQLSMALLINT c_type = SQL_C_CHAR;
@@ -184,15 +188,20 @@ size_t sqlsrv_stream_read( _Inout_ php_stream* stream, _Out_writes_bytes_(count)
184188

185189
return static_cast<size_t>( read );
186190
}
187-
188-
catch( core::CoreException& ) {
189-
191+
catch (core::CoreException&) {
192+
#if PHP_VERSION_ID >= 70400
193+
return -1;
194+
#else
190195
return 0;
196+
#endif
191197
}
192-
catch( ... ) {
193-
194-
LOG( SEV_ERROR, "sqlsrv_stream_read: Unknown exception caught." );
198+
catch (...) {
199+
LOG(SEV_ERROR, "sqlsrv_stream_read: Unknown exception caught.");
200+
#if PHP_VERSION_ID >= 70400
201+
return -1;
202+
#else
195203
return 0;
204+
#endif
196205
}
197206
}
198207

test/functional/pdo_sqlsrv/pdo_utf8_conn.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,6 @@ if ($c !== false) {
2424

2525
Fatal error: Uncaught PDOException: SQLSTATE\[(28000|08001|HYT00)\]: .*\[Microsoft\]\[ODBC Driver 1[0-9] for SQL Server\](\[SQL Server\])?(Named Pipes Provider: Could not open a connection to SQL Server \[2\]\. |TCP Provider: Error code (0x2726|0x2AF9)|Login timeout expired|Login failed for user 'sa'\.) in .+(\/|\\)pdo_utf8_conn\.php:[0-9]+
2626
Stack trace:
27-
#0 .+(\/|\\)pdo_utf8_conn\.php\([0-9]+\): PDO->__construct\('sqlsrv:Server=l\.\.\.', 'sa', 'Sunshine4u'\)
27+
#0 .+(\/|\\)pdo_utf8_conn\.php\([0-9]+\): PDO->__construct(\(\)|\('sqlsrv:Server=l\.\.\.', 'sa', 'Sunshine4u'\))
2828
#1 {main}
2929
thrown in .+(\/|\\)pdo_utf8_conn\.php on line [0-9]+

test/functional/pdo_sqlsrv/pdostatement_fetchAll.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,6 @@ Test_9 : FETCH_INVALID :
431431

432432
Fatal error: Uncaught Error: Undefined class constant 'FETCH_UNKNOWN' in %s:%x
433433
Stack trace:
434-
#0 %s: fetchAllInvalid(Object(PDO), 'PDO_MainTypes')
434+
#0 %s: fetchAllInvalid(%S)
435435
#1 {main}
436436
thrown in %s on line %x

0 commit comments

Comments
 (0)