├── tests ├── skipif-php8-or-newer.inc ├── config.inc ├── skipif-php7-or-older.inc ├── 001-FIELDS30.sql ├── skipif.inc ├── ibase_num_fields_001.phpt ├── fbclient_vers_001.phpt ├── ibase_num_params_001.phpt ├── ibase_param_info_002.phpt ├── ibase_trans_005.phpt ├── ibase_commit_001.phpt ├── ibase_rollback_002.phpt ├── ibase_param_info_003.phpt ├── ibase_commit_ret_001.phpt ├── ibase_rollback_ret_001.phpt ├── ibase_num_fields_004.phpt ├── ibase_trans_010.phpt ├── issue89_001.phpt ├── ibase_num_fields_003.phpt ├── ibase_close_001.phpt ├── bug46247_003.phpt ├── ibase_errmsg_001.phpt ├── bug46247_004.phpt ├── ibase_num_fields_002.phpt ├── ibase_trans_003.phpt ├── ibase_field_info_005.phpt ├── bug46247_002.phpt ├── ibase_close_002.phpt ├── ibase_close_004.phpt ├── ibase_num_params_003.phpt ├── ibase_trans_001.phpt ├── ibase_drop_db_001.phpt ├── time_003.phpt ├── ibase_trans_011.phpt ├── ibase_trans_015.phpt ├── time_001.phpt ├── bug45575.phpt ├── 002.phpt ├── ibase_close_003.phpt ├── ibase_trans_002.phpt ├── ibase_trans_014.phpt ├── ibase_close_005.phpt ├── ibase_free_query_002.phpt ├── time_002.phpt ├── time_004.phpt ├── ibase_num_combi_001.phpt ├── ibase_num_params_004.phpt ├── 001-FIELDS40.sql ├── ibase_field_info_002.phpt ├── ibase_affected_rows_001.phpt ├── ibase_trans_008.phpt ├── issue35_001.phpt ├── ibase_trans_004.phpt ├── ibase_trans_006.phpt ├── ibase_param_info_001.phpt ├── ibase_free_query_001.phpt ├── ibase_drop_db_004.phpt ├── fb40fields_002.phpt ├── ibase_drop_db_003.phpt ├── fb40fields_001.phpt ├── use_after_free-002.phpt ├── use_after_free-001.phpt ├── ibase_num_params_002.phpt ├── ibase_trans_012.phpt ├── ibase_drop_db_002.phpt ├── ibase_rollback_001.phpt ├── ibase_trans_013.phpt ├── ibase_field_info_001.phpt ├── ibase_field_info_004.phpt ├── ibase_field_info_003.phpt ├── 001-table.sql ├── bug46543.phpt ├── datatype_char_utf8.phpt ├── 008.phpt ├── ibase_trans_007.phpt ├── issue77_001.phpt ├── returning_001.phpt ├── ibase_trans_009.phpt ├── bug46247_001.phpt ├── bug45373.phpt ├── datatype_int128.phpt ├── ibase_param_info_004.phpt ├── ibase_name_result_001.phpt ├── long_names_002.phpt ├── datatype_001.phpt ├── 001-FIELDS25.sql ├── interbase.inc ├── long_names_001.phpt ├── proc-001.phpt ├── nulls_001.phpt ├── common.inc ├── functions.inc ├── 007.phpt ├── 003.phpt ├── 005.phpt ├── 006.phpt └── 004.phpt ├── CREDITS ├── .github ├── actions │ └── install-linux │ │ └── action.yml ├── FUNDING.yml └── workflows │ └── main.yml ├── config.w32 ├── .gitignore ├── firebird_utils.h ├── config.m4 ├── LICENSE ├── php_interbase.h ├── firebird_utils.cpp ├── README.md ├── php_ibase_includes.h ├── ibase_events.c ├── php_ibase_udf.c └── ibase_blobs.c /tests/skipif-php8-or-newer.inc: -------------------------------------------------------------------------------- 1 | = 8) { 4 | die('skip: This test verifies behavior that can only be observed in PHP versions < 8.0'); 5 | } 6 | -------------------------------------------------------------------------------- /tests/config.inc: -------------------------------------------------------------------------------- 1 | = 8.0'); 4 | } 5 | ?> 6 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | InterBase 2 | Jouni Ahto, Andrew Avdeev, Ard Biesheuvel 3 | 4 | Firebird 5 | You can find the contributors list at https://github.com/FirebirdSQL/php-firebird/graphs/contributors. 6 | -------------------------------------------------------------------------------- /tests/001-FIELDS30.sql: -------------------------------------------------------------------------------- 1 | RECREATE TABLE FIELDS30 ( 2 | ID INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 3 | BOOL_FIELD BOOLEAN DEFAULT TRUE 4 | ); 5 | -------------------------------------------------------------------------------- /tests/skipif.inc: -------------------------------------------------------------------------------- 1 | 5 | --FILE-- 6 | 15 | --EXPECTF-- 16 | int(2) 17 | -------------------------------------------------------------------------------- /tests/fbclient_vers_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Get fbclient version 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 16 | --EXPECT-- 17 | bool(true) 18 | -------------------------------------------------------------------------------- /tests/ibase_num_params_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_num_params(): Basic test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 16 | --EXPECTF-- 17 | int(2) 18 | -------------------------------------------------------------------------------- /tests/ibase_param_info_002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_param_info(): Error if called with a single argument 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 14 | --EXPECTF-- 15 | Warning: ibase_param_info() expects exactly 2 parameters, 1 given in %s on line %d 16 | NULL 17 | -------------------------------------------------------------------------------- /tests/ibase_trans_005.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_trans(): handles 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 17 | --EXPECTF-- 18 | resource(%d) of type (Firebird/InterBase transaction) 19 | -------------------------------------------------------------------------------- /tests/ibase_commit_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_commit(): Make sure the method can be invoked with zero arguments 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 17 | --EXPECTF-- 18 | bool(true) 19 | -------------------------------------------------------------------------------- /tests/ibase_rollback_002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_rollback(): Make sure the method can be invoked with zero arguments 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 17 | --EXPECTF-- 18 | bool(true) 19 | -------------------------------------------------------------------------------- /tests/ibase_param_info_003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_param_info(): Error if called with a single argument 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 14 | --EXPECTF-- 15 | Fatal error: Uncaught ArgumentCountError: ibase_param_info() expects exactly 2 arguments, 1 given in %a 16 | -------------------------------------------------------------------------------- /tests/ibase_commit_ret_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_commit_ret(): Make sure the method can be invoked with zero arguments 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 17 | --EXPECTF-- 18 | bool(true) 19 | -------------------------------------------------------------------------------- /tests/ibase_rollback_ret_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_rollback_ret(): Make sure the method can be invoked with zero arguments 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 17 | --EXPECTF-- 18 | bool(true) 19 | -------------------------------------------------------------------------------- /tests/ibase_num_fields_004.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_num_fields(): Make sure passing zero arguments to the function throws an error 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 14 | --EXPECTF-- 15 | Fatal error: Uncaught ArgumentCountError: ibase_num_fields() expects exactly 1 argument, 0 given in %a 16 | -------------------------------------------------------------------------------- /tests/ibase_trans_010.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_trans(): transaction control with SQL - commit default transaction 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 17 | --EXPECT-- 18 | bool(true) 19 | bool(true) 20 | -------------------------------------------------------------------------------- /tests/issue89_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Issue #89: Passing result from ibase_prepare() to ibase_fetch_*() causes segfault. 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | 19 | --EXPECT-- 20 | bool(false) 21 | -------------------------------------------------------------------------------- /tests/ibase_num_fields_003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_num_fields(): Make sure passing an integer to the function throws an error. 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 14 | --EXPECTF-- 15 | Fatal error: Uncaught TypeError: ibase_num_fields(): Argument #1 ($query_result) must be of type resource, int given in %a 16 | -------------------------------------------------------------------------------- /tests/ibase_close_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_close(): Basic test 3 | --SKIPIF-- 4 | 12 | --FILE-- 13 | 23 | --EXPECTF-- 24 | bool(true) 25 | bool(true) 26 | bool(true) 27 | -------------------------------------------------------------------------------- /tests/bug46247_003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Bug #46247 (ibase_set_event_handler() is allowing to pass callback without event) 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 20 | --EXPECTF-- 21 | Fatal error: Uncaught ArgumentCountError: Wrong parameter count for ibase_set_event_handler() in %a 22 | -------------------------------------------------------------------------------- /tests/ibase_errmsg_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_errmsg(): Basic test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 19 | --EXPECTF-- 20 | Warning: ibase_query(): Dynamic SQL Error SQL error code = -104 %s on line %d 21 | string(%d) "Dynamic SQL Error SQL error code = -104 %s" 22 | bool(false) 23 | -------------------------------------------------------------------------------- /tests/bug46247_004.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Bug #46247 (ibase_set_event_handler() is allowing to pass callback without event) 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 20 | --EXPECTF-- 21 | Fatal error: Uncaught TypeError: ibase_set_event_handler(): supplied argument is not a valid InterBase link resource in %a 22 | -------------------------------------------------------------------------------- /tests/ibase_num_fields_002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_num_fields(): Make sure passing an integer or zero args to the function emits a warning 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 15 | --EXPECTF-- 16 | Warning: ibase_num_fields() expects parameter 1 to be resource, int given in %s on line %d 17 | NULL 18 | 19 | Warning: ibase_num_fields() expects exactly 1 parameter, 0 given in %s on line %d 20 | NULL 21 | -------------------------------------------------------------------------------- /tests/ibase_trans_003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_trans(): Check order of link identifier and trans args 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 19 | --EXPECT-- 20 | Could not insert 21 | -------------------------------------------------------------------------------- /tests/ibase_field_info_005.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_field_info(): fields introduced in FB 4.0, with older client 3 | --SKIPIF-- 4 | 9 | --FILE-- 10 | 18 | --EXPECT-- 19 | ID/INTEGER/4 20 | NUMERIC_4/VARCHAR/188 21 | DECIMAL_4/VARCHAR/188 22 | DECFLOAT_16/VARCHAR/92 23 | DECFLOAT_34/VARCHAR/168 24 | INT128_FIELD/VARCHAR/188 25 | TIME_TZ/TIME/4 26 | TIMESTAMP_TZ/TIMESTAMP/8 27 | -------------------------------------------------------------------------------- /tests/bug46247_002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Bug #46247 (ibase_set_event_handler() is allowing to pass callback without event) 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 16 | --EXPECTF-- 17 | Warning: ibase_set_event_handler(): Callback argument foo is not a callable function in %s on line %d 18 | 19 | Warning: ibase_set_event_handler(): Callback argument foo is not a callable function in %s on line %d 20 | -------------------------------------------------------------------------------- /tests/ibase_close_002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_close(): Make sure passing a string to the function emits a warning 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 20 | --EXPECTF-- 21 | bool(true) 22 | bool(true) 23 | bool(true) 24 | 25 | Warning: ibase_close() expects parameter 1 to be resource, string given in %s on line %d 26 | NULL 27 | -------------------------------------------------------------------------------- /tests/ibase_close_004.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_close(): Basic test 3 | --SKIPIF-- 4 | 12 | --FILE-- 13 | 25 | --EXPECT-- 26 | bool(true) 27 | Fatal error: Uncaught TypeError: ibase_close(): supplied resource is not a valid Firebird/InterBase link resource 28 | -------------------------------------------------------------------------------- /tests/ibase_num_params_003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_num_params(): Basic test 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 22 | --EXPECTF-- 23 | int(2) 24 | 25 | Fatal error: Uncaught ArgumentCountError: ibase_num_params() expects exactly 1 argument, 0 given in %a -------------------------------------------------------------------------------- /tests/ibase_trans_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_trans(): Basic test 3 | --SKIPIF-- 4 | 13 | --FILE-- 14 | 25 | --EXPECTF-- 26 | resource(%d) of type (Firebird/InterBase transaction) 27 | resource(%d) of type (Firebird/InterBase transaction) 28 | bool(true) 29 | bool(true) 30 | -------------------------------------------------------------------------------- /tests/ibase_drop_db_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_drop_db(): Basic test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 21 | --EXPECTF-- 22 | resource(%d) of type (Firebird/InterBase link) 23 | bool(true) 24 | -------------------------------------------------------------------------------- /tests/time_003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | IBASE_UNIXTIME: ignore IBASE_UNIXTIME flag for TIME fields 3 | --SKIPIF-- 4 | 11 | --FILE-- 12 | 20 | --EXPECTF-- 21 | array(3) { 22 | ["ID"]=> 23 | int(1) 24 | ["T1"]=> 25 | string(8) "15:45:59" 26 | ["T2"]=> 27 | string(19) "2025-11-06 15:45:59" 28 | } 29 | array(3) { 30 | ["ID"]=> 31 | int(1) 32 | ["T1"]=> 33 | string(8) "15:45:59" 34 | ["T2"]=> 35 | int(1762436759) 36 | } -------------------------------------------------------------------------------- /tests/ibase_trans_011.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_trans(): transaction control with SQL - commit explicitly 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 18 | --EXPECTF-- 19 | resource(%d) of type (Firebird/InterBase transaction) 20 | bool(true) 21 | 22 | Warning: ibase_query(): Dynamic SQL Error SQL error code = -901 invalid transaction handle (expecting explicit transaction start)%s 23 | bool(false) 24 | -------------------------------------------------------------------------------- /tests/ibase_trans_015.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_trans(): Basic test 3 | --SKIPIF-- 4 | 14 | --FILE-- 15 | 23 | --EXPECTF-- 24 | resource(%d) of type (Firebird/InterBase transaction) 25 | resource(%d) of type (Firebird/InterBase transaction) 26 | bool(true) 27 | 28 | Warning: ibase_close(): supplied resource is not a valid Firebird/InterBase link resource%s 29 | bool(false) 30 | -------------------------------------------------------------------------------- /tests/time_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | IBASE_UNIXTIME: return negative unix timestamp (old behaviour) for TIME fields 3 | --SKIPIF-- 4 | 11 | --FILE-- 12 | 20 | --EXPECTF-- 21 | array(3) { 22 | ["ID"]=> 23 | int(1) 24 | ["T1"]=> 25 | string(8) "15:45:59" 26 | ["T2"]=> 27 | string(19) "2025-11-06 15:45:59" 28 | } 29 | array(3) { 30 | ["ID"]=> 31 | int(1) 32 | ["T1"]=> 33 | int(-%d) 34 | ["T2"]=> 35 | int(1762436759) 36 | } -------------------------------------------------------------------------------- /.github/actions/install-linux/action.yml: -------------------------------------------------------------------------------- 1 | name: Install 2 | runs: 3 | using: composite 4 | steps: 5 | - shell: bash 6 | run: | 7 | export DEBIAN_FRONTEND="noninteractive" 8 | sudo apt-get update 9 | sudo apt-get -y -q install firebird-dev firebird3.0 firebird3.0-common firebird3.0-server 10 | FB_ORIGINAL_PASS=`sudo cat /etc/firebird/3.0/SYSDBA.password | grep ISC_PASSWORD | sed -e 's/ISC_PASSWORD="\([^"]*\)".*/\1/'` 11 | gsec -user SYSDBA -password ${FB_ORIGINAL_PASS} -modify sysdba -pw masterkey 12 | sudo service firebird3.0 restart 13 | sudo apt-get -y install pkg-config build-essential autoconf bison re2c libxml2-dev libsqlite3-dev 14 | -------------------------------------------------------------------------------- /tests/bug45575.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Bug #45575 (Segfault with invalid non-string as event handler callback) 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 19 | --EXPECTF-- 20 | Warning: ibase_set_event_handler(): Callback argument is not a callable function in %s on line %d 21 | 22 | Warning: ibase_set_event_handler(): Callback argument 1 is not a callable function in %s on line %d 23 | -------------------------------------------------------------------------------- /tests/002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | InterBase: connect, close and pconnect 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 27 | --EXPECT-- 28 | --- test1 --- 29 | 1 test table not created with isql 30 | --- 31 | --- test1 --- 32 | 1 test table not created with isql 33 | --- 34 | -------------------------------------------------------------------------------- /tests/ibase_close_003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_close(): Make sure passing a string to the function throws an error. 3 | --SKIPIF-- 4 | 12 | --FILE-- 13 | 24 | --EXPECTF-- 25 | bool(true) 26 | bool(true) 27 | bool(true) 28 | 29 | Fatal error: Uncaught TypeError: ibase_close(): Argument #1 ($link_identifier) must be of type resource, string given in %a 30 | -------------------------------------------------------------------------------- /tests/ibase_trans_002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_trans(): Basic operations 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 27 | --EXPECT-- 28 | int(1) 29 | array(2) { 30 | ["I"]=> 31 | int(100) 32 | ["C"]=> 33 | string(3) "100" 34 | } 35 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | // vim:ft=javascript 2 | 3 | ARG_WITH("interbase", "InterBase support", "no"); 4 | 5 | if (PHP_INTERBASE != "no") { 6 | if ( 7 | CHECK_HEADER_ADD_INCLUDE("ibase.h", "CFLAGS_INTERBASE", PHP_INTERBASE + "\\include") && ( 8 | CHECK_LIB("fbclient_ms.lib", "interbase", PHP_INTERBASE + "\\lib") || 9 | CHECK_LIB("gds32_ms.lib", "interbase", PHP_INTERBASE + "\\lib") 10 | ) 11 | ) { 12 | EXTENSION("interbase", "interbase.c ibase_query.c ibase_service.c ibase_events.c ibase_blobs.c firebird_utils.cpp", PHP_INTERBASE_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); 13 | AC_DEFINE('HAVE_IBASE', 1, 'Have interbase library'); 14 | } else { 15 | WARNING("interbase not enabled; libraries and headers not found"); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/ibase_trans_014.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_trans(): Basic test 3 | --SKIPIF-- 4 | 14 | --FILE-- 15 | 25 | --EXPECTF-- 26 | resource(%d) of type (Firebird/InterBase transaction) 27 | resource(%d) of type (Firebird/InterBase transaction) 28 | bool(true) 29 | Fatal error: Uncaught TypeError: ibase_close(): supplied resource is not a valid Firebird/InterBase link resource 30 | -------------------------------------------------------------------------------- /tests/ibase_close_005.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_close(): Make sure passing a string to the function throws an error. 3 | --SKIPIF-- 4 | 13 | --FILE-- 14 | 27 | --EXPECT-- 28 | bool(true) 29 | Fatal error: Uncaught TypeError: ibase_close(): supplied resource is not a valid Firebird/InterBase link resource 30 | -------------------------------------------------------------------------------- /tests/ibase_free_query_002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_free_query(): Basic test 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 24 | --EXPECTF-- 25 | bool(true) 26 | 27 | Fatal error: Uncaught TypeError: ibase_free_query(): supplied resource is not a valid Firebird/InterBase query resource in %a -------------------------------------------------------------------------------- /tests/time_002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | IBASE_UNIXTIME: return negative unix timestamp (old behaviour) for TIME_TZ fields 3 | --SKIPIF-- 4 | 11 | --FILE-- 12 | 20 | --EXPECTF-- 21 | array(3) { 22 | ["ID"]=> 23 | int(1) 24 | ["T1"]=> 25 | string(20) "15:45:59 Europe/Riga" 26 | ["T2"]=> 27 | string(31) "2025-11-06 15:45:59 Europe/Riga" 28 | } 29 | array(3) { 30 | ["ID"]=> 31 | int(1) 32 | ["T1"]=> 33 | int(-%d) 34 | ["T2"]=> 35 | int(1762436759) 36 | } -------------------------------------------------------------------------------- /tests/time_004.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | IBASE_UNIXTIME: ignore IBASE_UNIXTIME flag for TIME_TZ fields 3 | --SKIPIF-- 4 | 11 | --FILE-- 12 | 20 | --EXPECTF-- 21 | array(3) { 22 | ["ID"]=> 23 | int(1) 24 | ["T1"]=> 25 | string(20) "15:45:59 Europe/Riga" 26 | ["T2"]=> 27 | string(31) "2025-11-06 15:45:59 Europe/Riga" 28 | } 29 | array(3) { 30 | ["ID"]=> 31 | int(1) 32 | ["T1"]=> 33 | string(20) "15:45:59 Europe/Riga" 34 | ["T2"]=> 35 | int(1762436759) 36 | } -------------------------------------------------------------------------------- /tests/ibase_num_combi_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_num_fields() / ibase_num_params(): combined 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | 28 | --EXPECT-- 29 | int(0) 30 | int(2) 31 | -------- 32 | int(2) 33 | int(2) 34 | -------------------------------------------------------------------------------- /tests/ibase_num_params_004.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_num_params(): Basic test 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 22 | --EXPECTF-- 23 | int(2) 24 | 25 | Warning: ibase_prepare(): Dynamic SQL Error SQL error code = -%d Column unknown X At line %d, column %d %s 26 | 27 | Fatal error: Uncaught TypeError: ibase_num_params(): Argument #1 ($query) must be of type resource, %a -------------------------------------------------------------------------------- /tests/001-FIELDS40.sql: -------------------------------------------------------------------------------- 1 | RECREATE TABLE FIELDS40 ( 2 | ID INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 3 | -- 3.141592653589793238462643383279502884197169399375105820974944592307816406286 4 | NUMERIC_4 NUMERIC(38, 37) DEFAULT 3.1415926535897932384626433832795028841, 5 | DECIMAL_4 DECIMAL(38, 37) DEFAULT 3.1415926535897932384626433832795028841, 6 | DECFLOAT_16 DECFLOAT(16) DEFAULT 3.141592653589793, 7 | DECFLOAT_34 DECFLOAT(34) DEFAULT 3.141592653589793238462643383279502, 8 | INT128_FIELD INT128 DEFAULT -170141183460469231731687303715884105727, 9 | TIME_TZ TIME WITH TIME ZONE DEFAULT '15:45:59 Europe/Berlin', 10 | TIMESTAMP_TZ TIMESTAMP WITH TIME ZONE DEFAULT '2025-11-06 15:45:59 Europe/Berlin' 11 | ); 12 | -------------------------------------------------------------------------------- /tests/ibase_field_info_002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_field_info(): fields introduced in FB 3.0 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 28 | --EXPECT-- 29 | ID/INTEGER/4 30 | BOOL_FIELD/BOOLEAN/1 31 | -------------------------------------------------------------------------------- /tests/ibase_affected_rows_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_affected_rows(): Basic test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 27 | --EXPECTF-- 28 | int(3) 29 | int(0) 30 | 31 | Warning: ibase_query(): Dynamic SQL Error SQL error code = -104 %s on line %d 32 | int(0) 33 | -------------------------------------------------------------------------------- /tests/ibase_trans_008.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_trans(): transaction control with SQL 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 27 | --EXPECT-- 28 | array(2) { 29 | ["I"]=> 30 | int(1) 31 | ["C"]=> 32 | string(8) "test1(1)" 33 | } -------------------------------------------------------------------------------- /tests/issue35_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Issue #35: ibase_prepare() fails to find table with SQL that has double quotes on table identifiers 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | 28 | --EXPECTF-- 29 | object(stdClass)#1 (2) { 30 | ["ID"]=> 31 | int(1) 32 | ["CLIENT_NAME"]=> 33 | string(9) "Some name" 34 | } -------------------------------------------------------------------------------- /tests/ibase_trans_004.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_trans(): handles 3 | --SKIPIF-- 4 | 10 | --FILE-- 11 | 25 | --EXPECTF-- 26 | resource(%d) of type (Firebird/InterBase transaction) 27 | bool(true) 28 | resource(%d) of type (Firebird/InterBase transaction) 29 | 30 | Warning: ibase_query(): invalid transaction handle (expecting explicit transaction start)%s 31 | bool(false) 32 | -------------------------------------------------------------------------------- /tests/ibase_trans_006.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_trans(): handles 3 | --SKIPIF-- 4 | 10 | --FILE-- 11 | 24 | --EXPECTF-- 25 | resource(%d) of type (Firebird/InterBase transaction) 26 | bool(true) 27 | resource(%d) of type (Firebird/InterBase transaction) 28 | 29 | Warning: ibase_query(): invalid transaction handle (expecting explicit transaction start)%s 30 | bool(false) 31 | -------------------------------------------------------------------------------- /tests/ibase_param_info_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_param_info(): Basic test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 20 | --EXPECTF-- 21 | array(10) { 22 | [0]=> 23 | string(0) "" 24 | ["name"]=> 25 | string(0) "" 26 | [1]=> 27 | string(0) "" 28 | ["alias"]=> 29 | string(0) "" 30 | [2]=> 31 | string(0) "" 32 | ["relation"]=> 33 | string(0) "" 34 | [3]=> 35 | string(1) "4" 36 | ["length"]=> 37 | string(1) "4" 38 | [4]=> 39 | string(7) "INTEGER" 40 | ["type"]=> 41 | string(7) "INTEGER" 42 | } 43 | --- 44 | bool(false) 45 | -------------------------------------------------------------------------------- /tests/ibase_free_query_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_free_query(): Basic test 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 24 | --EXPECTF-- 25 | bool(true) 26 | 27 | Warning: ibase_free_query(): supplied resource is not a valid Firebird/InterBase query resource in %s on line %d 28 | bool(false) 29 | 30 | Warning: ibase_free_query(): supplied resource is not a valid Firebird/InterBase query resource in %s on line %d 31 | bool(false) 32 | -------------------------------------------------------------------------------- /tests/ibase_drop_db_004.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_drop_db(): Make sure passing null to the function throws an error. 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 25 | --EXPECTF-- 26 | resource(%d) of type (Firebird/InterBase link) 27 | bool(true) 28 | 29 | Fatal error: Uncaught TypeError: ibase_drop_db(): Argument #1 ($link_identifier) must be of type resource, null given in %a 30 | -------------------------------------------------------------------------------- /tests/fb40fields_002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test data for fields introduced in FB 4.0, with older client 3 | --SKIPIF-- 4 | 9 | --FILE-- 10 | 18 | --EXPECT-- 19 | array(8) { 20 | ["ID"]=> 21 | int(1) 22 | ["NUMERIC_4"]=> 23 | string(39) "3.1415926535897932384626433832795028841" 24 | ["DECIMAL_4"]=> 25 | string(39) "3.1415926535897932384626433832795028841" 26 | ["DECFLOAT_16"]=> 27 | string(17) "3.141592653589793" 28 | ["DECFLOAT_34"]=> 29 | string(35) "3.141592653589793238462643383279502" 30 | ["INT128_FIELD"]=> 31 | string(40) "-170141183460469231731687303715884105727" 32 | ["TIME_TZ"]=> 33 | string(8) "16:45:59" 34 | ["TIMESTAMP_TZ"]=> 35 | string(19) "2025-11-06 16:45:59" 36 | } 37 | -------------------------------------------------------------------------------- /tests/ibase_drop_db_003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_drop_db(): Make sure passing an integer to the function throws an error. 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 25 | --EXPECTF-- 26 | resource(%d) of type (Firebird/InterBase link) 27 | bool(true) 28 | 29 | Fatal error: Uncaught TypeError: ibase_drop_db(): Argument #1 ($link_identifier) must be of type resource, int given in %a 30 | -------------------------------------------------------------------------------- /tests/fb40fields_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test data for fields introduced in FB 4.0 3 | --SKIPIF-- 4 | 9 | --FILE-- 10 | 18 | --EXPECT-- 19 | array(8) { 20 | ["ID"]=> 21 | int(1) 22 | ["NUMERIC_4"]=> 23 | string(39) "3.1415926535897932384626433832795028841" 24 | ["DECIMAL_4"]=> 25 | string(39) "3.1415926535897932384626433832795028841" 26 | ["DECFLOAT_16"]=> 27 | string(17) "3.141592653589793" 28 | ["DECFLOAT_34"]=> 29 | string(35) "3.141592653589793238462643383279502" 30 | ["INT128_FIELD"]=> 31 | string(40) "-170141183460469231731687303715884105727" 32 | ["TIME_TZ"]=> 33 | string(22) "15:45:59 Europe/Berlin" 34 | ["TIMESTAMP_TZ"]=> 35 | string(33) "2025-11-06 15:45:59 Europe/Berlin" 36 | } 37 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 14 | thanks_dev: # Replace with a single thanks.dev username 15 | custom: https://firebirdsql.org/en/donate/ 16 | -------------------------------------------------------------------------------- /tests/use_after_free-002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | InterBase: use after ibase_free_query() 3 | --SKIPIF-- 4 | 9 | --FILE-- 10 | 26 | --EXPECT-- 27 | ---- Batch 1 ---- 28 | Fatal error: Uncaught TypeError: ibase_fetch_assoc(): supplied resource is not a valid Firebird/InterBase query resource 29 | -------------------------------------------------------------------------------- /tests/use_after_free-001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | InterBase: use after ibase_free_query() 3 | --SKIPIF-- 4 | = 61)) print "Skip IBASE_VER < 6.1"; 7 | ?> 8 | --FILE-- 9 | 21 | --EXPECT-- 22 | ---- Batch 1 ---- 23 | array(2) { 24 | ["I"]=> 25 | int(5) 26 | ["C"]=> 27 | string(15) "ROW 1 (batch 5)" 28 | } 29 | array(2) { 30 | ["I"]=> 31 | int(5) 32 | ["C"]=> 33 | string(15) "ROW 2 (batch 5)" 34 | } 35 | ---- Batch 2 ---- 36 | ---- Batch 3 ---- 37 | ---- Batch 4 ---- 38 | ---- Batch 5 ---- -------------------------------------------------------------------------------- /tests/ibase_num_params_002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_num_params(): Basic test 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 26 | --EXPECTF-- 27 | int(2) 28 | 29 | Warning: ibase_num_params() expects exactly 1 parameter, 0 given in %s on line %d 30 | NULL 31 | 32 | Warning: ibase_prepare(): Dynamic SQL Error SQL error code = -206 %s in %s on line %d 33 | 34 | Warning: ibase_num_params() expects parameter 1 to be resource, bool given in %s on line %d 35 | NULL 36 | -------------------------------------------------------------------------------- /tests/ibase_trans_012.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_trans(): handles 3 | --SKIPIF-- 4 | 11 | --FILE-- 12 | 27 | --EXPECTF-- 28 | resource(%d) of type (Firebird/InterBase transaction) 29 | bool(true) 30 | resource(%d) of type (Firebird/InterBase transaction) 31 | resource(%d) of type (interbase %s) 32 | 33 | Warning: ibase_fetch_assoc(): Dynamic SQL Error SQL error code = -901 invalid transaction handle (expecting explicit transaction start)%s 34 | bool(false) 35 | -------------------------------------------------------------------------------- /tests/ibase_drop_db_002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_drop_db(): Make sure passing an integer or null to the function emits a warning 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 25 | --EXPECTF-- 26 | resource(%d) of type (Firebird/InterBase link) 27 | bool(true) 28 | 29 | Warning: ibase_drop_db() expects parameter 1 to be resource, int given in %s on line %d 30 | NULL 31 | 32 | Warning: ibase_drop_db() expects parameter 1 to be resource, null given in %s on line %d 33 | NULL 34 | -------------------------------------------------------------------------------- /tests/ibase_rollback_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_rollback(): Basic test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 28 | --EXPECTF-- 29 | array(1) { 30 | [0]=> 31 | int(3) 32 | } 33 | bool(true) 34 | array(1) { 35 | [0]=> 36 | int(0) 37 | } 38 | bool(true) 39 | 40 | Warning: ibase_rollback(): invalid transaction handle (expecting explicit transaction start) in %s on line %d 41 | bool(false) 42 | -------------------------------------------------------------------------------- /tests/ibase_trans_013.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_trans(): handles 3 | --SKIPIF-- 4 | 11 | --FILE-- 12 | 26 | --EXPECTF-- 27 | resource(%d) of type (Firebird/InterBase transaction) 28 | bool(true) 29 | resource(%d) of type (Firebird/InterBase transaction) 30 | resource(%d) of type (interbase %s) 31 | 32 | Warning: ibase_fetch_assoc(): Dynamic SQL Error SQL error code = -901 invalid transaction handle (expecting explicit transaction start) %s 33 | bool(false) 34 | -------------------------------------------------------------------------------- /tests/ibase_field_info_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_field_info(): basic fields 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | 20 | --EXPECT-- 21 | ID/INTEGER/4 22 | SMALLINT_1/SMALLINT/2 23 | INTEGER_FIELD/INTEGER/4 24 | BIGINT_FIELD/BIGINT/8 25 | NUMERIC_1/NUMERIC(4,4)/2 26 | DECIMAL_1/NUMERIC(9,4)/4 27 | NUMERIC_2/NUMERIC(9,8)/4 28 | DECIMAL_2/NUMERIC(9,8)/4 29 | NUMERIC_3/NUMERIC(18,16)/8 30 | DECIMAL_3/NUMERIC(18,16)/8 31 | FLOAT_FIELD/FLOAT/4 32 | DOUBLE_FIELD/DOUBLE PRECISION/8 33 | CHAR_FIXED/VARCHAR/10 34 | VARCHAR_FIELD/VARCHAR/50 35 | CHAR_UTF8/VARCHAR/40 36 | VARCHAR_UTF8/VARCHAR/200 37 | DATE_FIELD/DATE/4 38 | TIME_FIELD/TIME/4 39 | TIMESTAMP_FIELD/TIMESTAMP/8 40 | BINARY_FIXED/VARCHAR/16 41 | VARBINARY_FIELD/VARCHAR/100 42 | BLOB_TEXT/BLOB/8 43 | BLOB_BINARY/BLOB/8 -------------------------------------------------------------------------------- /tests/ibase_field_info_004.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_field_info(): basic fields - UTF8 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | 20 | --EXPECT-- 21 | ID/INTEGER/4 22 | SMALLINT_1/SMALLINT/2 23 | INTEGER_FIELD/INTEGER/4 24 | BIGINT_FIELD/BIGINT/8 25 | NUMERIC_1/NUMERIC(4,4)/2 26 | DECIMAL_1/NUMERIC(9,4)/4 27 | NUMERIC_2/NUMERIC(9,8)/4 28 | DECIMAL_2/NUMERIC(9,8)/4 29 | NUMERIC_3/NUMERIC(18,16)/8 30 | DECIMAL_3/NUMERIC(18,16)/8 31 | FLOAT_FIELD/FLOAT/4 32 | DOUBLE_FIELD/DOUBLE PRECISION/8 33 | CHAR_FIXED/VARCHAR/40 34 | VARCHAR_FIELD/VARCHAR/200 35 | CHAR_UTF8/VARCHAR/40 36 | VARCHAR_UTF8/VARCHAR/200 37 | DATE_FIELD/DATE/4 38 | TIME_FIELD/TIME/4 39 | TIMESTAMP_FIELD/TIMESTAMP/8 40 | BINARY_FIXED/VARCHAR/16 41 | VARBINARY_FIELD/VARCHAR/100 42 | BLOB_TEXT/BLOB/8 43 | BLOB_BINARY/BLOB/8 -------------------------------------------------------------------------------- /tests/ibase_field_info_003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_field_info(): fields introduced in FB 4.0 3 | --SKIPIF-- 4 | 9 | --FILE-- 10 | 29 | --EXPECT-- 30 | ID/INTEGER/4 31 | NUMERIC_4/VARCHAR/188 32 | DECIMAL_4/VARCHAR/188 33 | DECFLOAT_16/VARCHAR/92 34 | DECFLOAT_34/VARCHAR/168 35 | INT128_FIELD/VARCHAR/188 36 | TIME_TZ/TIME WITH TIME ZONE/8 37 | TIMESTAMP_TZ/TIMESTAMP WITH TIME ZONE/12 38 | -------------------------------------------------------------------------------- /tests/001-table.sql: -------------------------------------------------------------------------------- 1 | RECREATE TABLE TEST_001 2 | ( 3 | ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL PRIMARY KEY, 4 | BLOB_0 BLOB DEFAULT 'BLOB_0', 5 | BLOB_1 BLOB SUB_TYPE 1 DEFAULT 'BLOB_1', 6 | BOOL_1 BOOLEAN DEFAULT TRUE, 7 | DATE_1 DATE DEFAULT '2025-11-06', 8 | TIME_1 TIME WITHOUT TIME ZONE DEFAULT '15:45:59', 9 | DECFLOAT_16 DECFLOAT(16) DEFAULT 3.141592653589793, -- MAX 9.999999999999999E+384 10 | DECFLOAT_34 DECFLOAT(34) DEFAULT 3.141592653589793238462643383279502, -- MAX 9.999999999999999999999999999999999E+6144 11 | INT_NOT_NULL INTEGER DEFAULT 1 NOT NULL, 12 | DOUBLE_PRECISION_1 DOUBLE PRECISION DEFAULT 3.141592653589793, 13 | FLOAT_1 FLOAT DEFAULT 3.141592653589793, 14 | INT_1 INTEGER DEFAULT 1, 15 | INT_128 INT128 DEFAULT 170141183460469231731687303715884105727, 16 | VARCHAR_1 VARCHAR(100) DEFAULT 'VARCHAR_1', 17 | SMALLINT_1 SMALLINT DEFAULT 1, 18 | TIME_TZ TIME WITH TIME ZONE DEFAULT '15:45:59 Europe/Riga', 19 | TIMESTAMP_TZ TIMESTAMP WITH TIME ZONE DEFAULT '2025-11-06 15:45:59 Europe/Riga' 20 | ); 21 | -------------------------------------------------------------------------------- /tests/bug46543.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Bug #46543 (ibase_trans() memory leaks when using wrong parameters) 3 | --SKIPIF-- 4 | 10 | --FILE-- 11 | 26 | --EXPECTF-- 27 | Warning: ibase_trans(): supplied resource is not a valid Firebird/InterBase link resource in %sbug46543.php on line %d 28 | 29 | Warning: ibase_trans(): supplied resource is not a valid Firebird/InterBase link resource in %sbug46543.php on line %d 30 | 31 | Warning: ibase_trans(): supplied resource is not a valid Firebird/InterBase link resource in %sbug46543.php on line %d 32 | 33 | Warning: ibase_trans(): supplied resource is not a valid Firebird/InterBase link resource in %sbug46543.php on line %d 34 | -------------------------------------------------------------------------------- /tests/datatype_char_utf8.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check for data type CHAR(1) and UTF8 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | 33 | --EXPECTF-- 34 | array(3) { 35 | ["V_CHAR_UTF8_1"]=> 36 | string(3) "€" 37 | ["V_CHAR_UTF8_10"]=> 38 | string(12) " A € " 39 | ["V_VARCHAR_UTF8_1"]=> 40 | string(3) "€" 41 | } 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .deps 3 | .libs 4 | .php-version 5 | .rbenv-version 6 | acinclude.m4 7 | aclocal.m4 8 | autom4te.cache 9 | build 10 | config.guess 11 | config.h 12 | config.h.in 13 | config.log 14 | config.nice 15 | config.status 16 | config.sub 17 | configure 18 | configure.in 19 | configure.ac 20 | extras 21 | include 22 | install-sh 23 | libtool 24 | ltmain.sh 25 | ltmain.sh.backup 26 | Makefile 27 | Makefile.fragments 28 | Makefile.global 29 | Makefile.objects 30 | missing 31 | mkinstalldirs 32 | modules 33 | run-tests.php 34 | tmp-php.ini 35 | yaml.loT 36 | 37 | # General Ignores 38 | *~ 39 | .#* 40 | *. 41 | *.slo 42 | *.mk 43 | *.mem 44 | *.gcda 45 | *.gcno 46 | *.la 47 | *.lo 48 | *.o 49 | *.a 50 | *.ncb 51 | *.opt 52 | *.plg 53 | *swp 54 | *.patch 55 | *.tgz 56 | *.tar.gz 57 | *.tar.bz2 58 | .FBCIndex 59 | .FBCLockFolder 60 | core 61 | *.dep 62 | 63 | # Test specific Ignores 64 | tests/*.diff 65 | tests/*.exp 66 | tests/*.log 67 | tests/*.out 68 | tests/*.php 69 | tests/*.sh 70 | 71 | # coverage 72 | /coverage.info 73 | /reports 74 | win_build_scripts 75 | .vscode-win 76 | build_scripts/php-fb-config.bat 77 | -------------------------------------------------------------------------------- /tests/008.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | InterBase: event handling 3 | --SKIPIF-- 4 | 9 | --FILE-- 10 | 5) echo "FAIL ($count)\n"; 45 | echo "end of test\n"; 46 | 47 | ?> 48 | --EXPECT-- 49 | end of test 50 | -------------------------------------------------------------------------------- /tests/ibase_trans_007.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_trans(): handles 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 29 | --EXPECT-- 30 | int(1) 31 | |---- TEST1 default transaction 32 | array(2) { 33 | ["I"]=> 34 | int(1) 35 | ["C"]=> 36 | string(32) "test table not created with isql" 37 | } 38 | |-------- 39 | |---- TEST1 t1 transaction 40 | |-------- 41 | |---- TEST1 default transaction 42 | array(2) { 43 | ["I"]=> 44 | int(1) 45 | ["C"]=> 46 | string(32) "test table not created with isql" 47 | } 48 | |-------- 49 | -------------------------------------------------------------------------------- /tests/issue77_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Issue #77: Order of INSERT parameters cause "Incorrect values within SQLDA structure" 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | '1', 20 | 'F8'=>'', 21 | ]); 22 | ibase_query("INSERT INTO TTEST($fields_str) VALUES ($q_str)", ...array_values($data)); 23 | dump_table_rows("TTEST"); 24 | 25 | [$fields_str, $q_str, $data] = array2sql_parts([ 26 | 'F8'=>'', 27 | 'TTYPE'=>'2', 28 | ]); 29 | ibase_query("INSERT INTO TTEST($fields_str) VALUES ($q_str)", ...array_values($data)); 30 | dump_table_rows("TTEST"); 31 | } 32 | 33 | test77(); 34 | 35 | ?> 36 | --EXPECT-- 37 | array(2) { 38 | ["TTYPE"]=> 39 | string(1) "1" 40 | ["F8"]=> 41 | NULL 42 | } 43 | array(2) { 44 | ["TTYPE"]=> 45 | string(1) "1" 46 | ["F8"]=> 47 | NULL 48 | } 49 | array(2) { 50 | ["TTYPE"]=> 51 | string(1) "2" 52 | ["F8"]=> 53 | NULL 54 | } -------------------------------------------------------------------------------- /tests/returning_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | INSERT / UPDATE using RETURNING 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | 28 | --EXPECT-- 29 | array(2) { 30 | ["I"]=> 31 | int(1) 32 | ["C"]=> 33 | string(6) "data 1" 34 | } 35 | array(2) { 36 | ["I"]=> 37 | int(1) 38 | ["C"]=> 39 | string(6) "data 1" 40 | } 41 | -------- 42 | array(4) { 43 | ["OLD_I"]=> 44 | int(1) 45 | ["OLD_C"]=> 46 | string(6) "data 1" 47 | ["NEW_I"]=> 48 | int(2) 49 | ["NEW_C"]=> 50 | string(14) "data 1 updated" 51 | } 52 | array(2) { 53 | ["I"]=> 54 | int(2) 55 | ["C"]=> 56 | string(14) "data 1 updated" 57 | } -------------------------------------------------------------------------------- /tests/ibase_trans_009.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_trans(): transaction control with SQL 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 34 | --EXPECT-- 35 | ---- current status 36 | array(2) { 37 | ["I"]=> 38 | int(1) 39 | ["C"]=> 40 | string(8) "test2(1)" 41 | } 42 | array(2) { 43 | ["I"]=> 44 | int(2) 45 | ["C"]=> 46 | string(8) "test2(2)" 47 | } 48 | ---- now rollback 49 | array(2) { 50 | ["I"]=> 51 | int(1) 52 | ["C"]=> 53 | string(8) "test2(1)" 54 | } -------------------------------------------------------------------------------- /tests/bug46247_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Bug #46247 (ibase_set_event_handler() is allowing to pass callback without event) 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 29 | --EXPECTF-- 30 | Warning: Wrong parameter count for ibase_set_event_handler() in %s on line %d 31 | 32 | Warning: ibase_set_event_handler(): supplied argument is not a valid InterBase link resource in %s on line %d 33 | 34 | Warning: ibase_set_event_handler(): Callback argument foo is not a callable function in %s on line %d 35 | 36 | Warning: ibase_set_event_handler(): Callback argument foo is not a callable function in %s on line %d 37 | 38 | Warning: ibase_set_event_handler(): supplied argument is not a valid InterBase link resource in %s on line %d 39 | -------------------------------------------------------------------------------- /tests/bug45373.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Bug #45373 (php crash on query with errors in params) 3 | --SKIPIF-- 4 | 9 | --FILE-- 10 | 32 | --EXPECTF-- 33 | array(2) { 34 | ["I"]=> 35 | int(1) 36 | ["C"]=> 37 | string(32) "test table not created with isql" 38 | } 39 | 40 | Notice: ibase_execute(): Statement expects 2 arguments, 3 given in %s on line %d 41 | array(2) { 42 | ["I"]=> 43 | int(1) 44 | ["C"]=> 45 | string(32) "test table not created with isql" 46 | } 47 | 48 | Warning: ibase_execute(): Statement expects 2 arguments, 1 given in %s on line %d 49 | -------------------------------------------------------------------------------- /tests/datatype_int128.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check for data type INT128 (Firebird 4.0 or above) 3 | --SKIPIF-- 4 | = 4.0. Perhaps runtime 7 | // client lib checking also needed. 8 | skip_if_fb_lt(4.0); 9 | ?> 10 | --FILE-- 11 | 37 | --EXPECTF-- 38 | array(1) { 39 | ["V_INT128"]=> 40 | string(4) "1234" 41 | } 42 | array(1) { 43 | ["V_INT128"]=> 44 | string(40) "-170141183460469231731687303715884105728" 45 | } 46 | array(1) { 47 | ["V_INT128"]=> 48 | string(39) "170141183460469231731687303715884105727" 49 | } 50 | -------------------------------------------------------------------------------- /tests/ibase_param_info_004.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_param_info(): Basic test with ibase_query() 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 22 | --EXPECTF-- 23 | int(2) 24 | array(10) { 25 | [0]=> 26 | string(1) "I" 27 | ["name"]=> 28 | string(1) "I" 29 | [1]=> 30 | string(1) "I" 31 | ["alias"]=> 32 | string(1) "I" 33 | [2]=> 34 | string(5) "TEST1" 35 | ["relation"]=> 36 | string(5) "TEST1" 37 | [3]=> 38 | string(1) "4" 39 | ["length"]=> 40 | string(1) "4" 41 | [4]=> 42 | string(7) "INTEGER" 43 | ["type"]=> 44 | string(7) "INTEGER" 45 | } 46 | array(10) { 47 | [0]=> 48 | string(1) "C" 49 | ["name"]=> 50 | string(1) "C" 51 | [1]=> 52 | string(1) "C" 53 | ["alias"]=> 54 | string(1) "C" 55 | [2]=> 56 | string(5) "TEST1" 57 | ["relation"]=> 58 | string(5) "TEST1" 59 | [3]=> 60 | string(3) "100" 61 | ["length"]=> 62 | string(3) "100" 63 | [4]=> 64 | string(7) "VARCHAR" 65 | ["type"]=> 66 | string(7) "VARCHAR" 67 | } -------------------------------------------------------------------------------- /tests/ibase_name_result_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ibase_name_result(): basic test 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | 36 | --EXPECT-- 37 | ---- init ---- 38 | array(2) { 39 | ["I"]=> 40 | int(1) 41 | ["C"]=> 42 | string(4) "row1" 43 | } 44 | array(2) { 45 | ["I"]=> 46 | int(2) 47 | ["C"]=> 48 | string(4) "row2" 49 | } 50 | ---- after update ---- 51 | array(2) { 52 | ["I"]=> 53 | int(2) 54 | ["C"]=> 55 | string(6) "row1/2" 56 | } 57 | array(2) { 58 | ["I"]=> 59 | int(4) 60 | ["C"]=> 61 | string(6) "row2/4" 62 | } -------------------------------------------------------------------------------- /tests/long_names_002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Long names: Firebird 3.0 or older 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 52 | --EXPECT-- 53 | int(31) 54 | Table:TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT 55 | array(2) { 56 | ["FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"]=> 57 | int(1) 58 | ["🥰🥰🥰🥰🥰🥰🥰ppp"]=> 59 | int(2) 60 | } 61 | Table:😂😂😂😂😂😂😂 62 | array(2) { 63 | ["FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"]=> 64 | int(1) 65 | ["🥰🥰🥰🥰🥰🥰🥰ppp"]=> 66 | int(2) 67 | } -------------------------------------------------------------------------------- /tests/datatype_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check for data types using old clients 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 24 | --EXPECT-- 25 | array(17) { 26 | ["ID"]=> 27 | int(1) 28 | ["BLOB_0"]=> 29 | string(6) "BLOB_0" 30 | ["BLOB_1"]=> 31 | string(6) "BLOB_1" 32 | ["BOOL_1"]=> 33 | bool(true) 34 | ["DATE_1"]=> 35 | string(10) "2025-11-06" 36 | ["TIME_1"]=> 37 | string(8) "15:45:59" 38 | ["DECFLOAT_16"]=> 39 | string(17) "3.141592653589793" 40 | ["DECFLOAT_34"]=> 41 | string(35) "3.141592653589793238462643383279502" 42 | ["INT_NOT_NULL"]=> 43 | int(1) 44 | ["DOUBLE_PRECISION_1"]=> 45 | float(3.141592653589793) 46 | ["FLOAT_1"]=> 47 | float(3.1415927410125732) 48 | ["INT_1"]=> 49 | int(1) 50 | ["INT_128"]=> 51 | string(39) "170141183460469231731687303715884105727" 52 | ["VARCHAR_1"]=> 53 | string(9) "VARCHAR_1" 54 | ["SMALLINT_1"]=> 55 | int(1) 56 | ["TIME_TZ"]=> 57 | string(20) "15:45:59 Europe/Riga" 58 | ["TIMESTAMP_TZ"]=> 59 | string(31) "2025-11-06 15:45:59 Europe/Riga" 60 | } -------------------------------------------------------------------------------- /tests/001-FIELDS25.sql: -------------------------------------------------------------------------------- 1 | RECREATE TABLE FIELDS25 ( 2 | ID INTEGER NOT NULL PRIMARY KEY, 3 | 4 | -- 3.141592653589793238462643383279502 5 | -- Numeric types 6 | SMALLINT_1 SMALLINT DEFAULT -32768, 7 | INTEGER_FIELD INTEGER DEFAULT -2147483648, 8 | BIGINT_FIELD BIGINT DEFAULT -9223372036854775808, 9 | NUMERIC_1 NUMERIC(4, 4) DEFAULT 3.1415, 10 | DECIMAL_1 DECIMAL(4, 4) DEFAULT 3.1415, 11 | NUMERIC_2 NUMERIC(9, 8) DEFAULT 3.14159265, 12 | DECIMAL_2 DECIMAL(9, 8) DEFAULT 3.14159265, 13 | NUMERIC_3 NUMERIC(18, 16) DEFAULT 3.141592653589793, 14 | DECIMAL_3 DECIMAL(18, 16) DEFAULT 3.141592653589793, 15 | FLOAT_FIELD FLOAT DEFAULT 3.14, 16 | DOUBLE_FIELD DOUBLE PRECISION DEFAULT 3.14, 17 | 18 | -- Character types 19 | CHAR_FIXED CHAR(10) DEFAULT 'ABCDE', 20 | VARCHAR_FIELD VARCHAR(50) DEFAULT 'Default varchar text', 21 | CHAR_UTF8 CHAR(10) CHARACTER SET UTF8 DEFAULT 'テスト', 22 | VARCHAR_UTF8 VARCHAR(50) CHARACTER SET UTF8 DEFAULT 'Glāžšķūņu rūķīši', 23 | 24 | -- Date/Time types 25 | DATE_FIELD DATE DEFAULT '2025-11-10', 26 | TIME_FIELD TIME DEFAULT '15:16:59', 27 | TIMESTAMP_FIELD TIMESTAMP DEFAULT '2025-11-10 15:16:59', 28 | 29 | -- Binary types 30 | BINARY_FIXED CHAR(16) CHARACTER SET OCTETS DEFAULT x'000102030405060708090A0B0C0D0E0F', 31 | VARBINARY_FIELD VARCHAR(100) CHARACTER SET OCTETS DEFAULT x'DEADBEEF', 32 | 33 | -- Blob types 34 | BLOB_TEXT BLOB SUB_TYPE TEXT DEFAULT 'Hello from text blob', 35 | BLOB_BINARY BLOB SUB_TYPE 0 DEFAULT 'Hello from binary blob' 36 | ); 37 | -------------------------------------------------------------------------------- /tests/interbase.inc: -------------------------------------------------------------------------------- 1 | 8 | --FILE-- 9 | 53 | --EXPECT-- 54 | int(63) 55 | Table:TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT 56 | array(2) { 57 | ["FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"]=> 58 | int(1) 59 | ["🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰"]=> 60 | int(2) 61 | } 62 | Table:😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂😂 63 | array(2) { 64 | ["FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"]=> 65 | int(1) 66 | ["🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰🥰"]=> 67 | int(2) 68 | } -------------------------------------------------------------------------------- /tests/proc-001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Procedures 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 39 | --EXPECT-- 40 | array(2) { 41 | ["N"]=> 42 | int(1) 43 | ["RESULT"]=> 44 | int(2) 45 | } 46 | array(2) { 47 | ["N"]=> 48 | int(1) 49 | ["RESULT"]=> 50 | int(11) 51 | } 52 | ------------------ 53 | array(2) { 54 | ["N"]=> 55 | int(1) 56 | ["RESULT"]=> 57 | int(2) 58 | } 59 | array(2) { 60 | ["N"]=> 61 | int(2) 62 | ["RESULT"]=> 63 | int(3) 64 | } 65 | array(2) { 66 | ["N"]=> 67 | int(3) 68 | ["RESULT"]=> 69 | int(4) 70 | } 71 | array(2) { 72 | ["N"]=> 73 | int(4) 74 | ["RESULT"]=> 75 | int(5) 76 | } 77 | array(2) { 78 | ["N"]=> 79 | int(5) 80 | ["RESULT"]=> 81 | int(6) 82 | } 83 | array(2) { 84 | ["N"]=> 85 | int(1) 86 | ["RESULT"]=> 87 | int(11) 88 | } 89 | array(2) { 90 | ["N"]=> 91 | int(2) 92 | ["RESULT"]=> 93 | int(12) 94 | } 95 | array(2) { 96 | ["N"]=> 97 | int(3) 98 | ["RESULT"]=> 99 | int(13) 100 | } 101 | array(2) { 102 | ["N"]=> 103 | int(4) 104 | ["RESULT"]=> 105 | int(14) 106 | } 107 | array(2) { 108 | ["N"]=> 109 | int(5) 110 | ["RESULT"]=> 111 | int(15) 112 | } -------------------------------------------------------------------------------- /firebird_utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Copyright (c) The PHP Group | 4 | +----------------------------------------------------------------------+ 5 | | This source file is subject to version 3.01 of the PHP license, | 6 | | that is bundled with this package in the file LICENSE, and is | 7 | | available through the world-wide-web at the following url: | 8 | | https://www.php.net/license/3_01.txt | 9 | | If you did not receive a copy of the PHP license and are unable to | 10 | | obtain it through the world-wide-web, please send a note to | 11 | | license@php.net so we can mail you a copy immediately. | 12 | +----------------------------------------------------------------------+ 13 | | Author: Simonov Denis | 14 | +----------------------------------------------------------------------+ 15 | */ 16 | 17 | #ifndef PDO_FIREBIRD_UTILS_H 18 | #define PDO_FIREBIRD_UTILS_H 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | 25 | #if FB_API_VER >= 30 26 | 27 | #include 28 | #include "php_ibase_includes.h" 29 | 30 | unsigned fbu_get_client_version(void *master_ptr); 31 | ISC_TIME fbu_encode_time(void *master_ptr, unsigned hours, unsigned minutes, 32 | unsigned seconds, unsigned fractions); 33 | ISC_DATE fbu_encode_date(void *master_ptr, unsigned year, unsigned month, unsigned day); 34 | 35 | #endif // FB_API_VER >= 30 36 | 37 | 38 | #if FB_API_VER >= 40 39 | void fbu_decode_time_tz(void *master_ptr, const ISC_TIME_TZ* timeTz, unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions, 40 | unsigned timeZoneBufferLength, char* timeZoneBuffer); 41 | void fbu_decode_timestamp_tz(void *master_ptr, const ISC_TIMESTAMP_TZ* timestampTz, 42 | unsigned* year, unsigned* month, unsigned* day, 43 | unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions, 44 | unsigned timeZoneBufferLength, char* timeZoneBuffer); 45 | int fbu_insert_field_info(void *master_ptr, ISC_STATUS* st, int is_outvar, int num, 46 | zval *into_array, void *statement_ptr); 47 | int fbu_insert_aliases(void *master_ptr, ISC_STATUS* st, ibase_query *ib_query, 48 | void *statement_ptr); 49 | 50 | #endif // FB_API_VER >= 30 51 | 52 | 53 | #ifdef __cplusplus 54 | } 55 | #endif 56 | 57 | #endif /* PDO_FIREBIRD_UTILS_H */ 58 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | PHP_ARG_WITH([interbase], 2 | [for Firebird support], 3 | [AS_HELP_STRING([[--with-interbase[=DIR]]], 4 | [Include Firebird support. DIR is the Firebird base install directory 5 | [/opt/firebird]])]) 6 | 7 | if test "$PHP_INTERBASE" != "no"; then 8 | 9 | AC_PATH_PROG(FB_CONFIG, fb_config, no) 10 | 11 | if test -x "$FB_CONFIG" && test "$PHP_INTERBASE" = "yes"; then 12 | AC_MSG_CHECKING(for libfbconfig) 13 | FB_CFLAGS=`$FB_CONFIG --cflags` 14 | FB_LIBDIR=`$FB_CONFIG --libs` 15 | FB_VERSION=`$FB_CONFIG --version` 16 | AC_MSG_RESULT(version $FB_VERSION) 17 | PHP_EVAL_LIBLINE($FB_LIBDIR, INTERBASE_SHARED_LIBADD) 18 | PHP_EVAL_INCLINE($FB_CFLAGS) 19 | 20 | else 21 | if test "$PHP_INTERBASE" = "yes"; then 22 | IBASE_INCDIR=/opt/firebird/include 23 | IBASE_LIBDIR=/opt/firebird/lib 24 | else 25 | IBASE_INCDIR=$PHP_INTERBASE/include 26 | IBASE_LIBDIR=$PHP_INTERBASE/$PHP_LIBDIR 27 | fi 28 | 29 | PHP_CHECK_LIBRARY(fbclient, isc_detach_database, 30 | [ 31 | IBASE_LIBNAME=fbclient 32 | ], [ 33 | PHP_CHECK_LIBRARY(gds, isc_detach_database, 34 | [ 35 | IBASE_LIBNAME=gds 36 | ], [ 37 | PHP_CHECK_LIBRARY(ib_util, isc_detach_database, 38 | [ 39 | IBASE_LIBNAME=ib_util 40 | ], [ 41 | AC_MSG_ERROR([libfbclient, libgds or libib_util not found! Check config.log for more information.]) 42 | ], [ 43 | -L$IBASE_LIBDIR 44 | ]) 45 | ], [ 46 | -L$IBASE_LIBDIR 47 | ]) 48 | ], [ 49 | -L$IBASE_LIBDIR 50 | ]) 51 | 52 | PHP_ADD_LIBRARY_WITH_PATH($IBASE_LIBNAME, $IBASE_LIBDIR, INTERBASE_SHARED_LIBADD) 53 | PHP_ADD_INCLUDE($IBASE_INCDIR) 54 | fi 55 | 56 | AC_DEFINE(HAVE_IBASE,1,[ ]) 57 | PHP_NEW_EXTENSION(interbase, interbase.c ibase_query.c ibase_service.c ibase_events.c ibase_blobs.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1,[cxx]) 58 | PHP_SUBST(INTERBASE_SHARED_LIBADD) 59 | 60 | PHP_REQUIRE_CXX() 61 | PHP_CXX_COMPILE_STDCXX([11], [mandatory], [PHP_INTERBASE_STDCXX]) 62 | 63 | PHP_INTERBASE_CXX_SOURCES="firebird_utils.cpp" 64 | 65 | AS_VAR_IF([ext_shared], [no], 66 | [PHP_ADD_SOURCES([$ext_dir], 67 | [$PHP_INTERBASE_CXX_SOURCES], 68 | [$PHP_INTERBASE_STDCXX])], 69 | [PHP_ADD_SOURCES_X([$ext_dir], 70 | [$PHP_INTERBASE_CXX_SOURCES], 71 | [$PHP_INTERBASE_STDCXX], 72 | [shared_objects_interbase], 73 | [yes])]) 74 | 75 | fi 76 | -------------------------------------------------------------------------------- /tests/nulls_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test NULLs 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 46 | --EXPECT-- 47 | array(4) { 48 | ["ID"]=> 49 | int(1) 50 | ["CODE1"]=> 51 | string(7) "CODE1 1" 52 | ["CODE10"]=> 53 | string(8) "CODE10 1" 54 | ["MAN_ID"]=> 55 | NULL 56 | } 57 | array(4) { 58 | ["ID"]=> 59 | int(2) 60 | ["CODE1"]=> 61 | NULL 62 | ["CODE10"]=> 63 | string(8) "CODE10 2" 64 | ["MAN_ID"]=> 65 | int(101) 66 | } 67 | array(4) { 68 | ["ID"]=> 69 | int(3) 70 | ["CODE1"]=> 71 | string(7) "CODE1 3" 72 | ["CODE10"]=> 73 | NULL 74 | ["MAN_ID"]=> 75 | NULL 76 | } 77 | array(4) { 78 | ["ID"]=> 79 | int(4) 80 | ["CODE1"]=> 81 | NULL 82 | ["CODE10"]=> 83 | NULL 84 | ["MAN_ID"]=> 85 | int(104) 86 | } 87 | array(4) { 88 | ["ID"]=> 89 | int(5) 90 | ["CODE1"]=> 91 | string(7) "CODE1 5" 92 | ["CODE10"]=> 93 | NULL 94 | ["MAN_ID"]=> 95 | int(105) 96 | } 97 | array(4) { 98 | ["ID"]=> 99 | int(6) 100 | ["CODE1"]=> 101 | string(7) "CODE1 6" 102 | ["CODE10"]=> 103 | string(8) "CODE10 6" 104 | ["MAN_ID"]=> 105 | NULL 106 | } 107 | array(4) { 108 | ["ID"]=> 109 | int(7) 110 | ["CODE1"]=> 111 | NULL 112 | ["CODE10"]=> 113 | string(8) "CODE10 7" 114 | ["MAN_ID"]=> 115 | int(107) 116 | } 117 | array(4) { 118 | ["ID"]=> 119 | int(8) 120 | ["CODE1"]=> 121 | NULL 122 | ["CODE10"]=> 123 | NULL 124 | ["MAN_ID"]=> 125 | NULL 126 | } 127 | array(4) { 128 | ["ID"]=> 129 | int(9) 130 | ["CODE1"]=> 131 | string(7) "CODE1 8" 132 | ["CODE10"]=> 133 | string(8) "CODE10 9" 134 | ["MAN_ID"]=> 135 | int(109) 136 | } 137 | -------------------------------------------------------------------------------- /tests/common.inc: -------------------------------------------------------------------------------- 1 | $r) { 22 | print "---- Batch $batch ----\n"; 23 | dump_rows($r); 24 | ibase_free_result($r); 25 | } 26 | } 27 | 28 | function test_ibase_trans_014_015() { 29 | global $test_base; 30 | 31 | $x = ibase_connect($test_base); 32 | var_dump(ibase_trans($x)); 33 | var_dump(ibase_trans(1)); 34 | var_dump(ibase_close()); 35 | var_dump(ibase_close($x)); 36 | } 37 | 38 | function test_time_unixtime(): void { 39 | ini_set("ibase.timeformat", "%H:%M:%S"); 40 | ibase_query("CREATE TABLE TTEST ( 41 | ID INTEGER, 42 | T1 TIME DEFAULT '15:45:59', 43 | T2 TIMESTAMP DEFAULT '2025-11-06 15:45:59' 44 | )"); 45 | ibase_commit(); 46 | ibase_query("INSERT INTO TTEST (ID) VALUES (1)"); 47 | dump_table_rows("TTEST"); 48 | dump_table_rows("TTEST", null, IBASE_UNIXTIME); 49 | } 50 | 51 | function test_time_tz_unixtime(): void { 52 | ibase_query("CREATE TABLE TTEST ( 53 | ID INTEGER, 54 | T1 TIME WITH TIME ZONE DEFAULT '15:45:59 Europe/Riga', 55 | T2 TIMESTAMP WITH TIME ZONE DEFAULT '2025-11-06 15:45:59 Europe/Riga' 56 | )"); 57 | ibase_commit(); 58 | ibase_query("INSERT INTO TTEST (ID) VALUES (1)"); 59 | dump_table_rows("TTEST"); 60 | dump_table_rows("TTEST", null, IBASE_UNIXTIME); 61 | } 62 | 63 | function test_fields25(): void { 64 | ibase_query(file_get_contents(__DIR__."/001-FIELDS25.sql")); 65 | ibase_commit(); 66 | 67 | ibase_query("INSERT INTO FIELDS25 (ID) VALUES (1)"); 68 | $q = ibase_query("SELECT * FROM FIELDS25"); 69 | $num_fields = ibase_num_fields($q); 70 | for($i = 0; $i < $num_fields; $i++){ 71 | $info = ibase_field_info($q, $i); 72 | printf("%s/%s/%d\n", $info["name"], $info["type"], $info["length"]); 73 | } 74 | } 75 | 76 | function test_fields40(): void { 77 | ibase_query(file_get_contents(__DIR__."/001-FIELDS40.sql")); 78 | ibase_commit(); 79 | 80 | ibase_query("INSERT INTO FIELDS40 (ID) VALUES (DEFAULT)"); 81 | $q = ibase_query("SELECT * FROM FIELDS40"); 82 | $num_fields = ibase_num_fields($q); 83 | for($i = 0; $i < $num_fields; $i++){ 84 | $info = ibase_field_info($q, $i); 85 | printf("%s/%s/%d\n", $info["name"], $info["type"], $info["length"]); 86 | } 87 | } 88 | 89 | function test_field_data40(): void { 90 | ibase_query(file_get_contents(__DIR__."/001-FIELDS40.sql")); 91 | ibase_commit(); 92 | ibase_query("INSERT INTO FIELDS40 (ID) VALUES (1)"); 93 | dump_table_rows("FIELDS40"); 94 | } 95 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------- 2 | The PHP License, version 3.01 3 | Copyright (c) 1999 - 2019 The PHP Group. All rights reserved. 4 | -------------------------------------------------------------------- 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, is permitted provided that the following conditions 8 | are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | 13 | 2. Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in 15 | the documentation and/or other materials provided with the 16 | distribution. 17 | 18 | 3. The name "PHP" must not be used to endorse or promote products 19 | derived from this software without prior written permission. For 20 | written permission, please contact group@php.net. 21 | 22 | 4. Products derived from this software may not be called "PHP", nor 23 | may "PHP" appear in their name, without prior written permission 24 | from group@php.net. You may indicate that your software works in 25 | conjunction with PHP by saying "Foo for PHP" instead of calling 26 | it "PHP Foo" or "phpfoo" 27 | 28 | 5. The PHP Group may publish revised and/or new versions of the 29 | license from time to time. Each version will be given a 30 | distinguishing version number. 31 | Once covered code has been published under a particular version 32 | of the license, you may always continue to use it under the terms 33 | of that version. You may also choose to use such covered code 34 | under the terms of any subsequent version of the license 35 | published by the PHP Group. No one other than the PHP Group has 36 | the right to modify the terms applicable to covered code created 37 | under this License. 38 | 39 | 6. Redistributions of any form whatsoever must retain the following 40 | acknowledgment: 41 | "This product includes PHP software, freely available from 42 | ". 43 | 44 | THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND 45 | ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 46 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 47 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP 48 | DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 49 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 50 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 51 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 53 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 54 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 55 | OF THE POSSIBILITY OF SUCH DAMAGE. 56 | 57 | -------------------------------------------------------------------- 58 | 59 | This software consists of voluntary contributions made by many 60 | individuals on behalf of the PHP Group. 61 | 62 | The PHP Group can be contacted via Email at group@php.net. 63 | 64 | For more information on the PHP Group and the PHP project, 65 | please see . 66 | 67 | PHP includes the Zend Engine, freely available at 68 | . 69 | -------------------------------------------------------------------------------- /php_interbase.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 7, 8 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 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 | | Authors: Jouni Ahto | 16 | | Andrew Avdeev | 17 | | Ard Biesheuvel | 18 | | Martin Koeditz | 19 | | Jonatan Klemets | 20 | | others | 21 | +----------------------------------------------------------------------+ 22 | | You'll find history on Github | 23 | | https://github.com/FirebirdSQL/php-firebird/commits/master | 24 | +----------------------------------------------------------------------+ 25 | */ 26 | 27 | #ifndef PHP_INTERBASE_H 28 | #define PHP_INTERBASE_H 29 | 30 | extern zend_module_entry ibase_module_entry; 31 | #define phpext_interbase_ptr &ibase_module_entry 32 | 33 | #include "ibase.h" 34 | 35 | #define TO_STRING_(x) #x 36 | #define TO_STRING(x) TO_STRING_(x) 37 | 38 | #ifndef FB_API_VER 39 | static_assert(false, "FATAL: FB_API_VER is not defined. Assumed very old, unsupported client library"); 40 | #endif 41 | 42 | #define PHP_INTERBASE_VER_MAJOR 6 43 | #define PHP_INTERBASE_VER_MINOR 1 44 | #define PHP_INTERBASE_VER_REV 1 45 | #define PHP_INTERBASE_VER_PRE "-RC2" 46 | 47 | // Keep two digit style similar to FB_API_VER 48 | #define PHP_INTERBASE_VER PHP_INTERBASE_VER_MAJOR * 10 + PHP_INTERBASE_VER_MINOR 49 | #define PHP_INTERBASE_VER_STR \ 50 | TO_STRING(PHP_INTERBASE_VER_MAJOR) "." \ 51 | TO_STRING(PHP_INTERBASE_VER_MINOR) "." \ 52 | TO_STRING(PHP_INTERBASE_VER_REV) \ 53 | PHP_INTERBASE_VER_PRE 54 | 55 | PHP_MINIT_FUNCTION(ibase); 56 | PHP_RINIT_FUNCTION(ibase); 57 | PHP_MSHUTDOWN_FUNCTION(ibase); 58 | PHP_RSHUTDOWN_FUNCTION(ibase); 59 | PHP_MINFO_FUNCTION(ibase); 60 | 61 | PHP_FUNCTION(ibase_connect); 62 | PHP_FUNCTION(ibase_pconnect); 63 | PHP_FUNCTION(ibase_close); 64 | PHP_FUNCTION(ibase_drop_db); 65 | PHP_FUNCTION(ibase_query); 66 | PHP_FUNCTION(ibase_fetch_row); 67 | PHP_FUNCTION(ibase_fetch_assoc); 68 | PHP_FUNCTION(ibase_fetch_object); 69 | PHP_FUNCTION(ibase_free_result); 70 | PHP_FUNCTION(ibase_name_result); 71 | PHP_FUNCTION(ibase_prepare); 72 | PHP_FUNCTION(ibase_execute); 73 | PHP_FUNCTION(ibase_free_query); 74 | 75 | PHP_FUNCTION(ibase_timefmt); 76 | 77 | PHP_FUNCTION(ibase_gen_id); 78 | PHP_FUNCTION(ibase_num_fields); 79 | PHP_FUNCTION(ibase_num_params); 80 | PHP_FUNCTION(ibase_affected_rows); 81 | PHP_FUNCTION(ibase_field_info); 82 | PHP_FUNCTION(ibase_param_info); 83 | 84 | PHP_FUNCTION(ibase_trans); 85 | PHP_FUNCTION(ibase_commit); 86 | PHP_FUNCTION(ibase_rollback); 87 | PHP_FUNCTION(ibase_commit_ret); 88 | PHP_FUNCTION(ibase_rollback_ret); 89 | 90 | PHP_FUNCTION(ibase_blob_create); 91 | PHP_FUNCTION(ibase_blob_add); 92 | PHP_FUNCTION(ibase_blob_cancel); 93 | PHP_FUNCTION(ibase_blob_open); 94 | PHP_FUNCTION(ibase_blob_get); 95 | PHP_FUNCTION(ibase_blob_close); 96 | PHP_FUNCTION(ibase_blob_echo); 97 | PHP_FUNCTION(ibase_blob_info); 98 | PHP_FUNCTION(ibase_blob_import); 99 | 100 | PHP_FUNCTION(ibase_add_user); 101 | PHP_FUNCTION(ibase_modify_user); 102 | PHP_FUNCTION(ibase_delete_user); 103 | 104 | PHP_FUNCTION(ibase_service_attach); 105 | PHP_FUNCTION(ibase_service_detach); 106 | PHP_FUNCTION(ibase_backup); 107 | PHP_FUNCTION(ibase_restore); 108 | PHP_FUNCTION(ibase_maintain_db); 109 | PHP_FUNCTION(ibase_db_info); 110 | PHP_FUNCTION(ibase_server_info); 111 | 112 | PHP_FUNCTION(ibase_errmsg); 113 | PHP_FUNCTION(ibase_errcode); 114 | 115 | PHP_FUNCTION(ibase_wait_event); 116 | PHP_FUNCTION(ibase_set_event_handler); 117 | PHP_FUNCTION(ibase_free_event_handler); 118 | 119 | PHP_FUNCTION(ibase_get_client_version); 120 | PHP_FUNCTION(ibase_get_client_major_version); 121 | PHP_FUNCTION(ibase_get_client_minor_version); 122 | 123 | #else 124 | 125 | #define phpext_interbase_ptr NULL 126 | 127 | #endif /* PHP_INTERBASE_H */ 128 | -------------------------------------------------------------------------------- /tests/functions.inc: -------------------------------------------------------------------------------- 1 | $v)die("skip Firebird server version $cv > $v"); 127 | } 128 | 129 | /** @var float $v */ 130 | function skip_if_fb_gte($v) { 131 | if(($cv = get_fb_version()) >= $v)die("skip Firebird server version $cv >= $v"); 132 | } 133 | 134 | function skip_if_fbclient_lt(float $v): void { 135 | if(!function_exists("ibase_get_client_version"))die("skip Firebird client library version (undefined) < $v"); 136 | if(($cv = ibase_get_client_version()) < $v)die("skip Firebird client library version $cv < $v"); 137 | } 138 | 139 | function skip_if_fbclient_gte(float $v): void { 140 | if(function_exists("ibase_get_client_version")) { 141 | if(($cv = ibase_get_client_version()) >= $v)die("skip Firebird client library version $cv >= $v"); 142 | } 143 | } 144 | 145 | function skip_if_ext_lt(int $v): void { 146 | if(!defined('IBASE_VER'))die("Skip IBASE_VER (not defined) < $v"); 147 | if(IBASE_VER < $v)die("Skip IBASE_VER ".IBASE_VER." < $v"); 148 | } 149 | 150 | function skip_if_ext_gte(int $v): void { 151 | if(defined('IBASE_VER')) { 152 | if(IBASE_VER >= $v)die("Skip IBASE_VER ".IBASE_VER." >= $v"); 153 | } 154 | } 155 | 156 | function skip_if_php_lt(float $v): void { 157 | $cv = (float)PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION; 158 | if($cv < $v)die("skip PHP version $cv < $v"); 159 | } 160 | 161 | function skip_if_php_gte(float $v): void { 162 | $cv = (float)PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION; 163 | if($cv >= $v)die("skip PHP version $cv >= $v"); 164 | } 165 | 166 | function ibase_query_bulk(array $queries, $tr = null) { 167 | foreach($queries as $q){ 168 | if(is_array($q)){ 169 | [$sql, $args] = $q; 170 | } else { 171 | $sql = $q; 172 | $args = []; 173 | } 174 | 175 | if($tr) { 176 | ibase_query($tr, $sql, ...$args); 177 | } else { 178 | ibase_query($sql, ...$args); 179 | } 180 | } 181 | } 182 | 183 | /** @var Exception $e */ 184 | function php_ibase_exception_handler($e) { 185 | echo "Fatal error: Uncaught ".get_class($e).": ", $e->getMessage(), "\n"; 186 | } 187 | -------------------------------------------------------------------------------- /tests/007.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | InterBase: array handling 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | V_CHAR[$i],$v_char[$i],strlen($v_char[$i])) != 0) { 88 | echo " CHAR[$i] fail:\n"; 89 | echo " in: ".$v_char[$i]."\n"; 90 | echo " out: ".$row->V_CHAR[$i]."\n"; 91 | } 92 | if($row->V_DATE[$i] != $v_date[$i]) { 93 | echo " DATE[$i] fail\n"; 94 | echo " in: ".$v_date[$i]."\n"; 95 | echo " out: ".$row->V_DATE[$i]."\n"; 96 | } 97 | if($row->V_DECIMAL[$i] != $v_decimal[$i]) { 98 | echo " DECIMAL[$i] fail\n"; 99 | echo " in: ".$v_decimal[$i]."\n"; 100 | echo " out: ".$row->V_DECIMAL[$i]."\n"; 101 | } 102 | if(abs($row->V_DOUBLE[$i] - $v_double[$i]) > abs($v_double[$i] / 1E15)) { 103 | echo " DOUBLE[$i] fail\n"; 104 | echo " in: ".$v_double[$i]."\n"; 105 | echo " out: ".$row->V_DOUBLE[$i]."\n"; 106 | } 107 | if(abs($row->V_FLOAT[$i] - $v_float[$i]) > abs($v_float[$i] / 1E7)) { 108 | echo " FLOAT[$i] fail\n"; 109 | echo " in: ".$v_float[$i]."\n"; 110 | echo " out: ".$row->V_FLOAT[$i]."\n"; 111 | } 112 | if($row->V_INTEGER[$i] != $v_integer[$i]) { 113 | echo " INTEGER[$i] fail\n"; 114 | echo " in: ".$v_integer[$i]."\n"; 115 | echo " out: ".$row->V_INTEGER[$i]."\n"; 116 | } 117 | if ($row->V_NUMERIC[$i] != $v_numeric[$i]) { 118 | echo " NUMERIC[$i] fail\n"; 119 | echo " in: ".$v_numeric[$i]."\n"; 120 | echo " out: ".$row->V_NUMERIC[$i]."\n"; 121 | } 122 | if ($row->V_SMALLINT[$i] != $v_smallint[$i]) { 123 | echo " SMALLINT[$i] fail\n"; 124 | echo " in: ".$v_smallint[$i]."\n"; 125 | echo " out: ".$row->V_SMALLINT[$i]."\n"; 126 | } 127 | if ($row->V_VARCHAR[$i] != $v_varchar[$i]) { 128 | echo " VARCHAR[$i] fail:\n"; 129 | echo " in: ".$v_varchar[$i]."\n"; 130 | echo " out: ".$row->V_VARCHAR[$i]."\n"; 131 | } 132 | } 133 | ibase_free_result($sel); 134 | }/* for($iter) */ 135 | 136 | echo "select\n"; 137 | 138 | $sel = ibase_query("SELECT v_multi[5,5,5],v_multi[10,10,10] FROM test7 WHERE iter = 0"); 139 | print_r(ibase_fetch_row($sel)); 140 | ibase_free_result($sel); 141 | 142 | for($iter = 1; $iter <= 3; $iter++) { 143 | 144 | if(!($sel = ibase_query( 145 | "select iter from test7 where v_char[$iter] LIKE ?", $v_char[$iter]."%")) || 146 | !ibase_fetch_row($sel)) { 147 | echo "CHAR fail\n"; 148 | } 149 | ibase_free_result($sel); 150 | 151 | if(!($sel = ibase_query( 152 | "select iter from test7 where v_date[$iter] = ?", $v_date[$iter])) || 153 | !ibase_fetch_row($sel)) { 154 | echo "DATE fail\n"; 155 | } 156 | ibase_free_result($sel); 157 | if(!($sel = ibase_query( 158 | "select iter from test7 where v_decimal[$iter] = ?", $v_decimal[$iter])) || 159 | !ibase_fetch_row($sel)) { 160 | echo "DECIMAL fail\n"; 161 | } 162 | ibase_free_result($sel); 163 | if(!($sel = ibase_query( 164 | "select iter from test7 where v_integer[$iter] = ?", $v_integer[$iter])) || 165 | !ibase_fetch_row($sel)) { 166 | echo "INTEGER fail\n"; 167 | } 168 | ibase_free_result($sel); 169 | if(!($sel = ibase_query( 170 | "select iter from test7 where v_numeric[$iter] = ?", $v_numeric[$iter])) || 171 | !ibase_fetch_row($sel)) { 172 | echo "NUMERIC fail\n"; 173 | } 174 | ibase_free_result($sel); 175 | if(!($sel = ibase_query( 176 | "select iter from test7 where v_smallint[$iter] = ?", $v_smallint[$iter])) || 177 | !ibase_fetch_row($sel)) { 178 | echo "SMALLINT fail\n"; 179 | } 180 | ibase_free_result($sel); 181 | } 182 | ibase_close(); 183 | echo "end of test\n"; 184 | ?> 185 | --EXPECT-- 186 | insert 187 | select 188 | Array 189 | ( 190 | [0] => 125 191 | [1] => 1000 192 | ) 193 | end of test 194 | -------------------------------------------------------------------------------- /firebird_utils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Copyright (c) The PHP Group | 4 | +----------------------------------------------------------------------+ 5 | | This source file is subject to version 3.01 of the PHP license, | 6 | | that is bundled with this package in the file LICENSE, and is | 7 | | available through the world-wide-web at the following url: | 8 | | https://www.php.net/license/3_01.txt | 9 | | If you did not receive a copy of the PHP license and are unable to | 10 | | obtain it through the world-wide-web, please send a note to | 11 | | license@php.net so we can mail you a copy immediately. | 12 | +----------------------------------------------------------------------+ 13 | | Author: Simonov Denis | 14 | | Author: Martins Lazdans | 15 | +----------------------------------------------------------------------+ 16 | */ 17 | 18 | #include 19 | 20 | #if FB_API_VER >= 30 21 | #include 22 | #include 23 | #include "php.h" 24 | #include "firebird_utils.h" 25 | #include "php_ibase_includes.h" 26 | 27 | /* Returns the client version. 0 bytes are minor version, 1 bytes are major version. */ 28 | extern "C" unsigned fbu_get_client_version(void *master_ptr) 29 | { 30 | Firebird::IMaster* master = (Firebird::IMaster*)master_ptr; 31 | Firebird::IUtil* util = master->getUtilInterface(); 32 | return util->getClientVersion(); 33 | } 34 | 35 | extern "C" ISC_TIME fbu_encode_time(void *master_ptr, unsigned hours, unsigned minutes, unsigned seconds, unsigned fractions) 36 | { 37 | Firebird::IMaster* master = (Firebird::IMaster*)master_ptr; 38 | Firebird::IUtil* util = master->getUtilInterface(); 39 | return util->encodeTime(hours, minutes, seconds, fractions); 40 | } 41 | 42 | extern "C" ISC_DATE fbu_encode_date(void *master_ptr, unsigned year, unsigned month, unsigned day) 43 | { 44 | Firebird::IMaster* master = (Firebird::IMaster*)master_ptr; 45 | Firebird::IUtil* util = master->getUtilInterface(); 46 | return util->encodeDate(year, month, day); 47 | } 48 | 49 | static void fbu_copy_status(const ISC_STATUS* from, ISC_STATUS* to, size_t maxLength) 50 | { 51 | for(size_t i=0; i < maxLength; ++i) { 52 | memcpy(to + i, from + i, sizeof(ISC_STATUS)); 53 | if (from[i] == isc_arg_end) { 54 | break; 55 | } 56 | } 57 | } 58 | 59 | #endif // FB_API_VER >= 30 60 | 61 | #if FB_API_VER >= 40 62 | /* Decodes a time with time zone into its time components. */ 63 | extern "C" void fbu_decode_time_tz(void *master_ptr, const ISC_TIME_TZ* timeTz, unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions, 64 | unsigned timeZoneBufferLength, char* timeZoneBuffer) 65 | { 66 | Firebird::IMaster* master = (Firebird::IMaster*)master_ptr; 67 | Firebird::IUtil* util = master->getUtilInterface(); 68 | Firebird::IStatus* status = master->getStatus(); 69 | Firebird::CheckStatusWrapper st(status); 70 | util->decodeTimeTz(&st, timeTz, hours, minutes, seconds, fractions, 71 | timeZoneBufferLength, timeZoneBuffer); 72 | } 73 | 74 | /* Decodes a timestamp with time zone into its date and time components */ 75 | extern "C" void fbu_decode_timestamp_tz(void *master_ptr, const ISC_TIMESTAMP_TZ* timestampTz, 76 | unsigned* year, unsigned* month, unsigned* day, 77 | unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions, 78 | unsigned timeZoneBufferLength, char* timeZoneBuffer) 79 | { 80 | Firebird::IMaster* master = (Firebird::IMaster*)master_ptr; 81 | Firebird::IUtil* util = master->getUtilInterface(); 82 | Firebird::IStatus* status = master->getStatus(); 83 | Firebird::CheckStatusWrapper st(status); 84 | util->decodeTimeStampTz(&st, timestampTz, year, month, day, 85 | hours, minutes, seconds, fractions, 86 | timeZoneBufferLength, timeZoneBuffer); 87 | } 88 | 89 | extern "C" int fbu_insert_aliases(void *master_ptr, ISC_STATUS* st, ibase_query *ib_query, void *statement_ptr) 90 | { 91 | Firebird::IMaster* master = (Firebird::IMaster*)master_ptr; 92 | Firebird::ThrowStatusWrapper status(master->getStatus()); 93 | Firebird::IStatement* statement = (Firebird::IStatement *)statement_ptr; 94 | Firebird::IMessageMetadata* meta = NULL; 95 | ISC_STATUS res; 96 | 97 | try { 98 | meta = statement->getOutputMetadata(&status); 99 | unsigned cols = meta->getCount(&status); 100 | 101 | assert(cols == ib_query->out_fields_count); 102 | 103 | for (unsigned i = 0; i < cols; ++i) 104 | { 105 | _php_ibase_insert_alias(ib_query->ht_aliases, meta->getAlias(&status, i)); 106 | } 107 | } 108 | catch (const Firebird::FbException& error) 109 | { 110 | if (status.hasData()) { 111 | fbu_copy_status((const ISC_STATUS*)status.getErrors(), st, 20); 112 | return st[1]; 113 | } 114 | } 115 | 116 | return 0; 117 | } 118 | 119 | extern "C" int fbu_insert_field_info(void *master_ptr, ISC_STATUS* st, int is_outvar, int num, 120 | zval *into_array, void *statement_ptr) 121 | { 122 | Firebird::IMaster* master = (Firebird::IMaster*)master_ptr; 123 | Firebird::ThrowStatusWrapper status(master->getStatus()); 124 | Firebird::IStatement* statement = (Firebird::IStatement *)statement_ptr; 125 | Firebird::IMessageMetadata* meta = NULL; 126 | ISC_STATUS res; 127 | 128 | try { 129 | if(is_outvar) { 130 | meta = statement->getOutputMetadata(&status); 131 | } else { 132 | meta = statement->getInputMetadata(&status); 133 | } 134 | 135 | add_index_string(into_array, 0, meta->getField(&status, num)); 136 | add_assoc_string(into_array, "name", meta->getField(&status, num)); 137 | 138 | add_index_string(into_array, 1, meta->getAlias(&status, num)); 139 | add_assoc_string(into_array, "alias", meta->getAlias(&status, num)); 140 | 141 | add_index_string(into_array, 2, meta->getRelation(&status, num)); 142 | add_assoc_string(into_array, "relation", meta->getRelation(&status, num)); 143 | } 144 | catch (const Firebird::FbException& error) 145 | { 146 | if (status.hasData()) { 147 | fbu_copy_status((const ISC_STATUS*)status.getErrors(), st, 20); 148 | return st[1]; 149 | } 150 | } 151 | 152 | return 0; 153 | } 154 | 155 | #endif // FB_API_VER >= 40 156 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP Firebird extension 2 | 3 | ## Using the driver on Windows 4 | In order for this extension to work, there are DLL files that must be available to the Windows system PATH. For information on how to do this, see the FAQ entitled "How do I add my PHP directory to the PATH on Windows" (https://www.php.net/manual/en/faq.installation.php#faq.installation.addtopath). Although copying DLL files from the PHP folder into the Windows system directory also works (because the system directory is by default in the system's PATH), this is not recommended. This extension requires the following files to be in the PATH: fbclient.dll,gds32.dll 5 | 6 | If you installed the Firebird/InterBase database server on the same machine PHP is running on, you will have this DLL already and fbclient.dll, gds32.dll (gds32.dll is generated from the installer for legacy applications) will already be in the PATH. 7 | 8 | ## Building the driver 9 | 10 | ### Build the driver on Linux 11 | First of all, we have to meet some requirements. This means we need to install the `phpize` command. The `phpize` command is used to prepare the build environment for a PHP extension. 12 | Install the `phpize` command. This is usually done by installing the `php7-devel` or `php8-devel` package using the system's package manager. Also install the fbclient library and developer packages. 13 | 14 | For OpenSuse 15.1 and PHP 7 use 15 | ``` 16 | $ zypper in php7-devel libfbclient2 libfbclient-devel 17 | ``` 18 | 19 | The command in Linux Mint 20 / Ubuntu is 20 | ``` 21 | sudo apt-get install php-dev firebird-dev firebird3.0 firebird3.0-common firebird3.0-server 22 | ``` 23 | 24 | Now make sure you provide the fbclient.so and the header files (ibase.h). These are needed to compile. You can specify the include path for the ibase.h file with CPPFLAGS as you can see in the following listing. 25 | ``` 26 | $ git clone https://github.com/FirebirdSQL/php-firebird.git 27 | $ cd php-firebird 28 | $ phpize 29 | $ CPPFLAGS=-I/usr/include/firebird ./configure 30 | $ make 31 | ``` 32 | 33 | Note: If you use different PHP versions in parallel don't forget to make the correct settings. Linux Mint 20 / Ubuntu uses this syntax: 34 | ``` 35 | $ git clone https://github.com/FirebirdSQL/php-firebird.git 36 | $ cd php-firebird 37 | $ phpize7.4 38 | $ CPPFLAGS=-I/usr/include/firebird ./configure --with-php-config=/usr/bin/php-config7.4 39 | $ make 40 | ``` 41 | 42 | If the configure process passes you will get following message: 43 | ``` 44 | $ Build complete. 45 | $ Don't forget to run 'make test'. 46 | ``` 47 | You can find the `interbase.so` file in directory `php-firebird/modules`. Copy the file to your php extension dir and restart your webserver. 48 | 49 | #### Clean up your working directory 50 | After you've created the binary data, many temporary files will be created in your working directory. These can be removed with the command `phpize --clean`. Then you have a tidy directory again. 51 | 52 | ### Build the driver on Windows 53 | First of all, we have to meet some requirements. This means we need to install the Git for Windows and Visual Studio 2017 with following components: 54 | Visual C++ 2017 (vc15) or Visual C++ 2019 (vs16) must be installed prior SDK usage. Required components 55 | - C++ dev 56 | - Windows SDK 57 | - .NET dev 58 | 59 | Also make sure you are using a 64-bit build host with Windows 7 or later. 60 | Of course we need some Firebird related stuff. The easiest way is to install the related Firebird version on your build host including the development files. 61 | 62 | To start the build process open a command line. We assume that the build is done in the directory `c:\php-sdk`. So make sure you have the permission to create that folder on Drive C:. 63 | ``` 64 | git clone https://github.com/Microsoft/php-sdk-binary-tools.git c:\php-sdk 65 | cd c:\php-sdk 66 | git checkout php-sdk-2.2.0 67 | ``` 68 | With the above we downloaded the PHP SDK and entered our working directory. 69 | 70 | Next we will prepare our build environment. 71 | For Win32 do: 72 | ``` 73 | phpsdk-vc15-x86.bat 74 | ``` 75 | Use following command for Win64: 76 | ``` 77 | phpsdk-vc15-x64.bat 78 | ``` 79 | If you use VS 2019, replace vc15 by vs16. 80 | 81 | Now let's create the build structure, download the PHP sources and checkout the desired development branch: 82 | ``` 83 | phpsdk_buildtree phpmaster 84 | git clone https://github.com/php/php-src.git && cd php-src && git checkout PHP-7.4.0 85 | ``` 86 | 87 | Since we have our PHP sources now, we're on to get the depending libraries. 88 | ``` 89 | phpsdk_deps --update --branch 7.4 90 | ``` 91 | 92 | In the next step we will download our Firebird extension sources. 93 | ``` 94 | mkdir ..\pecl 95 | git clone https://github.com/FirebirdSQL/php-firebird.git ..\pecl\interbase 96 | ``` 97 | 98 | If everything is ok, we can now compile our PHP extension. Please specify the the correct path to your Firebird installation. 99 | 100 | #### Build TS extension 101 | Usually you will build thread safe extensions. 102 | For Win32 thread safe (TS) do: 103 | ``` 104 | buildconf --force && configure --disable-all --enable-cli --with-interbase="shared,C:\Program Files (x86)\Firebird\3_0" && nmake 105 | ``` 106 | For Win64thread safe (TS) do: 107 | ``` 108 | buildconf --force && configure --disable-all --enable-cli --with-interbase="shared,C:\Program Files\Firebird\3_0\lib" && nmake 109 | ``` 110 | After the compilation you can find your extension called `php_interbase.dll` e.g. in `C:\php-sdk\phpmaster\vc15\x64\php-src\x64\Release_TS\php_interbase.dll` 111 | Replace x64 with x86 for Win32. 112 | 113 | #### Build NTS extension 114 | For Win32 non-thread safe (NTS) run: 115 | ``` 116 | buildconf --force && configure --disable-zts --disable-all --enable-cli --with-interbase="shared,C:\Program Files (x86)\Firebird\3_0" && nmake 117 | ``` 118 | For Win64 non-thread safe (NTS) run: 119 | ``` 120 | buildconf --force && configure --disable-zts --disable-all --enable-cli --with-interbase="shared,C:\Program Files\Firebird\3_0\lib" && nmake 121 | ``` 122 | After the compilation you can find your extension called `php_interbase.dll` e.g. in `C:\php-sdk\phpmaster\vc15\x86\php-src\Release` 123 | Replace x86 with x64 for Win64. 124 | 125 | #### Clean up your working directory 126 | After you've created the binary data, many temporary files will be created in your working directory. These can be removed with the command `nmake clean`. Then you have a tidy directory again. 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /tests/003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | InterBase: misc sql types (may take a while) 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | V_CHAR,0,strlen($v_char)) != $v_char){ 70 | echo " CHAR fail:\n"; 71 | echo " in: $v_char\n"; 72 | echo " out: $row->V_CHAR\n"; 73 | } 74 | if($row->V_DATE != $v_date){ 75 | echo " DATE fail\n"; 76 | echo " in: $v_date\n"; 77 | echo " out: $row->V_DATE\n"; 78 | } 79 | if($row->V_DECIMAL4_2 != $v_decimal4_2){ 80 | echo " DECIMAL4_2 fail\n"; 81 | echo " in: $v_decimal4_2\n"; 82 | echo " out: $row->V_DECIMAL4_2\n"; 83 | } 84 | if($row->V_DECIMAL4_0 != $v_decimal4_0){ 85 | echo " DECIMAL4_0 fail\n"; 86 | echo " in: $v_decimal4_0\n"; 87 | echo " out: $row->V_DECIMAL4_0\n"; 88 | } 89 | if($row->V_DECIMAL7_2 != $v_decimal7_2){ 90 | echo " DECIMAL7_2 fail\n"; 91 | echo " in: $v_decimal7_2\n"; 92 | echo " out: $row->V_DECIMAL7_2\n"; 93 | } 94 | if($row->V_DECIMAL7_0 != $v_decimal7_0){ 95 | echo " DECIMAL7_0 fail\n"; 96 | echo " in: $v_decimal7_0\n"; 97 | echo " out: $row->V_DECIMAL7_0\n"; 98 | } 99 | if($row->V_NUMERIC15_15 != $v_numeric15_15){ 100 | echo " NUMERIC15_15 fail\n"; 101 | echo " in: $v_numeric15_15\n"; 102 | echo " out: $row->V_NUMERIC15_15\n"; 103 | } 104 | if($row->V_DECIMAL18_3 != $v_decimal18_3){ 105 | echo " DECIMAL18_3 fail\n"; 106 | echo " in: $v_decimal18_3\n"; 107 | echo " out: $row->V_DECIMAL18_3\n"; 108 | } 109 | if($row->V_NUMERIC15_0 != (string)$v_numeric15_0){ 110 | echo " NUMERIC15_0 fail\n"; 111 | echo " in: $v_numeric15_0\n"; 112 | echo " out: $row->V_NUMERIC15_0\n"; 113 | } 114 | 115 | if(abs($row->V_DOUBLE - $v_double) > abs($v_double / 1E15)){ 116 | echo " DOUBLE fail\n"; 117 | echo " in: $v_double\n"; 118 | echo " out: $row->V_DOUBLE\n"; 119 | } 120 | if(abs($row->V_FLOAT - $v_float) > abs($v_float / 1E7)){ 121 | echo " FLOAT fail\n"; 122 | echo " in: $v_float\n"; 123 | echo " out: $row->V_FLOAT\n"; 124 | } 125 | if($row->V_INTEGER != $v_integer){ 126 | echo " INTEGER fail\n"; 127 | echo " in: $v_integer\n"; 128 | echo " out: $row->V_INTEGER\n"; 129 | } 130 | if($row->V_SMALLINT != $v_smallint){ 131 | echo " SMALLINT fail\n"; 132 | echo " in: $v_smallint\n"; 133 | echo " out: $row->V_SMALLINT\n"; 134 | } 135 | 136 | if(substr($row->V_VARCHAR,0,strlen($v_varchar)) != $v_varchar){ 137 | echo " VARCHAR fail:\n"; 138 | echo " in: $v_varchar\n"; 139 | echo " out: $row->V_VARCHAR\n"; 140 | } 141 | 142 | ibase_free_result($sel); 143 | } /* for($iter) */ 144 | 145 | /* check for correct handling of duplicate field names */ 146 | $q = ibase_query('SELECT 1 AS id, 2 AS id, 3 AS id, 4 AS id, 5 AS id, 6 AS id, 7 AS id, 8 AS id, 9 AS id, 147 | 10 AS id, 11 AS id, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 FROM rdb$database'); 148 | var_dump(ibase_fetch_assoc($q)); 149 | 150 | ibase_close(); 151 | echo "end of test\n"; 152 | ?> 153 | --EXPECT-- 154 | array(22) { 155 | ["ID"]=> 156 | int(1) 157 | ["ID_01"]=> 158 | int(2) 159 | ["ID_02"]=> 160 | int(3) 161 | ["ID_03"]=> 162 | int(4) 163 | ["ID_04"]=> 164 | int(5) 165 | ["ID_05"]=> 166 | int(6) 167 | ["ID_06"]=> 168 | int(7) 169 | ["ID_07"]=> 170 | int(8) 171 | ["ID_08"]=> 172 | int(9) 173 | ["ID_09"]=> 174 | int(10) 175 | ["ID_10"]=> 176 | int(11) 177 | ["CONSTANT"]=> 178 | int(12) 179 | ["CONSTANT_01"]=> 180 | int(13) 181 | ["CONSTANT_02"]=> 182 | int(14) 183 | ["CONSTANT_03"]=> 184 | int(15) 185 | ["CONSTANT_04"]=> 186 | int(16) 187 | ["CONSTANT_05"]=> 188 | int(17) 189 | ["CONSTANT_06"]=> 190 | int(18) 191 | ["CONSTANT_07"]=> 192 | int(19) 193 | ["CONSTANT_08"]=> 194 | int(20) 195 | ["CONSTANT_09"]=> 196 | int(21) 197 | ["CONSTANT_10"]=> 198 | int(22) 199 | } 200 | end of test 201 | -------------------------------------------------------------------------------- /tests/005.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | InterBase: transactions 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | 214 | --EXPECTF-- 215 | default transaction: 216 | empty table 217 | --- test5 --- 218 | --- 219 | one row 220 | --- test5 --- 221 | 1 222 | --- 223 | after rollback table empty again 224 | --- test5 --- 225 | --- 226 | one row 227 | --- test5 --- 228 | 2 229 | --- 230 | one row 231 | --- test5 --- 232 | 2 233 | --- 234 | one row... again. 235 | --- test5 --- 236 | 2 237 | --- 238 | one row. 239 | --- test5 --- 240 | 2 241 | --- 242 | one row 243 | --- test5 --- 244 | 2 245 | --- 246 | two rows 247 | --- test5 --- 248 | 2 249 | 3 250 | --- 251 | two rows again 252 | --- test5 --- 253 | 2 254 | 4 255 | --- 256 | one row in second transaction 257 | --- test5 --- 258 | 2 259 | --- 260 | three rows in third transaction 261 | --- test5 --- 262 | 2 263 | 3 264 | 4 265 | --- 266 | three rows in fourth transaction with deadlock 267 | --- test5 --- 268 | 2 269 | 3 270 | 4 271 | errmsg [%S] 272 | --- 273 | three rows 274 | --- test5 --- 275 | 2 276 | 3 277 | 4 278 | --- 279 | four rows 280 | --- test5 --- 281 | 2 282 | 3 283 | 4 284 | 5 285 | --- 286 | four rows again 287 | --- test5 --- 288 | 2 289 | 3 290 | 4 291 | 5 292 | --- 293 | end of test 294 | -------------------------------------------------------------------------------- /tests/006.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | InterBase: binding (may take a while) 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | V_CHAR,0,strlen($v_char)) != $v_char) { 61 | echo " CHAR fail:\n"; 62 | echo " in: $v_char\n"; 63 | echo " out: $row->V_CHAR\n"; 64 | } 65 | if($row->V_DATE != $v_date) { 66 | echo " DATE fail\n"; 67 | echo " in: $v_date\n"; 68 | echo " out: $row->V_DATE\n"; 69 | } 70 | if($row->V_DECIMAL != $v_decimal) { 71 | echo " DECIMAL fail\n"; 72 | echo " in: $v_decimal\n"; 73 | echo " out: $row->V_DECIMAL\n"; 74 | } 75 | if(abs($row->V_DOUBLE - $v_double) > abs($v_double / 1E15)) { 76 | echo " DOUBLE fail\n"; 77 | echo " in: $v_double\n"; 78 | echo " out: $row->V_DOUBLE\n"; 79 | } 80 | if(abs($row->V_FLOAT - $v_float) > abs($v_float / 1E7)) { 81 | echo " FLOAT fail\n"; 82 | echo " in: $v_float\n"; 83 | echo " out: $row->V_FLOAT\n"; 84 | } 85 | if($row->V_INTEGER != $v_integer) { 86 | echo " INTEGER fail\n"; 87 | echo " in: $v_integer\n"; 88 | echo " out: $row->V_INTEGER\n"; 89 | } 90 | if ($row->V_NUMERIC != $v_numeric) { 91 | echo " NUMERIC fail\n"; 92 | echo " in: $v_numeric\n"; 93 | echo " out: $row->V_NUMERIC\n"; 94 | } 95 | if ($row->V_SMALLINT != $v_smallint) { 96 | echo " SMALLINT fail\n"; 97 | echo " in: $v_smallint\n"; 98 | echo " out: $row->V_SMALLINT\n"; 99 | } 100 | if ($row->V_VARCHAR != $v_varchar) { 101 | echo " VARCHAR fail:\n"; 102 | echo " in: $v_varchar\n"; 103 | echo " out: $row->V_VARCHAR\n"; 104 | } 105 | ibase_free_result($sel); 106 | }/* for($iter)*/ 107 | 108 | echo "select\n"; 109 | for($iter = 0; $iter < 3; $iter++) { 110 | /* prepare data */ 111 | $v_char = rand_str(1000); 112 | $v_date = (int)rand_number(10,0,0); 113 | $v_decimal = rand_number(12,3); 114 | $v_double = rand_number(20); 115 | $v_float = rand_number(7); 116 | $v_integer = rand_number(9,0); 117 | $v_numeric = rand_number(4,2); 118 | $v_smallint = ((int)rand_number(5)) % 32767; 119 | $v_varchar = rand_str(10000); 120 | 121 | /* clear table*/ 122 | ibase_query("delete from test6"); 123 | 124 | /* make one record */ 125 | ibase_query("insert into test6 126 | (iter, v_char,v_date,v_decimal, 127 | v_integer,v_numeric,v_smallint,v_varchar) 128 | values (666, '$v_char',?,$v_decimal, $v_integer, 129 | $v_numeric, $v_smallint, '$v_varchar')",$v_date); 130 | 131 | /* test all types */ 132 | if(!($sel = ibase_query( 133 | "select iter from test6 where v_char = ?", $v_char)) || 134 | !ibase_fetch_row($sel)) { 135 | echo "CHAR fail\n"; 136 | } 137 | ibase_free_result($sel); 138 | if(!($sel = ibase_query( 139 | "select iter from test6 where v_date = ?", $v_date)) || 140 | !ibase_fetch_row($sel)) { 141 | echo "DATE fail\n"; 142 | } 143 | ibase_free_result($sel); 144 | if(!($sel = ibase_query( 145 | "select iter from test6 where v_decimal = ?", $v_decimal)) || 146 | !ibase_fetch_row($sel)) { 147 | echo "DECIMAL fail\n"; 148 | } 149 | ibase_free_result($sel); 150 | if(!($sel = ibase_query( 151 | "select iter from test6 where v_integer = ?", $v_integer)) || 152 | !ibase_fetch_row($sel)) { 153 | echo "INTEGER fail\n"; 154 | } 155 | ibase_free_result($sel); 156 | if(!($sel = ibase_query( 157 | "select iter from test6 where v_numeric = ?", $v_numeric)) || 158 | !ibase_fetch_row($sel)) { 159 | echo "NUMERIC fail\n"; 160 | } 161 | ibase_free_result($sel); 162 | if(!($sel = ibase_query( 163 | "select iter from test6 where v_smallint = ?", $v_smallint)) || 164 | !ibase_fetch_row($sel)) { 165 | echo "SMALLINT fail\n"; 166 | } 167 | ibase_free_result($sel); 168 | if(!($sel = ibase_query( 169 | "select iter from test6 where v_varchar = ?", $v_varchar)) || 170 | !ibase_fetch_row($sel)) { 171 | echo "VARCHAR fail\n"; 172 | } 173 | ibase_free_result($sel); 174 | 175 | } /*for iter*/ 176 | 177 | echo "prepare and exec insert\n"; 178 | 179 | /* prepare table */ 180 | ibase_query("delete from test6"); 181 | 182 | /* prepare query */ 183 | $query = ibase_prepare( 184 | "insert into test6 (v_integer) values (?)"); 185 | 186 | for($i = 0; $i < 10; $i++) { 187 | ibase_execute($query, $i); 188 | } 189 | 190 | out_table("test6"); 191 | 192 | ibase_free_query($query); 193 | 194 | echo "prepare and exec select\n"; 195 | 196 | /* prepare query */ 197 | $query = ibase_prepare("select * from test6 198 | where v_integer between ? and ?"); 199 | 200 | $low_border = 2; 201 | $high_border = 6; 202 | 203 | $res = ibase_execute($query, $low_border, $high_border); 204 | out_result($res, "test6"); 205 | ibase_free_result($res); 206 | 207 | $low_border = 0; 208 | $high_border = 4; 209 | $res = ibase_execute($query, $low_border, $high_border); 210 | out_result($res, "test6"); 211 | ibase_free_result($res); 212 | 213 | $res = ibase_execute($query, "5", 7.499); 214 | out_result($res, "test6"); 215 | ibase_free_result($res); 216 | 217 | ibase_free_query($query); 218 | 219 | /* test execute procedure */ 220 | $query = ibase_prepare("execute procedure add1(?)"); 221 | $res = array(); 222 | for ($i = 0; $i < 10; $i++) { 223 | $res[] = ibase_execute($query,$i); 224 | } 225 | ibase_free_query($query); 226 | foreach ($res as $r) { 227 | out_result($r, "proc add1"); 228 | ibase_free_result($r); 229 | } 230 | 231 | ibase_close(); 232 | echo "end of test\n"; 233 | ?> 234 | --EXPECT-- 235 | insert 236 | select 237 | prepare and exec insert 238 | --- test6 --- 239 | 0 240 | 1 241 | 2 242 | 3 243 | 4 244 | 5 245 | 6 246 | 7 247 | 8 248 | 9 249 | --- 250 | prepare and exec select 251 | --- test6 --- 252 | 2 253 | 3 254 | 4 255 | 5 256 | 6 257 | --- 258 | --- test6 --- 259 | 0 260 | 1 261 | 2 262 | 3 263 | 4 264 | --- 265 | --- test6 --- 266 | 5 267 | 6 268 | 7 269 | --- 270 | --- proc add1 --- 271 | 1 272 | --- 273 | --- proc add1 --- 274 | 2 275 | --- 276 | --- proc add1 --- 277 | 3 278 | --- 279 | --- proc add1 --- 280 | 4 281 | --- 282 | --- proc add1 --- 283 | 5 284 | --- 285 | --- proc add1 --- 286 | 6 287 | --- 288 | --- proc add1 --- 289 | 7 290 | --- 291 | --- proc add1 --- 292 | 8 293 | --- 294 | --- proc add1 --- 295 | 9 296 | --- 297 | --- proc add1 --- 298 | 10 299 | --- 300 | end of test 301 | -------------------------------------------------------------------------------- /tests/004.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | InterBase: BLOB test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | V_BLOB); 40 | 41 | $blob = ''; 42 | while($piece = ibase_blob_get($bl_h, 1 + rand() % 1024)) 43 | $blob .= $piece; 44 | if($blob != $blob_str) 45 | echo " BLOB 1 fail (1)\n"; 46 | ibase_blob_close($bl_h); 47 | 48 | $bl_h = ibase_blob_open($link,$row->V_BLOB); 49 | 50 | $blob = ''; 51 | while($piece = ibase_blob_get($bl_h, 100 * 1024)) 52 | $blob .= $piece; 53 | if($blob != $blob_str) 54 | echo " BLOB 1 fail (2)\n"; 55 | ibase_blob_close($bl_h); 56 | ibase_free_result($q); 57 | unset($blob); 58 | 59 | echo "create blob 2\n"; 60 | 61 | ibase_query("INSERT INTO test4 (v_integer, v_blob) VALUES (2, ?)", $blob_str); 62 | 63 | echo "test blob 2\n"; 64 | 65 | $q = ibase_query("SELECT v_blob FROM test4 WHERE v_integer = 2"); 66 | $row = ibase_fetch_object($q,IBASE_TEXT); 67 | 68 | if($row->V_BLOB != $blob_str) 69 | echo " BLOB 2 fail\n"; 70 | ibase_free_result($q); 71 | unset($blob); 72 | 73 | 74 | echo "create blob 3\n"; 75 | 76 | $bl_h = ibase_blob_create($link); 77 | 78 | ibase_blob_add($bl_h, "+----------------------------------------------------------------------+\n"); 79 | ibase_blob_add($bl_h, "| PHP HTML Embedded Scripting Language Version 3.0 |\n"); 80 | ibase_blob_add($bl_h, "+----------------------------------------------------------------------+\n"); 81 | ibase_blob_add($bl_h, "| Copyright (c) 1997-2000 PHP Development Team (See Credits file) |\n"); 82 | ibase_blob_add($bl_h, "+----------------------------------------------------------------------+\n"); 83 | ibase_blob_add($bl_h, "| This program is free software; you can redistribute it and/or modify |\n"); 84 | ibase_blob_add($bl_h, "| it under the terms of one of the following licenses: |\n"); 85 | ibase_blob_add($bl_h, "| |\n"); 86 | ibase_blob_add($bl_h, "| A) the GNU General Public License as published by the Free Software |\n"); 87 | ibase_blob_add($bl_h, "| Foundation; either version 2 of the License, or (at your option) |\n"); 88 | ibase_blob_add($bl_h, "| any later version. |\n"); 89 | ibase_blob_add($bl_h, "| |\n"); 90 | ibase_blob_add($bl_h, "| B) the PHP License as published by the PHP Development Team and |\n"); 91 | ibase_blob_add($bl_h, "| included in the distribution in the file: LICENSE |\n"); 92 | ibase_blob_add($bl_h, "| |\n"); 93 | ibase_blob_add($bl_h, "| This program is distributed in the hope that it will be useful, |\n"); 94 | ibase_blob_add($bl_h, "| but WITHOUT ANY WARRANTY; without even the implied warranty of |\n"); 95 | ibase_blob_add($bl_h, "| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |\n"); 96 | ibase_blob_add($bl_h, "| GNU General Public License for more details. |\n"); 97 | ibase_blob_add($bl_h, "| |\n"); 98 | ibase_blob_add($bl_h, "| You should have received a copy of both licenses referred to here. |\n"); 99 | ibase_blob_add($bl_h, "| If you did not, or have any questions about PHP licensing, please |\n"); 100 | ibase_blob_add($bl_h, "| contact core@php.net. |\n"); 101 | ibase_blob_add($bl_h, "+----------------------------------------------------------------------+\n"); 102 | $bl_s = ibase_blob_close($bl_h); 103 | ibase_query("INSERT INTO test4 (v_integer, v_blob) VALUES (3, ?)", $bl_s); 104 | ibase_commit(); 105 | echo "echo blob 3\n"; 106 | 107 | $q = ibase_query("SELECT v_blob FROM test4 WHERE v_integer = 3"); 108 | $row = ibase_fetch_object($q); 109 | ibase_commit(); 110 | ibase_close(); 111 | 112 | $link = ibase_connect($test_base); 113 | ibase_blob_echo($link, $row->V_BLOB); 114 | ibase_free_result($q); 115 | 116 | echo "fetch blob 3\n"; 117 | $q = ibase_query("SELECT v_blob FROM test4 WHERE v_integer = 3"); 118 | $row = ibase_fetch_object($q,IBASE_TEXT); 119 | echo $row->V_BLOB; 120 | ibase_free_result($q); 121 | 122 | ibase_close(); 123 | unlink($name); 124 | echo "end of test\n"; 125 | ?> 126 | --EXPECT-- 127 | import blob 1 128 | test blob 1 129 | create blob 2 130 | test blob 2 131 | create blob 3 132 | echo blob 3 133 | +----------------------------------------------------------------------+ 134 | | PHP HTML Embedded Scripting Language Version 3.0 | 135 | +----------------------------------------------------------------------+ 136 | | Copyright (c) 1997-2000 PHP Development Team (See Credits file) | 137 | +----------------------------------------------------------------------+ 138 | | This program is free software; you can redistribute it and/or modify | 139 | | it under the terms of one of the following licenses: | 140 | | | 141 | | A) the GNU General Public License as published by the Free Software | 142 | | Foundation; either version 2 of the License, or (at your option) | 143 | | any later version. | 144 | | | 145 | | B) the PHP License as published by the PHP Development Team and | 146 | | included in the distribution in the file: LICENSE | 147 | | | 148 | | This program is distributed in the hope that it will be useful, | 149 | | but WITHOUT ANY WARRANTY; without even the implied warranty of | 150 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 151 | | GNU General Public License for more details. | 152 | | | 153 | | You should have received a copy of both licenses referred to here. | 154 | | If you did not, or have any questions about PHP licensing, please | 155 | | contact core@php.net. | 156 | +----------------------------------------------------------------------+ 157 | fetch blob 3 158 | +----------------------------------------------------------------------+ 159 | | PHP HTML Embedded Scripting Language Version 3.0 | 160 | +----------------------------------------------------------------------+ 161 | | Copyright (c) 1997-2000 PHP Development Team (See Credits file) | 162 | +----------------------------------------------------------------------+ 163 | | This program is free software; you can redistribute it and/or modify | 164 | | it under the terms of one of the following licenses: | 165 | | | 166 | | A) the GNU General Public License as published by the Free Software | 167 | | Foundation; either version 2 of the License, or (at your option) | 168 | | any later version. | 169 | | | 170 | | B) the PHP License as published by the PHP Development Team and | 171 | | included in the distribution in the file: LICENSE | 172 | | | 173 | | This program is distributed in the hope that it will be useful, | 174 | | but WITHOUT ANY WARRANTY; without even the implied warranty of | 175 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 176 | | GNU General Public License for more details. | 177 | | | 178 | | You should have received a copy of both licenses referred to here. | 179 | | If you did not, or have any questions about PHP licensing, please | 180 | | contact core@php.net. | 181 | +----------------------------------------------------------------------+ 182 | end of test 183 | -------------------------------------------------------------------------------- /php_ibase_includes.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 7, 8 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 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 | | Authors: Jouni Ahto | 16 | | Andrew Avdeev | 17 | | Ard Biesheuvel | 18 | | Martin Koeditz | 19 | | others | 20 | +----------------------------------------------------------------------+ 21 | | You'll find history on Github | 22 | | https://github.com/FirebirdSQL/php-firebird/commits/master | 23 | +----------------------------------------------------------------------+ 24 | */ 25 | 26 | #ifndef PHP_IBASE_INCLUDES_H 27 | #define PHP_IBASE_INCLUDES_H 28 | 29 | #include 30 | 31 | #ifndef SQLDA_CURRENT_VERSION 32 | #define SQLDA_CURRENT_VERSION SQLDA_VERSION1 33 | #endif 34 | 35 | #ifndef METADATALENGTH 36 | #if FB_API_VER >= 40 37 | # define METADATALENGTH 63*4 38 | #else 39 | # define METADATALENGTH 31 40 | #endif 41 | #endif 42 | 43 | #define RESET_ERRMSG do { IBG(errmsg)[0] = '\0'; IBG(sql_code) = 0; } while (0) 44 | 45 | #define IB_STATUS (IBG(status)) 46 | 47 | #ifdef IBASE_DEBUG 48 | #define IBDEBUG(a) php_printf("::: %s (%s:%d)\n", a, __FILE__, __LINE__); 49 | #endif 50 | 51 | #ifndef IBDEBUG 52 | #define IBDEBUG(a) 53 | #endif 54 | 55 | extern int le_link, le_plink, le_trans; 56 | 57 | #define LE_LINK "Firebird/InterBase link" 58 | #define LE_PLINK "Firebird/InterBase persistent link" 59 | #define LE_TRANS "Firebird/InterBase transaction" 60 | #define LE_EVENT "Firebird/InterBase blob" 61 | #define LE_BLOB "Firebird/InterBase event" 62 | #define LE_QUERY "Firebird/InterBase query" 63 | #define LE_SCVH "Firebird/InterBase service manager handle" 64 | 65 | #define IBASE_MSGSIZE 512 66 | #define MAX_ERRMSG (IBASE_MSGSIZE*2) 67 | 68 | #define IB_DEF_DATE_FMT "%Y-%m-%d" 69 | #define IB_DEF_TIME_FMT "%H:%M:%S" 70 | 71 | /* this value should never be > USHRT_MAX */ 72 | #define IBASE_BLOB_SEG 4096 73 | 74 | ZEND_BEGIN_MODULE_GLOBALS(ibase) 75 | ISC_STATUS status[20]; 76 | zend_resource *default_link; 77 | zend_long num_links, num_persistent; 78 | char errmsg[MAX_ERRMSG]; 79 | zend_long sql_code; 80 | zend_long default_trans_params; 81 | zend_long default_lock_timeout; // only used togetger with trans_param IBASE_LOCK_TIMEOUT 82 | void *get_master_interface; 83 | void *master_instance; 84 | void *get_statement_interface; 85 | int client_version; 86 | int client_major_version; 87 | int client_minor_version; 88 | ZEND_END_MODULE_GLOBALS(ibase) 89 | 90 | ZEND_EXTERN_MODULE_GLOBALS(ibase) 91 | 92 | typedef struct { 93 | isc_db_handle handle; 94 | struct tr_list *tr_list; 95 | unsigned short dialect; 96 | struct event *event_head; 97 | } ibase_db_link; 98 | 99 | typedef struct { 100 | isc_tr_handle handle; 101 | unsigned short link_cnt; 102 | unsigned long affected_rows; 103 | ibase_db_link *db_link[1]; /* last member */ 104 | } ibase_trans; 105 | 106 | typedef struct tr_list { 107 | ibase_trans *trans; 108 | struct tr_list *next; 109 | } ibase_tr_list; 110 | 111 | typedef struct { 112 | isc_blob_handle bl_handle; 113 | unsigned short type; 114 | ISC_QUAD bl_qd; 115 | } ibase_blob; 116 | 117 | typedef struct event { 118 | ibase_db_link *link; 119 | zend_resource* link_res; 120 | ISC_LONG event_id; 121 | unsigned short event_count; 122 | char **events; 123 | unsigned char *event_buffer, *result_buffer; 124 | zval callback; 125 | void *thread_ctx; 126 | struct event *event_next; 127 | enum event_state { NEW, ACTIVE, DEAD } state; 128 | } ibase_event; 129 | 130 | typedef struct { 131 | unsigned short vary_length; 132 | char vary_string[1]; 133 | } IBVARY; 134 | 135 | /* sql variables union 136 | * used for convert and binding input variables 137 | */ 138 | typedef struct { 139 | union { 140 | #ifdef SQL_BOOLEAN 141 | FB_BOOLEAN bval; 142 | #endif 143 | short sval; 144 | float fval; 145 | ISC_LONG lval; 146 | ISC_QUAD qval; 147 | ISC_TIMESTAMP tsval; 148 | ISC_DATE dtval; 149 | ISC_TIME tmval; 150 | } val; 151 | short nullind; 152 | } BIND_BUF; 153 | 154 | typedef struct { 155 | ISC_ARRAY_DESC ar_desc; 156 | ISC_LONG ar_size; /* size of entire array in bytes */ 157 | unsigned short el_type, el_size; 158 | } ibase_array; 159 | 160 | typedef struct _ib_query { 161 | ibase_db_link *link; 162 | ibase_trans *trans; 163 | zend_resource *trans_res; 164 | zend_resource *res; 165 | isc_stmt_handle stmt; 166 | XSQLDA *in_sqlda, *out_sqlda; 167 | ibase_array *in_array, *out_array; 168 | unsigned short type, has_more_rows, is_open; 169 | unsigned short in_array_cnt, out_array_cnt; 170 | unsigned short dialect; 171 | char *query; 172 | ISC_UCHAR statement_type; 173 | BIND_BUF *bind_buf; 174 | ISC_SHORT *out_nullind; 175 | ISC_SHORT *sql_types; 176 | ISC_USHORT in_fields_count, out_fields_count; 177 | HashTable *ht_aliases, *ht_ind; // Precomputed for ibase_fetch_*() 178 | int was_result_once; 179 | } ibase_query; 180 | 181 | enum php_interbase_option { 182 | PHP_IBASE_DEFAULT = 0, 183 | PHP_IBASE_CREATE = 0, 184 | /* fetch flags */ 185 | PHP_IBASE_FETCH_BLOBS = 1, 186 | PHP_IBASE_FETCH_ARRAYS = 2, 187 | PHP_IBASE_UNIXTIME = 4, 188 | /* transaction access mode */ 189 | PHP_IBASE_WRITE = 1, 190 | PHP_IBASE_READ = 2, 191 | /* transaction isolation level */ 192 | PHP_IBASE_CONCURRENCY = 4, 193 | PHP_IBASE_COMMITTED = 8, 194 | PHP_IBASE_REC_NO_VERSION = 32, 195 | PHP_IBASE_REC_VERSION = 64, 196 | PHP_IBASE_CONSISTENCY = 16, 197 | /* transaction lock resolution */ 198 | PHP_IBASE_WAIT = 128, 199 | PHP_IBASE_NOWAIT = 256, 200 | PHP_IBASE_LOCK_TIMEOUT = 512, 201 | }; 202 | 203 | #define IBG(v) ZEND_MODULE_GLOBALS_ACCESSOR(ibase, v) 204 | 205 | #if defined(ZTS) && defined(COMPILE_DL_INTERBASE) 206 | ZEND_TSRMLS_CACHE_EXTERN() 207 | #endif 208 | 209 | #define BLOB_ID_LEN 18 210 | #define BLOB_ID_MASK "0x%" LL_MASK "x" 211 | 212 | #define BLOB_INPUT 1 213 | #define BLOB_OUTPUT 2 214 | 215 | #ifdef PHP_WIN32 216 | // Case switch, because of troubles on Windows and PHP 8.0 217 | #if PHP_VERSION_ID < 80000 218 | #define LL_MASK "I64" 219 | #else 220 | #define LL_MASK "ll" 221 | #endif 222 | #define LL_LIT(lit) lit ## I64 223 | typedef void (__stdcall *info_func_t)(char*); 224 | #else 225 | #define LL_MASK "ll" 226 | #define LL_LIT(lit) lit ## ll 227 | typedef void (*info_func_t)(char*); 228 | #endif 229 | 230 | void _php_ibase_error(void); 231 | void _php_ibase_module_error(const char *, ...) 232 | PHP_ATTRIBUTE_FORMAT(printf,1,2); 233 | 234 | /* determine if a resource is a link or transaction handle */ 235 | #define PHP_IBASE_LINK_TRANS(zv, lh, th) \ 236 | do { \ 237 | if (!zv) { \ 238 | lh = (ibase_db_link *)zend_fetch_resource2( \ 239 | IBG(default_link), "InterBase link", le_link, le_plink); \ 240 | } else { \ 241 | _php_ibase_get_link_trans(INTERNAL_FUNCTION_PARAM_PASSTHRU, zv, &lh, &th); \ 242 | } \ 243 | if (SUCCESS != _php_ibase_def_trans(lh, &th)) { RETURN_FALSE; } \ 244 | } while (0) 245 | 246 | int _php_ibase_def_trans(ibase_db_link *ib_link, ibase_trans **trans); 247 | void _php_ibase_get_link_trans(INTERNAL_FUNCTION_PARAMETERS, zval *link_id, 248 | ibase_db_link **ib_link, ibase_trans **trans); 249 | 250 | /* provided by ibase_query.c */ 251 | void php_ibase_query_minit(INIT_FUNC_ARGS); 252 | 253 | /* provided by ibase_blobs.c */ 254 | void php_ibase_blobs_minit(INIT_FUNC_ARGS); 255 | int _php_ibase_string_to_quad(char const *id, ISC_QUAD *qd); 256 | zend_string *_php_ibase_quad_to_string(ISC_QUAD const qd); 257 | int _php_ibase_blob_get(zval *return_value, ibase_blob *ib_blob, zend_ulong max_len); 258 | int _php_ibase_blob_add(zval *string_arg, ibase_blob *ib_blob); 259 | 260 | /* provided by ibase_events.c */ 261 | void php_ibase_events_minit(INIT_FUNC_ARGS); 262 | void _php_ibase_free_event(ibase_event *event); 263 | 264 | /* provided by ibase_service.c */ 265 | void php_ibase_service_minit(INIT_FUNC_ARGS); 266 | 267 | #ifdef __cplusplus 268 | extern "C" { 269 | #endif 270 | 271 | void _php_ibase_insert_alias(HashTable *ht, const char *alias); 272 | 273 | #ifdef __cplusplus 274 | } 275 | #endif 276 | 277 | #ifndef max 278 | #define max(a,b) ((a)>(b)?(a):(b)) 279 | #endif 280 | 281 | #ifdef PHP_DEBUG 282 | void fbp_dump_buffer(int len, const unsigned char *buffer); 283 | void fbp_dump_buffer_raw(int len, const unsigned char *buffer); 284 | #endif 285 | 286 | void fbp_error_ex(long level, const char *, ...) 287 | PHP_ATTRIBUTE_FORMAT(printf,2,3); 288 | 289 | #ifdef PHP_WIN32 290 | #define fbp_fatal(msg, ...) fbp_error_ex(E_ERROR, msg " (%s:%d)\n", ## __VA_ARGS__, __FILE__, __LINE__) 291 | #define fbp_warning(msg, ...) fbp_error_ex(E_WARNING, msg " (%s:%d)\n", ## __VA_ARGS__, __FILE__, __LINE__) 292 | #define fbp_notice(msg, ...) fbp_error_ex(E_NOTICE, msg " (%s:%d)\n", ## __VA_ARGS__, __FILE__, __LINE__) 293 | #else 294 | #define fbp_fatal(msg, ...) fbp_error_ex(E_ERROR, msg " (%s:%d)\n" __VA_OPT__(,) __VA_ARGS__, __FILE__, __LINE__) 295 | #define fbp_warning(msg, ...) fbp_error_ex(E_WARNING, msg " (%s:%d)\n" __VA_OPT__(,) __VA_ARGS__, __FILE__, __LINE__) 296 | #define fbp_notice(msg, ...) fbp_error_ex(E_NOTICE, msg " (%s:%d)\n" __VA_OPT__(,) __VA_ARGS__, __FILE__, __LINE__) 297 | #endif 298 | 299 | typedef ISC_STATUS (ISC_EXPORT *fb_get_statement_interface_t)( 300 | ISC_STATUS* status_vector, void* db_handle, isc_stmt_handle* stmt_handle 301 | ); 302 | 303 | typedef void* (ISC_EXPORT *fb_get_master_interface_t)(void); 304 | 305 | #endif /* PHP_IBASE_INCLUDES_H */ 306 | -------------------------------------------------------------------------------- /ibase_events.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 7, 8 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 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 | | Authors: Ard Biesheuvel | 16 | | Martin Köditz | 17 | +----------------------------------------------------------------------+ 18 | */ 19 | 20 | #ifdef HAVE_CONFIG_H 21 | #include "config.h" 22 | #endif 23 | 24 | #include "php.h" 25 | 26 | /* 27 | * Since PHP 8 we have troubles building sources on Windows, 28 | * because of missing TSRMLS_FETCH_FROM_CTX and TSRMLS_SET_CTX symbols. 29 | */ 30 | #if PHP_VERSION_ID >= 80000 31 | # define FBIRD_TSRMLS_FETCH_FROM_CTX(user_data) 32 | # define FBIRD_TSRMLS_SET_CTX(user_data) 33 | #else 34 | # define FBIRD_TSRMLS_FETCH_FROM_CTX(user_data) TSRMLS_FETCH_FROM_CTX(user_data) 35 | # define FBIRD_TSRMLS_SET_CTX(user_data) TSRMLS_SET_CTX(user_data) 36 | #endif 37 | 38 | #if HAVE_IBASE 39 | 40 | #include "php_interbase.h" 41 | #include "php_ibase_includes.h" 42 | 43 | static int le_event; 44 | 45 | static void _php_ibase_event_free(unsigned char *event_buf, unsigned char *result_buf) /* {{{ */ 46 | { 47 | isc_free(event_buf); 48 | isc_free(result_buf); 49 | } 50 | /* }}} */ 51 | 52 | void _php_ibase_free_event(ibase_event *event) /* {{{ */ 53 | { 54 | unsigned short i; 55 | 56 | event->state = DEAD; 57 | 58 | if (event->link != NULL) { 59 | ibase_event **node; 60 | 61 | zend_list_delete(event->link_res); 62 | if (event->link->handle != 0 && 63 | isc_cancel_events(IB_STATUS, &event->link->handle, &event->event_id)) { 64 | _php_ibase_error(); 65 | } 66 | 67 | /* delete this event from the link struct */ 68 | for (node = &event->link->event_head; *node != event; node = &(*node)->event_next); 69 | *node = event->event_next; 70 | } 71 | 72 | if (Z_TYPE(event->callback) != IS_UNDEF) { 73 | zval_ptr_dtor(&event->callback); 74 | ZVAL_UNDEF(&event->callback); 75 | 76 | _php_ibase_event_free(event->event_buffer,event->result_buffer); 77 | 78 | for (i = 0; i < event->event_count; ++i) { 79 | if (event->events[i]) { 80 | efree(event->events[i]); 81 | } 82 | } 83 | efree(event->events); 84 | } 85 | } 86 | /* }}} */ 87 | 88 | static void _php_ibase_free_event_rsrc(zend_resource *rsrc) /* {{{ */ 89 | { 90 | ibase_event *e = (ibase_event *) rsrc->ptr; 91 | 92 | _php_ibase_free_event(e); 93 | efree(e); 94 | } 95 | /* }}} */ 96 | 97 | void php_ibase_events_minit(INIT_FUNC_ARGS) /* {{{ */ 98 | { 99 | le_event = zend_register_list_destructors_ex(_php_ibase_free_event_rsrc, NULL, 100 | LE_EVENT, module_number); 101 | } 102 | /* }}} */ 103 | 104 | static void _php_ibase_event_block(ibase_db_link *ib_link, unsigned short count, 105 | char **events, unsigned short *l, unsigned char **event_buf, unsigned char **result_buf) 106 | { 107 | ISC_STATUS dummy_result[20]; 108 | ISC_ULONG dummy_count[15]; 109 | 110 | /** 111 | * Unfortunately, there's no clean and portable way in C to pass arguments to 112 | * a variadic function if you don't know the number of arguments at compile time. 113 | * (And even if there were a way, the Interbase API doesn't provide a version of 114 | * this function that takes a va_list as an argument) 115 | * 116 | * In this case, the number of arguments is limited to 18 by the underlying API, 117 | * so we can work around it. 118 | */ 119 | 120 | *l = (unsigned short) isc_event_block(event_buf, result_buf, count, events[0], 121 | events[1], events[2], events[3], events[4], events[5], events[6], events[7], 122 | events[8], events[9], events[10], events[11], events[12], events[13], events[14]); 123 | 124 | /** 125 | * Currently, this is the only way to correctly initialize an event buffer. 126 | * This is clearly something that should be fixed, cause the semantics of 127 | * isc_wait_for_event() indicate that it blocks until an event occurs. 128 | * If the Firebird people ever fix this, these lines should be removed, 129 | * otherwise, events will have to fire twice before ibase_wait_event() returns. 130 | */ 131 | 132 | isc_wait_for_event(dummy_result, &ib_link->handle, *l, *event_buf, *result_buf); 133 | isc_event_counts(dummy_count, *l, *event_buf, *result_buf); 134 | } 135 | /* }}} */ 136 | 137 | /* {{{ proto string ibase_wait_event([resource link_identifier,] string event [, string event [, ...]]) 138 | Waits for any one of the passed Interbase events to be posted by the database, and returns its name */ 139 | PHP_FUNCTION(ibase_wait_event) 140 | { 141 | zval *args; 142 | ibase_db_link *ib_link; 143 | int num_args; 144 | unsigned char *event_buffer, *result_buffer; 145 | char *events[15]; 146 | unsigned short i = 0, event_count = 0, buffer_size; 147 | ISC_ULONG occurred_event[15]; 148 | 149 | RESET_ERRMSG; 150 | 151 | /* no more than 15 events */ 152 | if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 16) { 153 | WRONG_PARAM_COUNT; 154 | } 155 | 156 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &num_args) == FAILURE) { 157 | return; 158 | } 159 | 160 | if (Z_TYPE(args[0]) == IS_RESOURCE) { 161 | if ((ib_link = (ibase_db_link *)zend_fetch_resource2_ex(&args[0], "InterBase link", le_link, le_plink)) == NULL) { 162 | RETURN_FALSE; 163 | } 164 | i = 1; 165 | } else { 166 | if (ZEND_NUM_ARGS() > 15) { 167 | WRONG_PARAM_COUNT; 168 | } 169 | if ((ib_link = (ibase_db_link *)zend_fetch_resource2(IBG(default_link), "InterBase link", le_link, le_plink)) == NULL) { 170 | RETURN_FALSE; 171 | } 172 | } 173 | 174 | for (; i < ZEND_NUM_ARGS(); ++i) { 175 | convert_to_string_ex(&args[i]); 176 | events[event_count++] = Z_STRVAL(args[i]); 177 | } 178 | 179 | /* fills the required data structure with information about the events */ 180 | _php_ibase_event_block(ib_link, event_count, events, &buffer_size, &event_buffer, &result_buffer); 181 | 182 | /* now block until an event occurs */ 183 | if (isc_wait_for_event(IB_STATUS, &ib_link->handle, buffer_size, event_buffer, result_buffer)) { 184 | _php_ibase_error(); 185 | _php_ibase_event_free(event_buffer,result_buffer); 186 | RETURN_FALSE; 187 | } 188 | 189 | /* find out which event occurred */ 190 | isc_event_counts(occurred_event, buffer_size, event_buffer, result_buffer); 191 | for (i = 0; i < event_count; ++i) { 192 | if (occurred_event[i]) { 193 | zend_string *result = zend_string_init(events[i], strlen(events[i]), 0); 194 | _php_ibase_event_free(event_buffer,result_buffer); 195 | RETURN_STR(result); 196 | } 197 | } 198 | 199 | /* If we reach this line, isc_wait_for_event() did return, but we don't know 200 | which event fired. */ 201 | _php_ibase_event_free(event_buffer,result_buffer); 202 | RETURN_FALSE; 203 | } 204 | /* }}} */ 205 | 206 | #if FB_API_VER >= 20 207 | #define PHP_ISC_CALLBACK ISC_EVENT_CALLBACK 208 | static ISC_EVENT_CALLBACK _php_ibase_callback(ibase_event *event, /* {{{ */ 209 | ISC_USHORT buffer_size, ISC_UCHAR *result_buf) 210 | #else 211 | #define PHP_ISC_CALLBACK isc_callback 212 | static isc_callback _php_ibase_callback(ibase_event *event, /* {{{ */ 213 | unsigned short buffer_size, unsigned char *result_buf) 214 | #endif 215 | { 216 | /* this function is called asynchronously by the Interbase client library. */ 217 | FBIRD_TSRMLS_FETCH_FROM_CTX(event->thread_ctx); 218 | 219 | /** 220 | * The callback function is called when the event is first registered and when the event 221 | * is cancelled. I consider this is a bug. By clearing event->callback first and setting 222 | * it to -1 later, we make sure nothing happens if no event was actually posted. 223 | */ 224 | switch (event->state) { 225 | unsigned short i; 226 | ISC_ULONG occurred_event[15]; 227 | zval return_value, args[2]; 228 | 229 | default: /* == DEAD */ 230 | break; 231 | case ACTIVE: 232 | /* copy the updated results into the result buffer */ 233 | memcpy(event->result_buffer, result_buf, buffer_size); 234 | 235 | ZVAL_RES(&args[1], event->link_res); 236 | 237 | /* find out which event occurred */ 238 | isc_event_counts(occurred_event, buffer_size, event->event_buffer, event->result_buffer); 239 | for (i = 0; i < event->event_count; ++i) { 240 | if (occurred_event[i]) { 241 | ZVAL_STRING(&args[0], event->events[i]); 242 | //efree(event->events[i]); 243 | break; 244 | } 245 | } 246 | 247 | /* call the callback provided by the user */ 248 | if (SUCCESS != call_user_function(NULL, NULL, 249 | &event->callback, &return_value, 2, args)) { 250 | _php_ibase_module_error("Error calling callback %s", Z_STRVAL(event->callback)); 251 | break; 252 | } 253 | 254 | if (Z_TYPE(return_value) == IS_FALSE) { 255 | event->state = DEAD; 256 | break; 257 | } 258 | case NEW: 259 | /* re-register the event */ 260 | if (isc_que_events(IB_STATUS, &event->link->handle, &event->event_id, buffer_size, 261 | event->event_buffer,(PHP_ISC_CALLBACK)_php_ibase_callback, (void *)event)) { 262 | 263 | _php_ibase_error(); 264 | } 265 | event->state = ACTIVE; 266 | } 267 | return 0; 268 | } 269 | /* }}} */ 270 | 271 | /* {{{ proto resource ibase_set_event_handler([resource link_identifier,] callback handler, string event [, string event [, ...]]) 272 | Register the callback for handling each of the named events */ 273 | PHP_FUNCTION(ibase_set_event_handler) 274 | { 275 | /** 276 | * The callback passed to this function should take an event name (string) and a 277 | * link resource id (int) as arguments. The value returned from the function is 278 | * used to determine if the event handler should remain set. 279 | */ 280 | zval *args, *cb_arg; 281 | ibase_db_link *ib_link; 282 | ibase_event *event; 283 | unsigned short i = 1, buffer_size; 284 | int num_args; 285 | zend_resource *link_res; 286 | 287 | RESET_ERRMSG; 288 | 289 | /* Minimum and maximum number of arguments allowed */ 290 | if (ZEND_NUM_ARGS() < 2 || ZEND_NUM_ARGS() > 17) { 291 | WRONG_PARAM_COUNT; 292 | } 293 | 294 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &num_args) == FAILURE) { 295 | return; 296 | } 297 | 298 | /* get a working link */ 299 | if (Z_TYPE(args[0]) != IS_STRING) { 300 | /* resource, callback, event_1 [, ... event_15] 301 | * No more than 15 events 302 | */ 303 | if (ZEND_NUM_ARGS() < 3 || ZEND_NUM_ARGS() > 17) { 304 | WRONG_PARAM_COUNT; 305 | } 306 | 307 | cb_arg = &args[1]; 308 | i = 2; 309 | 310 | if ((ib_link = (ibase_db_link *)zend_fetch_resource2_ex(&args[0], "InterBase link", le_link, le_plink)) == NULL) { 311 | RETURN_FALSE; 312 | } 313 | 314 | link_res = Z_RES(args[0]); 315 | 316 | } else { 317 | /* callback, event_1 [, ... event_15] 318 | * No more than 15 events 319 | */ 320 | if (ZEND_NUM_ARGS() < 2 || ZEND_NUM_ARGS() > 16) { 321 | WRONG_PARAM_COUNT; 322 | } 323 | 324 | cb_arg = &args[0]; 325 | 326 | if ((ib_link = (ibase_db_link *)zend_fetch_resource2(IBG(default_link), "InterBase link", le_link, le_plink)) == NULL) { 327 | RETURN_FALSE; 328 | } 329 | link_res = IBG(default_link); 330 | } 331 | 332 | /* get the callback */ 333 | if (!zend_is_callable(cb_arg, 0, NULL)) { 334 | zend_string *cb_name = zend_get_callable_name(cb_arg); 335 | _php_ibase_module_error("Callback argument %s is not a callable function", ZSTR_VAL(cb_name)); 336 | zend_string_release_ex(cb_name, 0); 337 | RETURN_FALSE; 338 | } 339 | 340 | /* allocate the event resource */ 341 | event = (ibase_event *) safe_emalloc(sizeof(ibase_event), 1, 0); 342 | FBIRD_TSRMLS_SET_CTX(event->thread_ctx); 343 | event->link_res = link_res; 344 | GC_ADDREF(link_res); 345 | event->link = ib_link; 346 | event->event_count = 0; 347 | event->state = NEW; 348 | event->events = (char **) safe_emalloc(sizeof(char *), 15, 0); 349 | 350 | ZVAL_DUP(&event->callback, cb_arg); 351 | 352 | for (; i < 15; ++i) { 353 | if (i < ZEND_NUM_ARGS()) { 354 | convert_to_string_ex(&args[i]); 355 | event->events[event->event_count++] = estrdup(Z_STRVAL(args[i])); 356 | } else { 357 | event->events[i] = NULL; 358 | } 359 | } 360 | 361 | /* fills the required data structure with information about the events */ 362 | _php_ibase_event_block(ib_link, event->event_count, event->events, 363 | &buffer_size, &event->event_buffer, &event->result_buffer); 364 | 365 | /* now register the events with the Interbase API */ 366 | if (isc_que_events(IB_STATUS, &ib_link->handle, &event->event_id, buffer_size, 367 | event->event_buffer,(PHP_ISC_CALLBACK)_php_ibase_callback, (void *)event)) { 368 | 369 | _php_ibase_error(); 370 | efree(event); 371 | RETURN_FALSE; 372 | } 373 | 374 | event->event_next = ib_link->event_head; 375 | ib_link->event_head = event; 376 | 377 | RETVAL_RES(zend_register_resource(event, le_event)); 378 | Z_TRY_ADDREF_P(return_value); 379 | } 380 | /* }}} */ 381 | 382 | /* {{{ proto bool ibase_free_event_handler(resource event) 383 | Frees the event handler set by ibase_set_event_handler() */ 384 | PHP_FUNCTION(ibase_free_event_handler) 385 | { 386 | zval *event_arg; 387 | 388 | RESET_ERRMSG; 389 | 390 | if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "r", &event_arg)) { 391 | ibase_event *event; 392 | 393 | event = (ibase_event *)zend_fetch_resource_ex(event_arg, "Interbase event", le_event); 394 | 395 | event->state = DEAD; 396 | 397 | zend_list_delete(Z_RES_P(event_arg)); 398 | RETURN_TRUE; 399 | } else { 400 | RETURN_FALSE; 401 | } 402 | } 403 | /* }}} */ 404 | 405 | #endif /* HAVE_IBASE */ 406 | -------------------------------------------------------------------------------- /php_ibase_udf.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 7, 8 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 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 | | Authors: Jouni Ahto | 16 | | Andrew Avdeev | 17 | | Ard Biesheuvel | 18 | | Martin Koeditz | 19 | | others | 20 | +----------------------------------------------------------------------+ 21 | | You'll find history on Github | 22 | | https://github.com/FirebirdSQL/php-firebird/commits/master | 23 | +----------------------------------------------------------------------+ 24 | */ 25 | 26 | /** 27 | * This UDF library adds the ability to call PHP functions from SQL 28 | * statements. Because of SQL's strong typing, you will have to declare 29 | * an external function for every combination { output type, #args } that 30 | * your application requires. 31 | * 32 | * Declare the functions like this: 33 | * 34 | * DECLARE EXTERNAL FUNCTION CALL_PHP1 35 | * CSTRING(xx), 36 | * BY DESCRIPTOR, 37 | * INTEGER BY DESCRIPTOR 38 | * RETURNS PARAMETER 2 39 | * ENTRY_POINT 'udf_call_php1' MODULE_NAME 'php_ibase_udf' 40 | * 41 | * DECLARE EXTERNAL FUNCTION CALL_PHP2 42 | * CSTRING(xx), 43 | * BY DESCRIPTOR, 44 | * INTEGER BY DESCRIPTOR, 45 | * INTEGER BY DESCRIPTOR 46 | * RETURNS PARAMETER 2 47 | * ENTRY_POINT 'udf_call_php2' MODULE_NAME 'php_ibase_udf' 48 | * 49 | * ... and so on. [for up to 8 input arguments] 50 | * 51 | * The first input parameter contains the name of the PHP function you want 52 | * to call. The second argument is the result. (omit this argument when calling 53 | * the function) The return type of the function is the declared type of the 54 | * result. The value returned from the PHP function being called will 55 | * automatically be converted if necessary. 56 | * The arguments should have their types declared as well, but you're free 57 | * to pass arguments of other types instead. They will be converted to the 58 | * best matching PHP type before being passed to the PHP function. 59 | * 60 | * The declared functions can be called from SQL like: 61 | * 62 | * SELECT * FROM WHERE CALL_PHP1('soundex',) NOT LIKE ? 63 | * or 64 | * UPDATE
SET = CALL_PHP1('ucwords',) 65 | * 66 | * Additionally, there's a function 'exec_php' which allows the contents 67 | * of text BLOB fields to be parsed and executed by PHP. This is most useful 68 | * for declaring functions that can then be called with CALL_PHPx. 69 | * 70 | * DECLARE EXTERNAL FUNCTION EXEC_PHP 71 | * BLOB, 72 | * INTEGER BY DESCRIPTOR, 73 | * SMALLINT 74 | * RETURNS PARAMETER 2 75 | * ENTRY_POINT 'exec_php' MODULE_NAME 'php_ibase_udf' 76 | * 77 | * The function will return 1 if execution succeeded and 0 if an error 78 | * occurred. The result that is returned from the executed PHP code is 79 | * ignored. You can pass a non-zero value as second argument to force 80 | * the embedded PHP engine to re-initialise. 81 | * 82 | * There are several ways to build this library, depending on which way the 83 | * database is accessed. If you're using the classic server on a local 84 | * connection, you should compile the library like this: 85 | * 86 | * gcc -shared `php-config --includes` `php-config --ldflags` \ 87 | * `php-config --libs` -o php_ibase_udf.so php_ibase_udf.c 88 | * 89 | * If you connect to the classic server by TCP/IP, you should build the 90 | * PHP embedded static library and link against that. 91 | * 92 | * gcc -shared `php-config --includes` `php-config --ldflags` \ 93 | * `php-config --libs` -o php_ibase_udf.so php_ibase_udf.c \ 94 | * /usr/lib/libphp7.a 95 | * 96 | * If you use the super server, you should also link against the embedded 97 | * library, but be sure to enable thread safety, as the super server is 98 | * multi-threaded. After building, copy the resulting file to the folder 99 | * where your database expects to find its UDFs. 100 | */ 101 | 102 | #include "zend.h" 103 | #include "zend_API.h" 104 | 105 | #include "php.h" 106 | #include "php_ini.h" 107 | 108 | #include "ibase.h" 109 | 110 | #define min(a,b) ((a)<(b)?(a):(b)) 111 | 112 | #ifdef PHP_WIN32 113 | #define LL_LIT(lit) lit ## I64 114 | #else 115 | #define LL_LIT(lit) lit ## ll 116 | #endif 117 | 118 | #ifdef ZTS 119 | # include 120 | 121 | static void ***tsrm_ls; 122 | pthread_mutex_t mtx_res = PTHREAD_MUTEX_INITIALIZER; 123 | 124 | #define LOCK() do { pthread_mutex_lock(&mtx_res); } while (0) 125 | #define UNLOCK() do { pthread_mutex_unlock(&mtx_res); } while (0) 126 | #else 127 | #define LOCK() 128 | #define UNLOCK() 129 | #endif 130 | 131 | #ifdef PHP_EMBED 132 | # include "php_main.h" 133 | # include "sapi/embed/php_embed.h" 134 | 135 | static void __attribute__((constructor)) init() 136 | { 137 | php_embed_init(0, NULL P); 138 | } 139 | 140 | static void __attribute__((destructor)) fini() 141 | { 142 | php_embed_shutdown(); 143 | } 144 | 145 | #endif 146 | 147 | /** 148 | * Gets the contents of the BLOB b and offers it to Zend for parsing/execution 149 | */ 150 | void exec_php(BLOBCALLBACK b, PARAMDSC *res, ISC_SHORT *init) 151 | { 152 | int result, remaining = b->blob_total_length, i = 0; 153 | char *code = pemalloc(remaining+1, 1); 154 | ISC_USHORT read; 155 | 156 | for (code[remaining] = '\0'; remaining > 0; remaining -= read) 157 | b->blob_get_segment(b->blob_handle, &code[i++<<16],min(0x10000,remaining), &read); 158 | 159 | LOCK(); 160 | 161 | switch (init && *init) { 162 | 163 | default: 164 | #ifdef PHP_EMBED 165 | php_request_shutdown(NULL); 166 | if (FAILURE == (result = php_request_startup())) { 167 | break; 168 | } 169 | case 0: 170 | #endif 171 | /* feed it to the parser */ 172 | zend_first_try { 173 | result = zend_eval_stringl(code, b->blob_total_length, NULL, "Firebird Embedded PHP engine"); 174 | } zend_end_try(); 175 | } 176 | 177 | UNLOCK(); 178 | 179 | free(code); 180 | 181 | res->dsc_dtype = dtype_long; 182 | *(ISC_LONG*)res->dsc_address = (result == SUCCESS); 183 | } 184 | 185 | static ISC_INT64 const scales[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 100000000, 1000000000, 186 | 1000000000, LL_LIT(10000000000),LL_LIT(100000000000),LL_LIT(10000000000000),LL_LIT(100000000000000), 187 | LL_LIT(1000000000000000),LL_LIT(1000000000000000),LL_LIT(1000000000000000000) }; 188 | 189 | 190 | static void call_php(char *name, PARAMDSC *r, int argc, PARAMDSC **argv) 191 | { 192 | do { 193 | zval callback, args[4], return_value; 194 | PARAMVARY *res = (PARAMVARY*)r->dsc_address; 195 | int i; 196 | 197 | ZVAL_STRING(&callback, name); 198 | 199 | LOCK(); 200 | 201 | /* check if the requested function exists */ 202 | if (!zend_is_callable(&callback, 0, NULL)) { 203 | break; 204 | } 205 | 206 | UNLOCK(); 207 | 208 | /* create the argument array */ 209 | for (i = 0; i < argc; ++i) { 210 | 211 | /* test arg for null */ 212 | if (argv[i]->dsc_flags & DSC_null) { 213 | ZVAL_NULL(&args[i]); 214 | continue; 215 | } 216 | 217 | switch (argv[i]->dsc_dtype) { 218 | ISC_INT64 l; 219 | struct tm t; 220 | char const *fmt; 221 | char d[64]; 222 | 223 | case dtype_cstring: 224 | //??? 225 | ZVAL_STRING(&args[i], (char*)argv[i]->dsc_address); 226 | break; 227 | 228 | case dtype_text: 229 | //??? 230 | ZVAL_STRINGL(&args[i], (char*)argv[i]->dsc_address, argv[i]->dsc_length); 231 | break; 232 | 233 | case dtype_varying: 234 | //??? 235 | ZVAL_STRINGL(&args[i], ((PARAMVARY*)argv[i]->dsc_address)->vary_string, 236 | ((PARAMVARY*)argv[i]->dsc_address)->vary_length); 237 | break; 238 | 239 | case dtype_short: 240 | if (argv[i]->dsc_scale == 0) { 241 | ZVAL_LONG(&args[i], *(short*)argv[i]->dsc_address); 242 | } else { 243 | ZVAL_DOUBLE(&args[i], 244 | ((double)*(short*)argv[i]->dsc_address)/scales[-argv[i]->dsc_scale]); 245 | } 246 | break; 247 | 248 | case dtype_long: 249 | if (argv[i]->dsc_scale == 0) { 250 | ZVAL_LONG(&args[i], *(ISC_LONG*)argv[i]->dsc_address); 251 | } else { 252 | ZVAL_DOUBLE(&args[i], 253 | ((double)*(ISC_LONG*)argv[i]->dsc_address)/scales[-argv[i]->dsc_scale]); 254 | } 255 | break; 256 | 257 | case dtype_int64: 258 | l = *(ISC_INT64*)argv[i]->dsc_address; 259 | 260 | if (argv[i]->dsc_scale == 0 && l <= ZEND_LONG_MAX && l >= ZEND_LONG_MIN) { 261 | ZVAL_LONG(&args[i], (zend_long)l); 262 | } else { 263 | ZVAL_DOUBLE(&args[i], ((double)l)/scales[-argv[i]->dsc_scale]); 264 | } 265 | break; 266 | 267 | case dtype_real: 268 | ZVAL_DOUBLE(&args[i], *(float*)argv[i]->dsc_address); 269 | break; 270 | 271 | case dtype_double: 272 | ZVAL_DOUBLE(&args[i], *(double*)argv[i]->dsc_address); 273 | break; 274 | 275 | case dtype_sql_date: 276 | isc_decode_sql_date((ISC_DATE*)argv[i]->dsc_address, &t); 277 | ZVAL_STRINGL(&args[i], d, strftime(d, sizeof(d), INI_STR("ibase.dateformat"), &t),1); 278 | break; 279 | 280 | case dtype_sql_time: 281 | isc_decode_sql_time((ISC_TIME*)argv[i]->dsc_address, &t); 282 | ZVAL_STRINGL(&args[i], d, strftime(d, sizeof(d), INI_STR("ibase.timeformat"), &t),1); 283 | break; 284 | 285 | case dtype_timestamp: 286 | isc_decode_timestamp((ISC_TIMESTAMP*)argv[i]->dsc_address, &t); 287 | ZVAL_STRINGL(&args[i], d, strftime(d, sizeof(d), INI_STR("ibase.timestampformat"), &t)); 288 | break; 289 | } 290 | } 291 | 292 | LOCK(); 293 | 294 | /* now call the function */ 295 | if (FAILURE == call_user_function(NULL, NULL, 296 | &callback, &return_value, argc, args)) { 297 | UNLOCK(); 298 | break; 299 | } 300 | 301 | UNLOCK(); 302 | 303 | for (i = 0; i < argc; ++i) { 304 | switch (argv[i]->dsc_dtype) { 305 | case dtype_sql_date: 306 | case dtype_sql_time: 307 | case dtype_timestamp: 308 | zval_ptr_dtor_nogc(&args[i]); 309 | } 310 | } 311 | 312 | zval_ptr_dtor_str(&callback); 313 | 314 | /* return whatever type we got back from the callback: let DB handle conversion */ 315 | switch (Z_TYPE(return_value)) { 316 | 317 | case IS_LONG: 318 | r->dsc_dtype = dtype_long; 319 | *(zend_long*)r->dsc_address = Z_LVAL(return_value); 320 | r->dsc_length = sizeof(zend_long); 321 | break; 322 | 323 | case IS_DOUBLE: 324 | r->dsc_dtype = dtype_double; 325 | *(double*)r->dsc_address = Z_DVAL(return_value); 326 | r->dsc_length = sizeof(double); 327 | break; 328 | 329 | case IS_NULL: 330 | r->dsc_flags |= DSC_null; 331 | break; 332 | 333 | default: 334 | convert_to_string(&return_value); 335 | 336 | case IS_STRING: 337 | r->dsc_dtype = dtype_varying; 338 | memcpy(res->vary_string, Z_STRVAL(return_value), 339 | (res->vary_length = min(r->dsc_length-2,Z_STRLEN(return_value)))); 340 | r->dsc_length = res->vary_length+2; 341 | break; 342 | } 343 | 344 | zval_ptr_dtor(&return_value); 345 | 346 | return; 347 | 348 | } while (0); 349 | 350 | /** 351 | * If we end up here, we should report an error back to the DB engine, but 352 | * that's not possible. We can however report it back to PHP. 353 | */ 354 | LOCK(); 355 | php_error_docref(NULL, E_WARNING, "Error calling function '%s' from database", name); 356 | UNLOCK(); 357 | } 358 | 359 | 360 | /* Entry points for the DB engine */ 361 | 362 | void udf_call_php1(char *name, PARAMDSC *r, PARAMDSC *arg1) 363 | { 364 | PARAMDSC *args[1] = { arg1 }; 365 | call_php(name, r, 1, args); 366 | } 367 | 368 | void udf_call_php2(char *name, PARAMDSC *r, PARAMDSC *arg1, PARAMDSC *arg2) 369 | { 370 | PARAMDSC *args[2] = { arg1, arg2 }; 371 | call_php(name, r, 2, args); 372 | } 373 | 374 | void udf_call_php3(char *name, PARAMDSC *r, PARAMDSC *arg1, PARAMDSC *arg2, PARAMDSC *arg3) 375 | { 376 | PARAMDSC *args[3] = { arg1, arg2, arg3 }; 377 | call_php(name, r, 3, args); 378 | } 379 | 380 | void udf_call_php4(char *name, PARAMDSC *r, PARAMDSC *arg1, PARAMDSC *arg2, PARAMDSC *arg3, 381 | PARAMDSC *arg4) 382 | { 383 | PARAMDSC *args[4] = { arg1, arg2, arg3, arg4 }; 384 | call_php(name, r, 4, args); 385 | } 386 | 387 | void udf_call_php5(char *name, PARAMDSC *r, PARAMDSC *arg1, PARAMDSC *arg2, PARAMDSC *arg3, 388 | PARAMDSC *arg4, PARAMDSC *arg5) 389 | { 390 | PARAMDSC *args[5] = { arg1, arg2, arg3, arg4, arg5 }; 391 | call_php(name, r, 5, args); 392 | } 393 | 394 | void udf_call_php6(char *name, PARAMDSC *r, PARAMDSC *arg1, PARAMDSC *arg2, PARAMDSC *arg3, 395 | PARAMDSC *arg4, PARAMDSC *arg5, PARAMDSC *arg6) 396 | { 397 | PARAMDSC *args[6] = { arg1, arg2, arg3, arg4, arg5, arg6 }; 398 | call_php(name, r, 6, args); 399 | } 400 | 401 | void udf_call_php7(char *name, PARAMDSC *r, PARAMDSC *arg1, PARAMDSC *arg2, PARAMDSC *arg3, 402 | PARAMDSC *arg4, PARAMDSC *arg5, PARAMDSC *arg6, PARAMDSC *arg7) 403 | { 404 | PARAMDSC *args[7] = { arg1, arg2, arg3, arg4, arg5, arg6, arg7 }; 405 | call_php(name, r, 7, args); 406 | } 407 | 408 | void udf_call_php8(char *name, PARAMDSC *r, PARAMDSC *arg1, PARAMDSC *arg2, PARAMDSC *arg3, 409 | PARAMDSC *arg4, PARAMDSC *arg5, PARAMDSC *arg6, PARAMDSC *arg7, PARAMDSC *arg8) 410 | { 411 | PARAMDSC *args[8] = { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 }; 412 | call_php(name, r, 8, args); 413 | } 414 | -------------------------------------------------------------------------------- /ibase_blobs.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 7, 8 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 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 | | Authors: Jouni Ahto | 16 | | Andrew Avdeev | 17 | | Ard Biesheuvel | 18 | | Martin Koeditz | 19 | | others | 20 | +----------------------------------------------------------------------+ 21 | | You'll find history on Github | 22 | | https://github.com/FirebirdSQL/php-firebird/commits/master | 23 | +----------------------------------------------------------------------+ 24 | */ 25 | 26 | #ifdef HAVE_CONFIG_H 27 | #include "config.h" 28 | #endif 29 | 30 | #include "php.h" 31 | 32 | #if HAVE_IBASE 33 | 34 | #include "php_interbase.h" 35 | #include "php_ibase_includes.h" 36 | 37 | #define BLOB_CLOSE 1 38 | #define BLOB_CANCEL 2 39 | 40 | #define PARSE_PARAMETERS \ 41 | switch (ZEND_NUM_ARGS()) { \ 42 | default: \ 43 | WRONG_PARAM_COUNT; \ 44 | case 1: \ 45 | if (FAILURE == zend_parse_parameters(1, "s", &blob_id, &blob_id_len)) { \ 46 | RETURN_FALSE; \ 47 | } \ 48 | break; \ 49 | case 2: \ 50 | if (FAILURE == zend_parse_parameters(2, "rs", &link, &blob_id, &blob_id_len)) { \ 51 | RETURN_FALSE; \ 52 | } \ 53 | break; \ 54 | } \ 55 | 56 | static int le_blob; 57 | 58 | static void _php_ibase_free_blob(zend_resource *rsrc) /* {{{ */ 59 | { 60 | ibase_blob *ib_blob = (ibase_blob *)rsrc->ptr; 61 | 62 | if (ib_blob->bl_handle != 0) { /* blob open*/ 63 | if (isc_cancel_blob(IB_STATUS, &ib_blob->bl_handle)) { 64 | _php_ibase_module_error("You can lose data. Close any blob after reading from or " 65 | "writing to it. Use ibase_blob_close() before calling ibase_close()"); 66 | } 67 | } 68 | efree(ib_blob); 69 | } 70 | /* }}} */ 71 | 72 | void php_ibase_blobs_minit(INIT_FUNC_ARGS) /* {{{ */ 73 | { 74 | le_blob = zend_register_list_destructors_ex(_php_ibase_free_blob, NULL, 75 | LE_BLOB, module_number); 76 | } 77 | /* }}} */ 78 | 79 | int _php_ibase_string_to_quad(char const *id, ISC_QUAD *qd) /* {{{ */ 80 | { 81 | /* shortcut for most common case */ 82 | if (sizeof(ISC_QUAD) == sizeof(ISC_UINT64)) { 83 | return sscanf(id, BLOB_ID_MASK, (ISC_UINT64 *) qd); 84 | } else { 85 | ISC_UINT64 res; 86 | if (sscanf(id, BLOB_ID_MASK, &res)) { 87 | qd->gds_quad_high = (ISC_LONG) (res >> 0x20); 88 | qd->gds_quad_low = (ISC_LONG) (res & 0xFFFFFFFF); 89 | return 1; 90 | } 91 | return 0; 92 | } 93 | } 94 | /* }}} */ 95 | 96 | zend_string *_php_ibase_quad_to_string(ISC_QUAD const qd) /* {{{ */ 97 | { 98 | /* shortcut for most common case */ 99 | if (sizeof(ISC_QUAD) == sizeof(ISC_UINT64)) { 100 | return strpprintf(BLOB_ID_LEN+1, "0x%0*" LL_MASK "x", 16, *(ISC_UINT64*)(void *) &qd); 101 | } else { 102 | ISC_UINT64 res = ((ISC_UINT64) qd.gds_quad_high << 0x20) | qd.gds_quad_low; 103 | return strpprintf(BLOB_ID_LEN+1, "0x%0*" LL_MASK "x", 16, res); 104 | } 105 | } 106 | /* }}} */ 107 | 108 | typedef struct { /* {{{ */ 109 | ISC_LONG max_segment; /* Length of longest segment */ 110 | ISC_LONG num_segments; /* Total number of segments */ 111 | ISC_LONG total_length; /* Total length of blob */ 112 | int bl_stream; /* blob is stream ? */ 113 | /* }}} */ 114 | } IBASE_BLOBINFO; 115 | 116 | int _php_ibase_blob_get(zval *return_value, ibase_blob *ib_blob, zend_ulong max_len) /* {{{ */ 117 | { 118 | if (ib_blob->bl_qd.gds_quad_high || ib_blob->bl_qd.gds_quad_low) { /*not null ?*/ 119 | 120 | ISC_STATUS stat; 121 | zend_string *bl_data; 122 | zend_ulong cur_len; 123 | unsigned short seg_len; 124 | 125 | bl_data = zend_string_safe_alloc(1, max_len, 0, 0); 126 | 127 | for (cur_len = stat = 0; (stat == 0 || stat == isc_segment) && cur_len < max_len; cur_len += seg_len) { 128 | 129 | unsigned short chunk_size = (max_len-cur_len) > USHRT_MAX ? USHRT_MAX 130 | : (unsigned short)(max_len-cur_len); 131 | 132 | stat = isc_get_segment(IB_STATUS, &ib_blob->bl_handle, &seg_len, chunk_size, &ZSTR_VAL(bl_data)[cur_len]); 133 | } 134 | 135 | if (IB_STATUS[0] == 1 && (stat != 0 && stat != isc_segstr_eof && stat != isc_segment)) { 136 | zend_string_free(bl_data); 137 | _php_ibase_error(); 138 | return FAILURE; 139 | } 140 | ZSTR_VAL(bl_data)[cur_len] = '\0'; 141 | ZSTR_LEN(bl_data) = cur_len; 142 | RETVAL_NEW_STR(bl_data); 143 | } else { /* null blob */ 144 | RETVAL_EMPTY_STRING(); /* empty string */ 145 | } 146 | return SUCCESS; 147 | } 148 | /* }}} */ 149 | 150 | int _php_ibase_blob_add(zval *string_arg, ibase_blob *ib_blob) /* {{{ */ 151 | { 152 | zend_ulong put_cnt = 0, rem_cnt; 153 | unsigned short chunk_size; 154 | 155 | convert_to_string_ex(string_arg); 156 | 157 | for (rem_cnt = Z_STRLEN_P(string_arg); rem_cnt > 0; rem_cnt -= chunk_size) { 158 | 159 | chunk_size = rem_cnt > USHRT_MAX ? USHRT_MAX : (unsigned short)rem_cnt; 160 | 161 | if (isc_put_segment(IB_STATUS, &ib_blob->bl_handle, chunk_size, &Z_STRVAL_P(string_arg)[put_cnt] )) { 162 | _php_ibase_error(); 163 | return FAILURE; 164 | } 165 | put_cnt += chunk_size; 166 | } 167 | return SUCCESS; 168 | } 169 | /* }}} */ 170 | 171 | static int _php_ibase_blob_info(isc_blob_handle bl_handle, IBASE_BLOBINFO *bl_info) /* {{{ */ 172 | { 173 | static char bl_items[] = { 174 | isc_info_blob_num_segments, 175 | isc_info_blob_max_segment, 176 | isc_info_blob_total_length, 177 | isc_info_blob_type 178 | }; 179 | 180 | char bl_inf[sizeof(zend_long)*8], *p; 181 | 182 | bl_info->max_segment = 0; 183 | bl_info->num_segments = 0; 184 | bl_info->total_length = 0; 185 | bl_info->bl_stream = 0; 186 | 187 | if (isc_blob_info(IB_STATUS, &bl_handle, sizeof(bl_items), bl_items, sizeof(bl_inf), bl_inf)) { 188 | _php_ibase_error(); 189 | return FAILURE; 190 | } 191 | 192 | for (p = bl_inf; *p != isc_info_end && p < bl_inf + sizeof(bl_inf);) { 193 | unsigned short item_len; 194 | int item = *p++; 195 | 196 | item_len = (short) isc_vax_integer(p, 2); 197 | p += 2; 198 | switch (item) { 199 | case isc_info_blob_num_segments: 200 | bl_info->num_segments = isc_vax_integer(p, item_len); 201 | break; 202 | case isc_info_blob_max_segment: 203 | bl_info->max_segment = isc_vax_integer(p, item_len); 204 | break; 205 | case isc_info_blob_total_length: 206 | bl_info->total_length = isc_vax_integer(p, item_len); 207 | break; 208 | case isc_info_blob_type: 209 | bl_info->bl_stream = isc_vax_integer(p, item_len); 210 | break; 211 | case isc_info_end: 212 | break; 213 | case isc_info_truncated: 214 | case isc_info_error: /* hmm. don't think so...*/ 215 | _php_ibase_module_error("PHP module internal error"); 216 | return FAILURE; 217 | } /* switch */ 218 | p += item_len; 219 | } /* for */ 220 | return SUCCESS; 221 | } 222 | /* }}} */ 223 | 224 | /* {{{ proto resource ibase_blob_create([resource link_identifier]) 225 | Create blob for adding data */ 226 | PHP_FUNCTION(ibase_blob_create) 227 | { 228 | zval *link = NULL; 229 | ibase_db_link *ib_link; 230 | ibase_trans *trans = NULL; 231 | ibase_blob *ib_blob; 232 | 233 | RESET_ERRMSG; 234 | 235 | if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|r", &link)) { 236 | RETURN_FALSE; 237 | } 238 | 239 | PHP_IBASE_LINK_TRANS(link, ib_link, trans); 240 | 241 | ib_blob = (ibase_blob *) emalloc(sizeof(ibase_blob)); 242 | ib_blob->bl_handle = 0; 243 | ib_blob->type = BLOB_INPUT; 244 | 245 | if (isc_create_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob->bl_handle, &ib_blob->bl_qd)) { 246 | _php_ibase_error(); 247 | efree(ib_blob); 248 | RETURN_FALSE; 249 | } 250 | 251 | RETVAL_RES(zend_register_resource(ib_blob, le_blob)); 252 | Z_TRY_ADDREF_P(return_value); 253 | } 254 | /* }}} */ 255 | 256 | /* {{{ proto resource ibase_blob_open([ resource link_identifier, ] string blob_id) 257 | Open blob for retrieving data parts */ 258 | PHP_FUNCTION(ibase_blob_open) 259 | { 260 | char *blob_id; 261 | size_t blob_id_len; 262 | zval *link = NULL; 263 | ibase_db_link *ib_link; 264 | ibase_trans *trans = NULL; 265 | ibase_blob *ib_blob; 266 | 267 | RESET_ERRMSG; 268 | PARSE_PARAMETERS; 269 | 270 | PHP_IBASE_LINK_TRANS(link, ib_link, trans); 271 | 272 | ib_blob = (ibase_blob *) emalloc(sizeof(ibase_blob)); 273 | ib_blob->bl_handle = 0; 274 | ib_blob->type = BLOB_OUTPUT; 275 | 276 | do { 277 | if (! _php_ibase_string_to_quad(blob_id, &ib_blob->bl_qd)) { 278 | _php_ibase_module_error("String is not a BLOB ID"); 279 | break; 280 | } 281 | 282 | if (isc_open_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob->bl_handle, 283 | &ib_blob->bl_qd)) { 284 | _php_ibase_error(); 285 | break; 286 | } 287 | 288 | RETVAL_RES(zend_register_resource(ib_blob, le_blob)); 289 | Z_TRY_ADDREF_P(return_value); 290 | return; 291 | 292 | } while (0); 293 | 294 | efree(ib_blob); 295 | RETURN_FALSE; 296 | } 297 | /* }}} */ 298 | 299 | /* {{{ proto bool ibase_blob_add(resource blob_handle, string data) 300 | Add data into created blob */ 301 | PHP_FUNCTION(ibase_blob_add) 302 | { 303 | zval *blob_arg, *string_arg; 304 | ibase_blob *ib_blob; 305 | 306 | RESET_ERRMSG; 307 | 308 | if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "rz", &blob_arg, &string_arg)) { 309 | return; 310 | } 311 | 312 | ib_blob = (ibase_blob *)zend_fetch_resource_ex(blob_arg, "Interbase blob", le_blob); 313 | 314 | if (ib_blob->type != BLOB_INPUT) { 315 | _php_ibase_module_error("BLOB is not open for input"); 316 | RETURN_FALSE; 317 | } 318 | 319 | if (_php_ibase_blob_add(string_arg, ib_blob) != SUCCESS) { 320 | RETURN_FALSE; 321 | } 322 | } 323 | /* }}} */ 324 | 325 | /* {{{ proto string ibase_blob_get(resource blob_handle, int len) 326 | Get len bytes data from open blob */ 327 | PHP_FUNCTION(ibase_blob_get) 328 | { 329 | zval *blob_arg; 330 | zend_ulong len_arg; 331 | ibase_blob *ib_blob; 332 | 333 | RESET_ERRMSG; 334 | 335 | if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &blob_arg, &len_arg)) { 336 | return; 337 | } 338 | 339 | ib_blob = (ibase_blob *)zend_fetch_resource_ex(blob_arg, "Interbase blob", le_blob); 340 | 341 | if (ib_blob->type != BLOB_OUTPUT) { 342 | _php_ibase_module_error("BLOB is not open for output"); 343 | RETURN_FALSE; 344 | } 345 | 346 | if (_php_ibase_blob_get(return_value, ib_blob, len_arg) != SUCCESS) { 347 | RETURN_FALSE; 348 | } 349 | } 350 | /* }}} */ 351 | 352 | static void _php_ibase_blob_end(INTERNAL_FUNCTION_PARAMETERS, int bl_end) /* {{{ */ 353 | { 354 | zval *blob_arg; 355 | ibase_blob *ib_blob; 356 | 357 | RESET_ERRMSG; 358 | 359 | if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "r", &blob_arg)) { 360 | return; 361 | } 362 | 363 | ib_blob = (ibase_blob *)zend_fetch_resource_ex(blob_arg, "Interbase blob", le_blob); 364 | 365 | if (bl_end == BLOB_CLOSE) { /* return id here */ 366 | 367 | if (ib_blob->bl_qd.gds_quad_high || ib_blob->bl_qd.gds_quad_low) { /*not null ?*/ 368 | if (isc_close_blob(IB_STATUS, &ib_blob->bl_handle)) { 369 | _php_ibase_error(); 370 | RETURN_FALSE; 371 | } 372 | } 373 | ib_blob->bl_handle = 0; 374 | 375 | RETVAL_NEW_STR(_php_ibase_quad_to_string(ib_blob->bl_qd)); 376 | } else { /* discard created blob */ 377 | if (isc_cancel_blob(IB_STATUS, &ib_blob->bl_handle)) { 378 | _php_ibase_error(); 379 | RETURN_FALSE; 380 | } 381 | ib_blob->bl_handle = 0; 382 | RETVAL_TRUE; 383 | } 384 | zend_list_delete(Z_RES_P(blob_arg)); 385 | } 386 | /* }}} */ 387 | 388 | /* {{{ proto string ibase_blob_close(resource blob_handle) 389 | Close blob */ 390 | PHP_FUNCTION(ibase_blob_close) 391 | { 392 | _php_ibase_blob_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, BLOB_CLOSE); 393 | } 394 | /* }}} */ 395 | 396 | /* {{{ proto bool ibase_blob_cancel(resource blob_handle) 397 | Cancel creating blob */ 398 | PHP_FUNCTION(ibase_blob_cancel) 399 | { 400 | _php_ibase_blob_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, BLOB_CANCEL); 401 | } 402 | /* }}} */ 403 | 404 | /* {{{ proto array ibase_blob_info([ resource link_identifier, ] string blob_id) 405 | Return blob length and other useful info */ 406 | PHP_FUNCTION(ibase_blob_info) 407 | { 408 | char *blob_id; 409 | size_t blob_id_len; 410 | zval *link = NULL; 411 | ibase_db_link *ib_link; 412 | ibase_trans *trans = NULL; 413 | ibase_blob ib_blob = { 0, BLOB_INPUT }; 414 | IBASE_BLOBINFO bl_info; 415 | 416 | RESET_ERRMSG; 417 | PARSE_PARAMETERS; 418 | 419 | PHP_IBASE_LINK_TRANS(link, ib_link, trans); 420 | 421 | if (! _php_ibase_string_to_quad(blob_id, &ib_blob.bl_qd)) { 422 | _php_ibase_module_error("Unrecognized BLOB ID"); 423 | RETURN_FALSE; 424 | } 425 | 426 | if (ib_blob.bl_qd.gds_quad_high || ib_blob.bl_qd.gds_quad_low) { /* not null ? */ 427 | if (isc_open_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob.bl_handle, 428 | &ib_blob.bl_qd)) { 429 | _php_ibase_error(); 430 | RETURN_FALSE; 431 | } 432 | 433 | if (_php_ibase_blob_info(ib_blob.bl_handle, &bl_info)) { 434 | RETURN_FALSE; 435 | } 436 | if (isc_close_blob(IB_STATUS, &ib_blob.bl_handle)) { 437 | _php_ibase_error(); 438 | RETURN_FALSE; 439 | } 440 | } else { /* null blob, all values to zero */ 441 | bl_info.max_segment = 0; 442 | bl_info.num_segments = 0; 443 | bl_info.total_length = 0; 444 | bl_info.bl_stream = 0; 445 | } 446 | 447 | array_init(return_value); 448 | 449 | add_index_long(return_value, 0, bl_info.total_length); 450 | add_assoc_long(return_value, "length", bl_info.total_length); 451 | 452 | add_index_long(return_value, 1, bl_info.num_segments); 453 | add_assoc_long(return_value, "numseg", bl_info.num_segments); 454 | 455 | add_index_long(return_value, 2, bl_info.max_segment); 456 | add_assoc_long(return_value, "maxseg", bl_info.max_segment); 457 | 458 | add_index_bool(return_value, 3, bl_info.bl_stream); 459 | add_assoc_bool(return_value, "stream", bl_info.bl_stream); 460 | 461 | add_index_bool(return_value, 4, (!ib_blob.bl_qd.gds_quad_high && !ib_blob.bl_qd.gds_quad_low)); 462 | add_assoc_bool(return_value, "isnull", (!ib_blob.bl_qd.gds_quad_high && !ib_blob.bl_qd.gds_quad_low)); 463 | } 464 | /* }}} */ 465 | 466 | /* {{{ proto bool ibase_blob_echo([ resource link_identifier, ] string blob_id) 467 | Output blob contents to browser */ 468 | PHP_FUNCTION(ibase_blob_echo) 469 | { 470 | char *blob_id; 471 | size_t blob_id_len; 472 | zval *link = NULL; 473 | ibase_db_link *ib_link; 474 | ibase_trans *trans = NULL; 475 | ibase_blob ib_blob_id = { 0, BLOB_OUTPUT }; 476 | char bl_data[IBASE_BLOB_SEG]; 477 | unsigned short seg_len; 478 | 479 | RESET_ERRMSG; 480 | PARSE_PARAMETERS; 481 | 482 | PHP_IBASE_LINK_TRANS(link, ib_link, trans); 483 | 484 | if (! _php_ibase_string_to_quad(blob_id, &ib_blob_id.bl_qd)) { 485 | _php_ibase_module_error("Unrecognized BLOB ID"); 486 | RETURN_FALSE; 487 | } 488 | 489 | do { 490 | if (isc_open_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob_id.bl_handle, 491 | &ib_blob_id.bl_qd)) { 492 | break; 493 | } 494 | 495 | while (!isc_get_segment(IB_STATUS, &ib_blob_id.bl_handle, &seg_len, sizeof(bl_data), bl_data) 496 | || IB_STATUS[1] == isc_segment) { 497 | PHPWRITE(bl_data, seg_len); 498 | } 499 | 500 | if (IB_STATUS[0] && (IB_STATUS[1] != isc_segstr_eof)) { 501 | break; 502 | } 503 | 504 | if (isc_close_blob(IB_STATUS, &ib_blob_id.bl_handle)) { 505 | break; 506 | } 507 | RETURN_TRUE; 508 | } while (0); 509 | 510 | _php_ibase_error(); 511 | RETURN_FALSE; 512 | } 513 | /* }}} */ 514 | 515 | /* {{{ proto string ibase_blob_import([ resource link_identifier, ] resource file) 516 | Create blob, copy file in it, and close it */ 517 | PHP_FUNCTION(ibase_blob_import) 518 | { 519 | zval *link = NULL, *file; 520 | int size; 521 | unsigned short b; 522 | ibase_blob ib_blob = { 0, 0 }; 523 | ibase_db_link *ib_link; 524 | ibase_trans *trans = NULL; 525 | char bl_data[IBASE_BLOB_SEG]; 526 | php_stream *stream; 527 | 528 | RESET_ERRMSG; 529 | 530 | if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "r|r", 531 | (ZEND_NUM_ARGS()-1) ? &link : &file, &file)) { 532 | RETURN_FALSE; 533 | } 534 | 535 | PHP_IBASE_LINK_TRANS(link, ib_link, trans); 536 | 537 | php_stream_from_zval(stream, file); 538 | 539 | do { 540 | if (isc_create_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob.bl_handle, 541 | &ib_blob.bl_qd)) { 542 | break; 543 | } 544 | 545 | for (size = 0; (b = php_stream_read(stream, bl_data, sizeof(bl_data))); size += b) { 546 | if (isc_put_segment(IB_STATUS, &ib_blob.bl_handle, b, bl_data)) { 547 | break; 548 | } 549 | } 550 | 551 | if (isc_close_blob(IB_STATUS, &ib_blob.bl_handle)) { 552 | break; 553 | } 554 | RETURN_NEW_STR(_php_ibase_quad_to_string(ib_blob.bl_qd)); 555 | } while (0); 556 | 557 | _php_ibase_error(); 558 | RETURN_FALSE; 559 | } 560 | /* }}} */ 561 | 562 | #endif /* HAVE_IBASE */ 563 | --------------------------------------------------------------------------------