Skip to content

3.1.5

Latest

Choose a tag to compare

@Falseclock Falseclock released this 18 Sep 06:11

dbd-php 3.1.5

Backward-compatible patch release. No public API or query execution behavior changed; PHP requirement (>=8.0) and Composer constraints unchanged. Every fix ships with a regression test that failed on 3.1.4.

Fixes

1. DBD\Cache\MemCache is loadable with psr/simple-cache ^3.0

  • Bug. 3.1.4 raised the requirement to psr/simple-cache ^3.0.0 (typed PSR-16 signatures) but MemCache::get(), getMultiple(), setMultiple() and deleteMultiple() kept untyped v1 signatures. Autoloading the class fails with PHP Fatal error: Declaration of DBD\Cache\MemCache::get($key, $default = null) must be compatible with Psr\SimpleCache\CacheInterface::get(string $key, mixed $default = null): mixed.
  • Impact. Any application that instantiates MemCache (or passes it to Config::setCacheDriver()) cannot run on 3.1.4; the library's own test suite could not start.
  • Fix. Return types : mixed, : iterable, : bool, : bool added; parameters stay untyped (contravariant with the interface); PHPDoc @return void corrected to the real types.
  • Compatibility. Callers unaffected. Subclasses overriding one of the four methods must declare a compatible return type; such subclasses could not load on 3.1.4 either.
  • Regression coverage. tests/Cache/MemCacheCompatibilityTest.php loads the class in a separate PHP process (a signature mismatch is a compile-time fatal error that cannot be caught in-process).

2. PgUtils::tableStructure() reports nullability correctly

  • Bug. ext-pgsql returns PostgreSQL booleans as the strings 't'/'f'; the code tested isset($row['is_nullable']), which is true for both, so every column came back with Column->nullable === false, with and without Options::convertBoolean.
  • Impact. Schema introspection and any code generation built on tableStructure() treated nullable columns as NOT NULL.
  • Fix. Column->nullable = Utils::convertBoolVar($row['is_nullable']) (handles 't'/'f', native booleans and 'true'/'false'; available in dbd-php-entity since 3.0.0, the minimum allowed version).
  • Compatibility. Column->nullable is now true for nullable columns; consumers that relied on the constant false will observe the corrected value.
  • Regression coverage. tests/Pg/PgUtilsNullableTest.php (testNullableIsParsedFromPostgresBooleanStrings, testNullableIsParsedWhenBooleanConversionIsEnabled) with a fixture required_value TEXT NOT NULL, optional_value TEXT NULL.

3. PgUtils::tableStructure() works for tables without a comment

  • Bug. obj_description() returns NULL for a table without COMMENT ON TABLE; assigning it to the non-nullable Table::$annotation (string, dbd-php-entity 3.x) raised TypeError: Cannot assign null to property DBD\Entity\Table::$annotation of type string.
  • Fix. Empty string when no comment exists.
  • Compatibility. Previously a fatal error; '' is the only representable value for the typed property.
  • Regression coverage. PgUtilsNullableTest::testTableWithoutCommentIsDescribedWithEmptyAnnotation.

4. Pg::escape() / Pg::escapeBinary() use the connection of the current Pg instance

  • Bug. Pg::_escape() and Pg::_escapeBinary() called pg_escape_string() / pg_escape_bytea() without a connection argument. ext-pgsql then uses the last opened PostgreSQL connection of the process (deprecated since PHP 8.1: Automatic fetching of PostgreSQL connection is deprecated), or no connection at all when nothing is connected yet.
  • Impact. Two deprecations in every process that escapes values (src/Pg.php:504, src/Pg.php:483). With two Pg instances the escaping followed the connection opened last, not the instance's own (standard_conforming_strings, client encoding). On an onDemand instance that had not connected yet, escape('a\b') doubled the backslash ('a\b', the pre-9.1 rule) and escapeBinary() produced the legacy escape format instead of the hex format the connection would use.
  • Fix. Both methods pass $this->resourceLink and, when the instance is not connected, open its own connection first with the existing if (!$this->isConnected()) { $this->_connect(); } semantics of DBD::connectionPreCheck(). escape(null|true|false) and escapeBinary(null) still never connect. pg_escape_literal() was deliberately not adopted (a separate API decision).
  • Compatibility. Return values for a connected instance are unchanged (pinned by PgEscapeTest and PgEscapeConnectionTest). New: escaping a string or binary on a not-yet-connected onDemand instance opens the connection, as any query would, and can therefore throw DBDException when the server is unreachable; before, it silently escaped without a connection.
  • Regression coverage. tests/Pg/PgEscapeConnectionTest.php (+ tests/Pg/PgConnectionProbe.php): results with an open connection; an onDemand instance connects on the first escape(string) / escapeBinary(non-null) and not before; reconnect after disconnect() like the query path; two instances with distinct backends (SET standard_conforming_strings = off on the one opened last) escape through their own connection. Three of the four tests fail on 3.1.4.

Test infrastructure (no production change)

  • Test cache fixtures (tests/Common/TestCacheDriver.php, tests/Common/BadCacheDriver.php) implement the PSR-16 v3 signatures.
  • CommonTest::assertException() runs the callback with pg_* E_WARNINGs captured locally (captureDriverWarnings()): ext-pgsql reports intentionally failing queries, prepares and connections as warnings in addition to returning false; PHPUnit 10 no longer converts them to exceptions, so with failOnWarning="true" the suite exited 1 even though all tests passed. Three tests that used expectException() directly now use assertException(). Non-driver warnings still reach PHPUnit.
  • Abstract base classes tests/CommonTest.php and tests/Pg/PgAbstractTest.php are excluded from the test suite in phpunit.xml (PHPUnit 10 reported them as runner warnings).

Known, not changed

DBD::$preparedStatements is process-wide and keyed by crc32, DBD::$executedStatements grows unbounded, the ? placeholder scanner is not SQL-aware, two Pg instances with the same DSN share one libpq connection. These are v4 topics; see the v4 baseline report.

Creation of dynamic property Memcache::$connection is deprecated (reported at src/Cache/MemCache.php:45) is raised inside the PECL memcache extension: addServer() / connect() store the pool as a dynamic connection property of the Memcache object (add_property_resource() in src/memcache.c). Builds based on upstream 8.0 / 4.0.5.2 (for example Ubuntu 24.04 php8.3-memcache 8.0+4.0.5.2+..., reporting version 4.0.5.2) emit it on PHP 8.2+; upstream PECL memcache 8.2 (2023-04-30) flags Memcache / MemcachePool with ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES and is clean. No DBD code change; upgrade the extension.

Test result

PHPUnit 10.5.64, PostgreSQL 16: OK (97 tests, 1051 assertions), exit code 0 on PHP 8.1.34 (dbd-php-entity 3.0.0), 8.2.33 and 8.3.6 (3.1.4 baseline: suite could not start). The only remaining deprecation on the PHP 8.3 host is the PECL memcache 4.0.5.2 one described above; with PECL memcache 8.2 (the 8.1 / 8.2 runs) the suite reports none.