Skip to content

Instantly share code, notes, and snippets.

@mariuz
Last active December 4, 2025 15:10
Show Gist options
  • Select an option

  • Save mariuz/12bedde90aad26bb38ca1fa324103e87 to your computer and use it in GitHub Desktop.

Select an option

Save mariuz/12bedde90aad26bb38ca1fa324103e87 to your computer and use it in GitHub Desktop.
PHP Firebird — Compatibility Guide Versions covered: 5.0.2 → 6.1.1-RC.2

PHP Firebird — Compatibility Guide

Versions covered: 5.0.2 → 6.1.1-RC.2
Purpose: one-page upgrade & compatibility reference with short migration examples you can share with your team.


Quick summary (why read this)

Between 5.0.2 and 6.1.1-RC.2 the extension added Firebird 4/5 type support, new transaction/INI options, client-version helpers, and changed some runtime behaviors that may break existing code:

  • INT128 values are represented as strings (5.0.2).
  • New IBASE_LOCK_TIMEOUT + INI options to control lock timeouts (6.1.1-RC.0).
  • Extension now supports long metadata (names) and more Firebird types (6.1.1-RC.0).
  • ibase_close() behavior changed: now closes the connection immediately instead of decrementing a refcount (6.1.1-RC.2).
  • Fetching TIME fields with IBASE_UNIXTIME now returns a time string instead of negative numeric values (6.1.1-RC.2).
  • New helpers: ibase_get_client_version(), ibase_get_client_major_version(), ibase_get_client_minor_version().

1) ibase_close() — migration notes

Change: ibase_close($link) now closes the DB connection immediately (no internal refcount decrement behavior). Code that relied on implicit sharing / refcounting may now see connections closed earlier.

Recommended approaches:

  • Do not call ibase_close() in code paths where other parts of the application still rely on the same connection — only close when you truly want the connection gone.
  • Use persistent connections where appropriate (if your workload fits persistent connections).
  • Explicit connection-lifetime management: open, use, close within the same scope or use wrappers.

Examples

Old (implicitly relied on refcount):

function querySomething() {
    $link = $GLOBALS['FB_LINK']; // shared global
    // do queries...
    ibase_close($link); // previously might have just decreased refcount
}

New — Option A: do not close shared connections (recommended for shared/global link):

// initialize once at bootstrap
$GLOBALS['FB_LINK'] = ibase_connect($dsn, $user, $pass);

// when using connection, DON'T call ibase_close() if other code still needs it
function querySomething() {
    $link = $GLOBALS['FB_LINK'];
    // do queries...
    // DO NOT call ibase_close($link) here
}

New — Option B: explicit scoped usage (close when done):

function withConnection(callable $fn) {
    $link = ibase_connect($dsn, $user, $pass);
    try {
        return $fn($link);
    } finally {
        ibase_close($link); // safe: connection used only here
    }
}

// usage:
withConnection(function($link) {
    // do work, will be closed automatically afterwards
});

New — Option C: persistent connection (if appropriate):

$link = ibase_pconnect($dsn, $user, $pass);
// don't call ibase_close() on a persistent connection

Checklist:

  • Audit places where ibase_close() is called on shared/global links. Remove or relocate close calls.
  • Prefer explicit lifetime scoping or persistent connections as needed.

2) TIME fields + IBASE_UNIXTIME — migration snippet

Change: fetching TIME fields with the IBASE_UNIXTIME flag may now return a string (e.g., "HH:MM:SS" or with fractional seconds) instead of earlier numeric/negative values.

Compatibility helper: Accept both numeric and string results.

function ibase_time_to_seconds($value) {
    // If old numeric behavior (seconds)
    if (is_int($value) || is_float($value)) {
        return (int)$value;
    }

    // If new string behavior "HH:MM:SS" or "HH:MM:SS.sss"
    if (is_string($value)) {
        // Try to parse "HH:MM:SS" (allow fractional seconds)
        if (preg_match('/^(-?\d+):(\d+):(\d+(?:\.\d+)?)$/', $value, $m)) {
            $hours = (int)$m[1];
            $minutes = (int)$m[2];
            $seconds = (float)$m[3];
            return (int)($hours * 3600 + $minutes * 60 + floor($seconds));
        }

        // fallback: try DateTime parsing (assumes value is a time-of-day)
        $dt = DateTime::createFromFormat('H:i:s', $value);
        if ($dt !== false) {
            $midnight = DateTime::createFromFormat('Y-m-d H:i:s', $dt->format('Y-m-d') . ' 00:00:00');
            return (int)$dt->getTimestamp() - (int)$midnight->getTimestamp();
        }
    }

    // unknown format
    return null;
}

