in source/pdo_sqlsrv/pdo_dbh.cpp [1652:1841]
int pdo_sqlsrv_dbh_quote( _Inout_ pdo_dbh_t* dbh, _In_reads_(unquoted_len) const char* unquoted, _In_ size_t unquoted_len, _Outptr_result_buffer_(*quoted_len) char **quoted, _Out_ size_t* quoted_len,
enum pdo_param_type paramtype )
#else
zend_string* pdo_sqlsrv_dbh_quote(_Inout_ pdo_dbh_t* dbh, _In_ const zend_string *unquoted, _In_ enum pdo_param_type paramtype)
#endif
{
PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN;
PDO_LOG_DBH_ENTRY;
SQLSRV_ENCODING encoding = SQLSRV_ENCODING_CHAR;
bool use_national_char_set = false;
pdo_sqlsrv_dbh* driver_dbh = static_cast<pdo_sqlsrv_dbh*>(dbh->driver_data);
SQLSRV_ASSERT(driver_dbh != NULL, "pdo_sqlsrv_dbh_quote: driver_data object was NULL.");
// get the current object in PHP; this distinguishes pdo_sqlsrv_dbh_quote being called from:
// 1. PDO::quote() - object name is PDO
// 2. PDOStatement::execute() - object name is PDOStatement
zend_execute_data* execute_data = EG( current_execute_data );
zval *object = getThis();
// iterate through parents to find "PDOStatement"
bool is_statement = false;
if ( object ) {
zend_class_entry* curr_class = ( Z_OBJ_P( object ))->ce;
while ( curr_class != NULL ) {
if ( strcmp( reinterpret_cast<const char*>( curr_class->name->val ), "PDOStatement" ) == 0 ) {
is_statement = true;
break;
}
curr_class = curr_class->parent;
}
}
// only change the encoding if quote is called from the statement level (which should only be called when a statement
// is prepared with emulate prepared on)
if (is_statement) {
pdo_stmt_t *stmt = Z_PDO_STMT_P(object);
SQLSRV_ASSERT(stmt != NULL, "pdo_sqlsrv_dbh_quote: stmt object was null");
// set the encoding to be the encoding of the statement otherwise set to be the encoding of the dbh
pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast<pdo_sqlsrv_stmt*>(stmt->driver_data);
SQLSRV_ASSERT(driver_stmt != NULL, "pdo_sqlsrv_dbh_quote: driver_data object was null");
encoding = driver_stmt->encoding();
if (encoding == SQLSRV_ENCODING_INVALID || encoding == SQLSRV_ENCODING_DEFAULT) {
pdo_sqlsrv_dbh* stmt_driver_dbh = reinterpret_cast<pdo_sqlsrv_dbh*>(stmt->driver_data);
encoding = stmt_driver_dbh->encoding();
}
// get the placeholder at the current position in driver_stmt->placeholders ht
// Normally it's not a good idea to alter the internal pointer in a hashed array
// (see pull request 634 on GitHub) but in this case this is for internal use only
zval* placeholder = NULL;
if ((placeholder = zend_hash_get_current_data(driver_stmt->placeholders)) != NULL && zend_hash_move_forward(driver_stmt->placeholders) == SUCCESS && stmt->bound_params != NULL) {
pdo_bound_param_data* param = NULL;
if (Z_TYPE_P(placeholder) == IS_STRING) {
param = reinterpret_cast<pdo_bound_param_data*>(zend_hash_find_ptr(stmt->bound_params, Z_STR_P(placeholder)));
}
else if (Z_TYPE_P(placeholder) == IS_LONG) {
param = reinterpret_cast<pdo_bound_param_data*>(zend_hash_index_find_ptr(stmt->bound_params, Z_LVAL_P(placeholder)));
}
if (NULL != param) {
SQLSRV_ENCODING param_encoding = static_cast<SQLSRV_ENCODING>(Z_LVAL(param->driver_params));
if (param_encoding != SQLSRV_ENCODING_INVALID) {
encoding = param_encoding;
}
}
}
}
use_national_char_set = (driver_dbh->use_national_characters == 1 || encoding == SQLSRV_ENCODING_UTF8);
#if PHP_VERSION_ID >= 70200
if ((paramtype & PDO_PARAM_STR_NATL) == PDO_PARAM_STR_NATL) {
use_national_char_set = true;
}
if ((paramtype & PDO_PARAM_STR_CHAR) == PDO_PARAM_STR_CHAR) {
use_national_char_set = false;
}
#endif
if (encoding == SQLSRV_ENCODING_BINARY) {
#if PHP_VERSION_ID < 80100
*quoted_len = (unquoted_len * 2) + 2; // each character will be converted to 2 hex digits and prepend '0x' to the result
*quoted = reinterpret_cast<char*>(sqlsrv_malloc(*quoted_len, sizeof(char), 1)); // include space for null terminator
memset(*quoted, '\0', *quoted_len + 1);
unsigned int pos = 0;
(*quoted)[pos++] = '0';
(*quoted)[pos++] = 'x';
for (size_t index = 0; index < unquoted_len && unquoted[index] != '\0'; ++index) {
// On success, snprintf returns the total number of characters written
// On failure, a negative number is returned
// The generated string has a length of at most len - 1, so
// len is 3 (2 hex digits + 1)
// Requires "& 0x000000FF", or snprintf will translate "0x90" to "0xFFFFFF90"
int n = snprintf((char*)(*quoted + pos), 3, "%02X", unquoted[index] & 0x000000FF);
if (n < 0) {
// Something went wrong, simply return 0 (failure)
return 0;
}
pos += 2;
}
return 1;
#else
size_t unquoted_len = ZSTR_LEN(unquoted);
const char *unquoted_str = ZSTR_VAL(unquoted);
sqlsrv_malloc_auto_ptr<char> quoted;
size_t quoted_len = (unquoted_len * 2) + 2; // each character will be converted to 2 hex digits and prepend '0x' to the result
quoted = reinterpret_cast<char*>(sqlsrv_malloc(quoted_len, sizeof(char), 1)); // include space for null terminator
memset(quoted, '\0', quoted_len + 1);
unsigned int pos = 0;
quoted[pos++] = '0';
quoted[pos++] = 'x';
char *p = quoted;
for (size_t index = 0; index < unquoted_len && unquoted_str[index] != '\0'; ++index) {
// On success, snprintf returns the total number of characters written
// On failure, a negative number is returned
// The generated string has a length of at most len - 1, so
// len is 3 (2 hex digits + 1)
// Requires "& 0x000000FF", or snprintf will translate "0x90" to "0xFFFFFF90"
int n = snprintf((char*)(p + pos), 3, "%02X", unquoted_str[index] & 0x000000FF);
if (n < 0) {
// Something went wrong, simply return NULL (failure)
return NULL;
}
pos += 2;
}
zend_string* zstr = zend_string_init(quoted, quoted_len, 0);
return zstr;
#endif
}
else {
// The minimum number of single quotes needed is 2 -- the initial start and end quotes
// Add the letter N before the initial quote if the encoding is UTF8
int quotes_needed = (use_national_char_set) ? 3 : 2;
char c = '\'';
#if PHP_VERSION_ID < 80100
std::string tmp_str(unquoted, unquoted_len); // Copy all unquoted_len characters from unquoted
#else
size_t unquoted_len = ZSTR_LEN(unquoted);
const char *unquoted_str = ZSTR_VAL(unquoted);
std::string tmp_str(unquoted_str, unquoted_len); // Copy all unquoted_len characters from unquoted
#endif
std::size_t found = tmp_str.find(c); // Find the first single quote
while (found != std::string::npos) {
tmp_str.insert(found + 1, 1, c); // Insert an additional single quote
found = tmp_str.find(c, found + 2); // Find the next single quote
}
size_t len = tmp_str.length();
#if PHP_VERSION_ID < 80100
*quoted_len = quotes_needed + len; // The new length should be number of quotes plus the length of tmp_str
*quoted = reinterpret_cast<char*>(sqlsrv_malloc(*quoted_len, sizeof(char), 1)); // include space for null terminator
memset(*quoted, '\0', *quoted_len + 1);
char *p = *quoted;
#else
sqlsrv_malloc_auto_ptr<char> quoted;
size_t quoted_len = quotes_needed + len; // length returned to the caller should not account for null terminator
quoted = reinterpret_cast<char*>(sqlsrv_malloc(quoted_len, sizeof(char), 1)); // include space for null terminator
memset(quoted, '\0', quoted_len + 1);
char *p = quoted;
#endif
size_t pos = 0;
if (use_national_char_set) { // Insert the letter N if the encoding is UTF8
*(p + (pos++)) = 'N';
}
*(p + (pos++)) = c; // Add the initial quote
tmp_str.copy(p + pos, len, 0); // Copy tmp_str to *quoted
pos += len;
*(p + pos) = c; // Add the end quote
#if PHP_VERSION_ID < 80100
return 1;
#else
zend_string* zstr = zend_string_init(quoted, quoted_len, 0);
return zstr;
#endif
}
}