diff --git a/README.md b/README.md index 0b1f4354f..9afd8eb44 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,11 @@ The Microsoft Drivers for PHP for SQL Server Team ##Announcements -**September, 2016** : Updated Windows drivers (4.1.2) compiled with PHP 7.0.10 are available. Here is the list of updates: +**October 4 , 2016** : Updated Windows drivers (4.1.3) compiled with PHP 7.0.11 and 7.1.0RC3 are available. Here is the list of updates: + +- Fixed [issue #139](https://github.com/Microsoft/msphpsql/issues/139) : sqlsrv_fetch_object calls custom class constructor in static context and outputs an error. + +**September 9, 2016** : Updated Windows drivers (4.1.2) compiled with PHP 7.0.10 are available. Here is the list of updates: - Fixed [issue #119](https://github.com/Microsoft/msphpsql/issues/119) (modifying class name in sqlsrv_fetch_object). - Added following integer SQL Types constants for cases which function-like SQL types constants cannot be used e.g. type comparison: diff --git a/pdo_sqlsrv/CREDITS b/pdo_sqlsrv/CREDITS index 76237ad5b..3f8712e81 100644 --- a/pdo_sqlsrv/CREDITS +++ b/pdo_sqlsrv/CREDITS @@ -1 +1 @@ -Microsoft Drivers 4.1 for PHP for SQL Server (PDO driver) +Microsoft Drivers 4.1.0 for PHP for SQL Server (PDO driver) diff --git a/pdo_sqlsrv/pdo_dbh.cpp b/pdo_sqlsrv/pdo_dbh.cpp index a0659797d..77ea5ba74 100644 --- a/pdo_sqlsrv/pdo_dbh.cpp +++ b/pdo_sqlsrv/pdo_dbh.cpp @@ -17,11 +17,11 @@ // IN THE SOFTWARE. //--------------------------------------------------------------------------------------------------------------------------------- -#include "pdo_sqlsrv.h" +#include "php_pdo_sqlsrv.h" + #include #include #include - #include #include diff --git a/pdo_sqlsrv/pdo_init.cpp b/pdo_sqlsrv/pdo_init.cpp index 5c4936695..f3fca1b81 100644 --- a/pdo_sqlsrv/pdo_init.cpp +++ b/pdo_sqlsrv/pdo_init.cpp @@ -17,7 +17,7 @@ // IN THE SOFTWARE. //--------------------------------------------------------------------------------------------------------------------------------- -#include "pdo_sqlsrv.h" +#include "php_pdo_sqlsrv.h" #include #ifdef ZTS diff --git a/pdo_sqlsrv/pdo_parser.cpp b/pdo_sqlsrv/pdo_parser.cpp index 68e1c8eff..6e735b56b 100644 --- a/pdo_sqlsrv/pdo_parser.cpp +++ b/pdo_sqlsrv/pdo_parser.cpp @@ -19,7 +19,7 @@ // IN THE SOFTWARE. //--------------------------------------------------------------------------------------------------------------------------------- -#include "pdo_sqlsrv.h" +#include "php_pdo_sqlsrv.h" // Constructor conn_string_parser:: conn_string_parser( sqlsrv_context& ctx, const char* dsn, int len, _Inout_ HashTable* conn_options_ht ) diff --git a/pdo_sqlsrv/pdo_stmt.cpp b/pdo_sqlsrv/pdo_stmt.cpp index e8ce257c6..1579a2600 100644 --- a/pdo_sqlsrv/pdo_stmt.cpp +++ b/pdo_sqlsrv/pdo_stmt.cpp @@ -17,7 +17,7 @@ // IN THE SOFTWARE. //--------------------------------------------------------------------------------------------------------------------------------- -#include "pdo_sqlsrv.h" +#include "php_pdo_sqlsrv.h" // *** internal variables and constants *** diff --git a/pdo_sqlsrv/pdo_util.cpp b/pdo_sqlsrv/pdo_util.cpp index 817d9effc..89843b666 100644 --- a/pdo_sqlsrv/pdo_util.cpp +++ b/pdo_sqlsrv/pdo_util.cpp @@ -17,7 +17,7 @@ // IN THE SOFTWARE. //--------------------------------------------------------------------------------------------------------------------------------- -#include "pdo_sqlsrv.h" +#include "php_pdo_sqlsrv.h" #include "zend_exceptions.h" diff --git a/pdo_sqlsrv/php_pdo_sqlsrv.h b/pdo_sqlsrv/php_pdo_sqlsrv.h new file mode 100644 index 000000000..bf3285cad --- /dev/null +++ b/pdo_sqlsrv/php_pdo_sqlsrv.h @@ -0,0 +1,403 @@ +#ifndef PHP_PDO_SQLSRV_H +#define PHP_PDO_SQLSRV_H + +//--------------------------------------------------------------------------------------------------------------------------------- +// File: pdo_sqlsrv.h +// +// Contents: Declarations for the extension +// +// Microsoft Drivers 4.1 for PHP for SQL Server +// Copyright(c) Microsoft Corporation +// All rights reserved. +// MIT License +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""), +// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//--------------------------------------------------------------------------------------------------------------------------------- + +#include "core_sqlsrv.h" +#include "version.h" + +extern "C" { + +#include "pdo/php_pdo.h" +#include "pdo/php_pdo_driver.h" +#include "pdo/php_pdo_int.h" + +} + +#include +#include + + +//********************************************************************************************************************************* +// Constants and Types +//********************************************************************************************************************************* + +// sqlsrv driver specific PDO attributes +enum PDO_SQLSRV_ATTR { + + // Currently there are only three custom attributes for this driver. + SQLSRV_ATTR_ENCODING = PDO_ATTR_DRIVER_SPECIFIC, + SQLSRV_ATTR_QUERY_TIMEOUT, + SQLSRV_ATTR_DIRECT_QUERY, + SQLSRV_ATTR_CURSOR_SCROLL_TYPE, + SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE, + SQLSRV_ATTR_FETCHES_NUMERIC_TYPE, +}; + +// valid set of values for TransactionIsolation connection option +namespace PDOTxnIsolationValues { + + const char READ_UNCOMMITTED[] = "READ_UNCOMMITTED"; + const char READ_COMMITTED[] = "READ_COMMITTED"; + const char REPEATABLE_READ[] = "REPEATABLE_READ"; + const char SERIALIZABLE[] = "SERIALIZABLE"; + const char SNAPSHOT[] = "SNAPSHOT"; +} + +//********************************************************************************************************************************* +// Global variables +//********************************************************************************************************************************* + +extern "C" { + +// request level variables +ZEND_BEGIN_MODULE_GLOBALS(pdo_sqlsrv) + +unsigned int log_severity; +zend_long client_buffer_max_size; + +ZEND_END_MODULE_GLOBALS(pdo_sqlsrv) + +ZEND_EXTERN_MODULE_GLOBALS(pdo_sqlsrv); + +} + +// macros used to access the global variables. Use these to make global variable access agnostic to threads +#ifdef ZTS +#define PDO_SQLSRV_G(v) TSRMG(pdo_sqlsrv_globals_id, zend_pdo_sqlsrv_globals *, v) +#else +#define PDO_SQLSRV_G(v) pdo_sqlsrv_globals.v +#endif + +// INI settings and constants +// (these are defined as macros to allow concatenation as we do below) +#define INI_PDO_SQLSRV_CLIENT_BUFFER_MAX_SIZE "client_buffer_max_kb_size" +#define INI_PDO_SQLSRV_LOG "log_severity" +#define INI_PREFIX "pdo_sqlsrv." + +PHP_INI_BEGIN() + STD_PHP_INI_ENTRY( INI_PREFIX INI_PDO_SQLSRV_LOG , "0", PHP_INI_ALL, OnUpdateLong, log_severity, + zend_pdo_sqlsrv_globals, pdo_sqlsrv_globals ) + STD_PHP_INI_ENTRY( INI_PREFIX INI_PDO_SQLSRV_CLIENT_BUFFER_MAX_SIZE , INI_BUFFERED_QUERY_LIMIT_DEFAULT, PHP_INI_ALL, OnUpdateLong, + client_buffer_max_size, zend_pdo_sqlsrv_globals, pdo_sqlsrv_globals ) +PHP_INI_END() + +// henv context for creating connections +extern sqlsrv_context* g_henv_cp; +extern sqlsrv_context* g_henv_ncp; + + +//********************************************************************************************************************************* +// Initialization +//********************************************************************************************************************************* + +// module global variables (initialized in minit and freed in mshutdown) +extern HashTable* g_pdo_errors_ht; + +// module initialization +PHP_MINIT_FUNCTION(pdo_sqlsrv); +// module shutdown function +PHP_MSHUTDOWN_FUNCTION(pdo_sqlsrv); +// request initialization function +PHP_RINIT_FUNCTION(pdo_sqlsrv); +// request shutdown function +PHP_RSHUTDOWN_FUNCTION(pdo_sqlsrv); +// module info function (info returned by phpinfo()) +PHP_MINFO_FUNCTION(pdo_sqlsrv); + +extern zend_module_entry g_pdo_sqlsrv_module_entry; // describes the extension to PHP + +//********************************************************************************************************************************* +// PDO DSN Parser +//********************************************************************************************************************************* + +// Parser class used to parse DSN connection string. +class conn_string_parser +{ + enum States + { + FirstKeyValuePair, + Key, + Value, + ValueContent1, + ValueContent2, + RCBEncountered, + NextKeyValuePair, + }; + + private: + const char* conn_str; + sqlsrv_context* ctx; + int len; + int pos; + unsigned int current_key; + const char* current_key_name; + HashTable* conn_options_ht; + inline bool next( void ); + inline bool is_eos( void ); + inline bool is_white_space( char c ); + bool discard_white_spaces( void ); + int discard_trailing_white_spaces( const char* str, int len ); + void conn_string_parser::validate_key( const char *key, int key_len TSRMLS_DC ); + void add_key_value_pair( const char* value, int len TSRMLS_DC ); + + public: + conn_string_parser( sqlsrv_context& ctx, const char* dsn, int len, _Inout_ HashTable* conn_options_ht ); + void parse_conn_string( TSRMLS_D ); +}; + +//********************************************************************************************************************************* +// Connection +//********************************************************************************************************************************* +extern const connection_option PDO_CONN_OPTS[]; + +int pdo_sqlsrv_db_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC); + +// a core layer pdo dbh object. This object inherits and overrides the statement factory +struct pdo_sqlsrv_dbh : public sqlsrv_conn { + + zval* stmts; + bool direct_query; + long query_timeout; + zend_long client_buffer_max_size; + SQLSRV_ENCODING bind_param_encoding; + bool fetch_numeric; + + pdo_sqlsrv_dbh( SQLHANDLE h, error_callback e, void* driver TSRMLS_DC ); +}; + + +//********************************************************************************************************************************* +// Statement +//********************************************************************************************************************************* + +struct stmt_option_encoding : public stmt_option_functor { + + virtual void operator()( sqlsrv_stmt* stmt, stmt_option const* /*opt*/, zval* value_z TSRMLS_DC ); +}; + +struct stmt_option_scrollable : public stmt_option_functor { + + virtual void operator()( sqlsrv_stmt* stmt, stmt_option const* /*opt*/, zval* value_z TSRMLS_DC ); +}; + +struct stmt_option_direct_query : public stmt_option_functor { + + virtual void operator()( sqlsrv_stmt* stmt, stmt_option const* /*opt*/, zval* value_z TSRMLS_DC ); +}; + +struct stmt_option_cursor_scroll_type : public stmt_option_functor { + + virtual void operator()( sqlsrv_stmt* stmt, stmt_option const* /*opt*/, zval* value_z TSRMLS_DC ); +}; + +struct stmt_option_emulate_prepares : public stmt_option_functor { + + virtual void operator()( sqlsrv_stmt* stmt, stmt_option const* /*opt*/, zval* value_z TSRMLS_DC ); +}; + +struct stmt_option_fetch_numeric : public stmt_option_functor { + virtual void operator()( sqlsrv_stmt* stmt, stmt_option const* /*opt*/, zval* value_z TSRMLS_DC ); +}; + +extern struct pdo_stmt_methods pdo_sqlsrv_stmt_methods; + +// a core layer pdo stmt object. This object inherits and overrides the callbacks necessary +struct pdo_sqlsrv_stmt : public sqlsrv_stmt { + + pdo_sqlsrv_stmt( sqlsrv_conn* c, SQLHANDLE handle, error_callback e, void* drv TSRMLS_DC ) : + sqlsrv_stmt( c, handle, e, drv TSRMLS_CC ), + direct_query( false ), + direct_query_subst_string( NULL ), + direct_query_subst_string_len( 0 ), + bound_column_param_types( NULL ), + fetch_numeric( false ) + { + pdo_sqlsrv_dbh* db = static_cast( c ); + direct_query = db->direct_query; + fetch_numeric = db->fetch_numeric; + } + + virtual ~pdo_sqlsrv_stmt( void ); + + // driver specific conversion rules from a SQL Server/ODBC type to one of the SQLSRV_PHPTYPE_* constants + // for PDO, everything is a string, so we return SQLSRV_PHPTYPE_STRING for all SQL types + virtual sqlsrv_phptype sql_type_to_php_type( SQLINTEGER sql_type, SQLUINTEGER size, bool prefer_string_to_stream, bool prefer_number_to_string ); + + bool direct_query; // flag set if the query should be executed directly or prepared + const char* direct_query_subst_string; // if the query is direct, hold the substitution string if using named parameters + size_t direct_query_subst_string_len; // length of query string used for direct queries + + // meta data for current result set + std::vector > current_meta_data; + pdo_param_type* bound_column_param_types; + bool fetch_numeric; +}; + + +//********************************************************************************************************************************* +// Error Handling Functions +//********************************************************************************************************************************* + +// represents the mapping between an error_code and the corresponding error message. +struct pdo_error { + + unsigned int error_code; + sqlsrv_error_const sqlsrv_error; +}; + +// called when an error occurs in the core layer. These routines are set as the error_callback in a +// context. The context is passed to this function since it contains the function + +bool pdo_sqlsrv_handle_env_error( sqlsrv_context& ctx, unsigned int sqlsrv_error_code, bool warning TSRMLS_DC, + va_list* print_args ); +bool pdo_sqlsrv_handle_dbh_error( sqlsrv_context& ctx, unsigned int sqlsrv_error_code, bool warning TSRMLS_DC, + va_list* print_args ); +bool pdo_sqlsrv_handle_stmt_error( sqlsrv_context& ctx, unsigned int sqlsrv_error_code, bool warning TSRMLS_DC, + va_list* print_args ); + +// pointer to the function to return the class entry for the PDO exception Set in MINIT +extern zend_class_entry* (*pdo_get_exception_class)( void ); + +// common routine to transfer a sqlsrv_context's error to a PDO zval +void pdo_sqlsrv_retrieve_context_error( sqlsrv_error const* last_error, zval* pdo_zval ); + +// reset the errors from the last operation +inline void pdo_reset_dbh_error( pdo_dbh_t* dbh TSRMLS_DC ) +{ + strcpy_s( dbh->error_code, sizeof( dbh->error_code ), "00000" ); // 00000 means no error + + // release the last statement from the dbh so that error handling won't have a statement passed to it + if( dbh->query_stmt ) { + dbh->query_stmt = NULL; + zend_objects_store_del( Z_OBJ_P(&dbh->query_stmt_zval TSRMLS_CC) ); + } + + // if the driver isn't valid, just return (PDO calls close sometimes more than once?) + if( dbh->driver_data == NULL ) { + return; + } + + // reset the last error on the sqlsrv_context + sqlsrv_context* ctx = static_cast( dbh->driver_data ); + + if( ctx->last_error() ) { + ctx->last_error().reset(); + } +} + +#define PDO_RESET_DBH_ERROR pdo_reset_dbh_error( dbh TSRMLS_CC ); + +inline void pdo_reset_stmt_error( pdo_stmt_t* stmt ) +{ + strcpy_s( stmt->error_code, sizeof( stmt->error_code ), "00000" ); // 00000 means no error + + // if the driver isn't valid, just return (PDO calls close sometimes more than once?) + if( stmt->driver_data == NULL ) { + return; + } + + // reset the last error on the sqlsrv_context + sqlsrv_context* ctx = static_cast( stmt->driver_data ); + + if( ctx->last_error() ) { + ctx->last_error().reset(); + } +} + +#define PDO_RESET_STMT_ERROR pdo_reset_stmt_error( stmt ); + +// validate the driver objects +#define PDO_VALIDATE_CONN if( dbh->driver_data == NULL ) { DIE( "Invalid driver data in PDO object." ); } +#define PDO_VALIDATE_STMT if( stmt->driver_data == NULL ) { DIE( "Invalid driver data in PDOStatement object." ); } + + +//********************************************************************************************************************************* +// Utility Functions +//********************************************************************************************************************************* + +// List of PDO specific error messages. +enum PDO_ERROR_CODES { + + PDO_SQLSRV_ERROR_INVALID_DBH_ATTR = SQLSRV_ERROR_DRIVER_SPECIFIC, + PDO_SQLSRV_ERROR_INVALID_STMT_ATTR, + PDO_SQLSRV_ERROR_INVALID_ENCODING, + PDO_SQLSRV_ERROR_INVALID_DRIVER_PARAM, + PDO_SQLSRV_ERROR_PDO_STMT_UNSUPPORTED, + PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR, + PDO_SQLSRV_ERROR_STMT_LEVEL_ATTR, + PDO_SQLSRV_ERROR_READ_ONLY_DBH_ATTR, + PDO_SQLSRV_ERROR_INVALID_STMT_OPTION, + PDO_SQLSRV_ERROR_INVALID_CURSOR_TYPE, + PDO_SQLSRV_ERROR_FUNCTION_NOT_IMPLEMENTED, + PDO_SQLSRV_ERROR_PARAM_PARSE, + PDO_SQLSRV_ERROR_LAST_INSERT_ID, + PDO_SQLSRV_ERROR_INVALID_COLUMN_DRIVER_DATA, + PDO_SQLSRV_ERROR_COLUMN_TYPE_DOES_NOT_SUPPORT_ENCODING, + PDO_SQLSRV_ERROR_INVALID_DRIVER_COLUMN_ENCODING, + PDO_SQLSRV_ERROR_INVALID_DRIVER_PARAM_TYPE, + PDO_SQLSRV_ERROR_INVALID_DRIVER_PARAM_ENCODING, + PDO_SQLSRV_ERROR_INVALID_PARAM_DIRECTION, + PDO_SQLSRV_ERROR_INVALID_OUTPUT_STRING_SIZE, + PDO_SQLSRV_ERROR_CURSOR_ATTR_AT_PREPARE_ONLY, + PDO_SQLSRV_ERROR_INVALID_DSN_STRING, + PDO_SQLSRV_ERROR_INVALID_DSN_KEY, + PDO_SQLSRV_ERROR_INVALID_DSN_VALUE, + PDO_SQLSRV_ERROR_SERVER_NOT_SPECIFIED, + PDO_SQLSRV_ERROR_DSN_STRING_ENDED_UNEXPECTEDLY, + PDO_SQLSRV_ERROR_EXTRA_SEMI_COLON_IN_DSN_STRING, + SQLSRV_ERROR_UNESCAPED_RIGHT_BRACE_IN_DSN, + PDO_SQLSRV_ERROR_RCB_MISSING_IN_DSN_VALUE, + PDO_SQLSRV_ERROR_DQ_ATTR_AT_PREPARE_ONLY, + PDO_SQLSRV_ERROR_INVALID_COLUMN_INDEX, + PDO_SQLSRV_ERROR_INVALID_OUTPUT_PARAM_TYPE, + PDO_SQLSRV_ERROR_INVALID_CURSOR_WITH_SCROLL_TYPE, + +}; + +extern pdo_error PDO_ERRORS[]; + +#define THROW_PDO_ERROR( ctx, custom, ... ) \ + call_error_handler( ctx, custom TSRMLS_CC, false, __VA_ARGS__ ); \ + throw pdo::PDOException(); + +namespace pdo { + + // an error which occurred in our PDO driver, NOT an exception thrown by PDO + struct PDOException : public core::CoreException { + + PDOException() : CoreException() + { + } + }; + +} // namespace pdo + +// called pdo_parse_params in php_pdo_driver.h +// we renamed it for 2 reasons: 1) we can't have the same name since it would conflict with our dynamic linking, and +// 2) this is a more precise name +extern int (*pdo_subst_named_params)(pdo_stmt_t *stmt, char *inquery, size_t inquery_len, + char **outquery, size_t *outquery_len TSRMLS_DC); + +// logger for pdo_sqlsrv called by the core layer when it wants to log something with the LOG macro +void pdo_sqlsrv_log( unsigned int severity TSRMLS_DC, const char* msg, va_list* print_args ); + +#endif /* PHP_PDO_SQLSRV_H */ + diff --git a/sqlsrv/CREDITS b/sqlsrv/CREDITS index 76237ad5b..73961bd6b 100644 --- a/sqlsrv/CREDITS +++ b/sqlsrv/CREDITS @@ -1 +1 @@ -Microsoft Drivers 4.1 for PHP for SQL Server (PDO driver) +Microsoft Drivers 4.1.0 for PHP for SQL Server diff --git a/sqlsrv/stmt.cpp b/sqlsrv/stmt.cpp index 67a876797..46ee33452 100644 --- a/sqlsrv/stmt.cpp +++ b/sqlsrv/stmt.cpp @@ -794,7 +794,6 @@ PHP_FUNCTION( sqlsrv_fetch_object ) } class_name = Z_STRVAL( *class_name_z ); class_name_len = Z_STRLEN( *class_name_z ); - //zend_str_tolower( class_name, class_name_len ); } if( ctor_params_z && Z_TYPE_P( ctor_params_z ) != IS_ARRAY ) { @@ -879,20 +878,22 @@ PHP_FUNCTION( sqlsrv_fetch_object ) memset( &fci, 0, sizeof( fci )); fci.size = sizeof( fci ); +#if PHP_VERSION_ID < 70100 fci.function_table = &( class_entry )->function_table; +#endif ZVAL_UNDEF( &( fci.function_name ) ); fci.retval = &ctor_retval_z; fci.param_count = num_params; fci.params = params_m; // purposefully not transferred since ownership isn't actually transferred. - fci.object = reinterpret_cast( &retval_z ); + fci.object = Z_OBJ_P( &retval_z ); memset( &fcic, 0, sizeof( fcic )); fcic.initialized = 1; fcic.function_handler = class_entry->constructor; fcic.calling_scope = class_entry; - fci.object = reinterpret_cast( &retval_z ); + fcic.object = Z_OBJ_P( &retval_z ); zr = zend_call_function( &fci, &fcic TSRMLS_CC ); CHECK_ZEND_ERROR( zr, stmt, SS_SQLSRV_ERROR_ZEND_OBJECT_FAILED, class_name ) {