Action items:

  • Update code that assumed numeric (possibly negative) values — use the helper above or adapt parsing.
  • Add unit tests for TIME fields to ensure both legacy numeric and new string cases are handled.

3) INT128 (Firebird 4/5) — migration pattern

Change: INT128 values returned by the extension are converted to strings because PHP doesn't have a native 128-bit integer.

Best practices:

  • Treat INT128 columns as strings in PHP.
  • If you need arithmetic on INT128, use arbitrary-precision libraries: GMP or BCMath.

Example using GMP (recommended for integer math):

$int128str = ibase_query_value(...); // returns string like "170141183460469231731687303715884105727"
if (extension_loaded('gmp')) {
    $g = gmp_init($int128str);
    $two = gmp_init('2');
    $sum = gmp_add($g, $two);
    echo gmp_strval($sum);
} elseif (extension_loaded('bcmath')) {
    // bc* functions for decimal math
    $sum = bcadd($int128str, '2');
} else {
    // fallback: keep as string; or add dependency to gmp/bcmath
}

Checklist:

  • Replace any (int) casts or numeric expectations for INT128 columns.
  • Add tests, and consider adding a runtime check to detect large-int columns and choose an appropriate library.

4) New client-version helpers — runtime checks

Use these to pick code paths depending on fbclient version / extension capability:

$full = ibase_get_client_version(); // e.g. "4.0.0" or string the function returns
$major = ibase_get_client_major_version(); // int
$minor = ibase_get_client_minor_version(); // int

if ($major >= 4) {
    // enable Firebird 4/5 type handling
}

Action:

  • Add initialization-time checks and warnings/logs when fbclient is not new enough for expected features.

5) IBASE_LOCK_TIMEOUT & INI options

New INI options (example usage):

// set in php.ini or runtime (runtime only affects subsequent operations)
ini_set("ibase.default_trans_params", IBASE_READ | IBASE_NOWAIT);
ini_set("ibase.default_lock_timeout", "30"); // seconds

Notes:

  • The lock timeout option is used when you include IBASE_LOCK_TIMEOUT in transaction params.
  • Ensure fbclient supports these options where you need them.

Test:

  • Add tests that start transactions with lock timeout and verify expected wait/timeout behavior under contention.

6) Long metadata / names

Change: support for longer table and field names.

Impact:

  • Generally improves compatibility if you previously had truncated names.
  • Check code that assumed fixed/short name lengths (e.g., substrings) and update if needed.

Recommended testing checklist

  • Functional tests for all queries that use:
    • INT128 columns (read/write/compare)
    • TIME/TIMESTAMP fields (with and without IBASE_UNIXTIME)
    • Transactions with lock contention, verifying IBASE_LOCK_TIMEOUT behavior
    • Connection lifecycle tests (shared connection, closing in different places)
  • Integration tests to verify:
    • Code paths that call ibase_close() still behave correctly after the change
    • Behavior when fbclient is an older version (fallbacks)
  • Add runtime telemetry or warnings in bootstrap if ibase_get_client_major_version() < expected.

Quick rollout plan

  1. Update dev/staging with php-firebird 6.1.1-RC.2 (or latest RC) and updated fbclient (4/5 if you want Firebird 4/5 features).
  2. Run the tests in the checklist; fix parsing/close-call issues flagged by tests.
  3. Deploy to canary environment, monitor connection/transaction behavior and time parsing.
  4. Full rollout.

Notes / references

  • RC releases are pre-release; maintainers recommend thorough testing before production.
  • If your codebase is large, run an automated scan for:
    • Calls to ibase_close(
    • Direct numeric casts on result columns that may be INT128
    • Usage of IBASE_UNIXTIME and code that assumes numeric output
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment