Skip to content

FEATURE: Complete M3 migration — return objects from fbird_prepare() and fbird_prepare_ex() #297

Description

@satwareAG-ironMike

Description

The v11.0.0 M3 migration converted most fbird_* functions to return opaque Firebird\* objects instead of raw resources. However, fbird_prepare() and fbird_prepare_ex() were not migrated — they still return raw resource handles.

This is the last remaining resource/object duality in the extension. Completing the migration would:

  • Eliminate all is_resource() / get_resource_type() checks in consuming code
  • Make the type system consistent (every handle-returning function returns an object)
  • Align with PHP's long-term resource-to-object migration trajectory

Current State

Functions that return objects (M3 complete) ✅

Function Returns
fbird_connect() / fbird_pconnect() / fbird_create_database() Firebird\Connection
fbird_trans() / fbird_trans_start() Firebird\Transaction
fbird_execute() Firebird\ResultSet (SELECT)
fbird_blob_create() / fbird_blob_open() / fbird_blob_create_seekable() / fbird_blob_open_seekable() Firebird\Blob
fbird_service_attach() Firebird\Service
fbird_set_event_handler() Firebird\Event

Functions that still return resources (M3 incomplete) ❌

Function Returns Stub C code
fbird_prepare() resource|false : mixed (@return resource|false) fbird_query_exec.c:1319RETVAL_RES(ib_query->res)
fbird_prepare_ex() resource|false : mixed (@return resource|false) fbird_query_exec.c:1377RETVAL_RES(ib_query->res)

Related: functions that return resources for SELECT results (see issue #296)

Function Returns C code
fbird_query() resource (stub says \Firebird\ResultSet) fbird_query_exec.c:509,677,877RETVAL_RES()
fbird_execute_query() resource|false Same pattern
fbird_execute_auto() resource|int|false Same pattern
fbird_query_params_tx() resource|int|false Same pattern

The Firebird\Statement Class Already Exists

The Firebird\Statement class was registered as a skeleton class entry in v8.0.0 (M3 Phase A+B). It has a private constructor and is intended to be returned by fbird_prepare() — the same pattern used for Firebird\Transaction (returned by fbird_trans() since M3 Phase D).

The infrastructure is already in place:

  • fbird_statement_ce class entry (registered in fbird_register_classes(), fbird_classes.c)
  • fbird_resultset_ce class entry (same)
  • fbird_setup_resultset_object() helper (already used by fbird_execute())
  • A fbird_setup_statement_object() helper would follow the same pattern

Suggested Implementation

1. fbird_prepare() and fbird_prepare_ex()

Replace RETVAL_RES(ib_query->res) with an object-wrapping call, following the same pattern as fbird_setup_resultset_object():

// New helper in fbird_classes.c (same pattern as fbird_setup_resultset_object):
void fbird_setup_statement_object(zval *return_value, zend_resource *res)
{
    zval_ptr_dtor(return_value);
    object_init_ex(return_value, fbird_statement_ce);
    fbird_statement_obj *intern = fbird_statement_from_obj(Z_OBJ_P(return_value));
    GC_ADDREF(res);
    intern->query_res = res;
}

// In PHP_FUNCTION(fbird_prepare) and PHP_FUNCTION(fbird_prepare_ex):
// Replace:
//   RETVAL_RES(ib_query->res);
//   Z_TRY_ADDREF_P(return_value);
// With:
//   zend_resource *_res = ib_query->res;
//   Z_TRY_ADDREF_P(&_res);  // keep alive for object
//   fbird_setup_statement_object(return_value, _res);

2. fbird_query(), fbird_execute_query(), fbird_query_params_tx(), fbird_execute_auto()

Add the fbird_setup_resultset_object() wrapping block (already used by fbird_execute()) at the end of each function. See issue #296 for the exact code block and insertion points.

3. Update stubs

// Current (stubs/firebird-stubs.php)
function fbird_prepare(...): mixed {}  // @return resource|false
function fbird_prepare_ex(...): mixed {}  // @return resource|false

// After migration
function fbird_prepare(...): \Firebird\Statement|false {}
function fbird_prepare_ex(...): \Firebird\Statement|false {}

4. Dual-accept bridge

All consuming functions (fbird_execute(), fbird_free_query(), fbird_fetch_row(), etc.) already accept both resources and Firebird\ResultSet/Firebird\Statement objects via the dual-accept bridge (M3 Phase E/F). No changes needed on the consuming side.

Impact on doctrine-firebird-driver

Completing the migration would allow the driver to:

Current code After migration
Statement::isStatementValid()!== null && !== false (handles resource+object+false) instanceof \Firebird\Statement
Result::isResultValid()is_object() (broad, can't use instanceof) instanceof \Firebird\ResultSet
All remaining is_resource() calls Eliminated entirely
@param resource|\Firebird\Transaction|false|null @param \Firebird\Statement|\Firebird\Transaction|false|null

Net effect: the driver would have zero resource-type checks — a fully object-oriented API surface.

Environment

  • php-firebird v11.0.1
  • Firebird\Statement class registered since v8.0.0
  • Dual-accept bridge in place since v11.0.0 (Phase E/F)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions