Releases: decMuc/PDOdb
PDOdb 1.3.9 – PHP 8.4 compatibility
This is a small but important maintenance release that keeps PDOdb clean under PHP 8.4 by fixing deprecation warnings for implicitly nullable parameters.
What changed
- Updated several method signatures to explicitly include
nullwhen a parameter uses= null:get()andquery()now declare$numRowsasint|array|null(matching the existing PHPDoc).- Optional array parameters in
rawQueryOne(),insertMulti()and the internal_buildQuery()helper are now declared as?array.
- No behavioural changes: the public API and runtime behaviour remain the same; only the type declarations were adjusted.
Notes
mixed $value = nullparameters (e.g. inwhere(),orWhere(),secureWhere(),having(), …) were intentionally not changed, asmixedis already implicitly nullable and does not trigger PHP 8.4 deprecation warnings.- This release is a safe drop-in update for all users of PDOdb 1.3.x. If you are running PHP 8.4 (or planning to), updating to 1.3.9 is recommended.
Acknowledgements
Big thanks to @daveo91 for reporting the issue and helping to keep PDOdb ready for PHP 8.4.
PDOdb v1.3.8 — Raw WHERE segments + decimal input fix
PDOdb v1.3.8 — Raw WHERE segments + decimal input fix
This release delivers two things people kept asking for: truly flexible raw WHERE blocks for complex queries, and a reliable way to accept localized decimal input (e.g. "12,20") without MySQL shouting about truncated data.
Version 1.3.7 was skipped and not published because the initial decimal normalizer didn’t run on all code paths (notably rawQuery() / CALL ...). This is fixed in 1.3.8.
Added
-
whereRaw()andorWhereRaw()- Lets you inject arbitrary
WHEREfragments, including grouped/nested AND/OR logic, function calls, and multi-column comparisons. - Supports
?placeholders with a separate values array for proper PDO prepared binding, e.g.:$db->whereRaw('(base_name LIKE ? OR sku LIKE ?)', [$search, $search]);
- Can be combined with the existing query builder methods and will be included in
get(),update(),delete(), etc. - Raw segments are intentionally not validated for column/operator names — only their parameters are bound.
- Lets you inject arbitrary
-
Automatic normalization of decimal-comma input
- Values like
"12,20"or"1.234,50"are now converted to SQL-friendly dot-decimals before binding. - This now also runs at the central parameter-binding layer, so it applies to stored procedure calls (
CALL ...) as well, not just to inserts/updates where the column type is known.
- Values like
Changed
- The internal WHERE builder now supports mixing typed helpers (
whereInt(),whereStr(), …) with raw segments and keeps the correct order. - The central parameter binding now normalizes decimal-like strings before calling
bindValue(), so MySQL/MariaDB numeric columns (DECIMAL,FLOAT,DOUBLE) no longer produce
Data truncated for column ...
just because the client sent a comma.
Fixed
- Decimal input using a German-style comma could cause
SQLSTATE[01000]: Warning: 1265 Data truncated for column 'price' at row 1
PDOdb v1.3.6 — Array parameter expansion (IN ...) refined
This update rewrites the internal expansion for array parameters used in IN (...) (and similar). It now strictly validates placeholder usage, supports reuse of named scalars across multiple SQL occurrences, and deterministically expands named arrays per occurrence. Positional arrays expand into N comma-separated ? as expected.
Why: more predictable bindings, clear errors on misuse, and safer defaults.
Added
- Reuse of named scalar placeholders across multiple SQL occurrences.
- Deterministic expansion of named array placeholders per occurrence: :name_{occIdx}_{elemIdx}.
Changed
- Stricter validation of placeholder styles; named vs. positional must not be mixed.
- Improved error messages and state reset on failure.
Fixed
- Correct placeholder/parameter count checks for positional arrays.
Breaking
- Empty arrays (named or positional) now throw.
- Mixed named + positional in one query now throws.
PDOdb v1.3.5 – Improved Error Handling
Error Management Refactored
PDOdb has long followed an exception-based error handling model.
As of v1.3.5, this approach is now consistently enforced across all operations.
While ThingEngineer's MysqliDb provides basic error feedback, its behavior under try/catch can be unreliable in certain edge cases. For example, when errors occur inside loops or chained calls, the internal error state might not reset cleanly – and if the result is not checked manually, the application can continue in an invalid state.
In some cases, MysqliDb would not return anything in getLastError() simply because a fatal PHP error had already occurred before the error could be stored. Since no exception was thrown, try/catch could not intercept the failure, and the script terminated unexpectedly.
In contrast, PDOdb guarantees that all critical errors immediately throw an exception and are safely stored internally for later inspection. This allows developers to handle errors in a controlled and predictable way using standard try/catch blocks.
Exception-First Design
It’s now a design decision to prefer structured try/catch blocks in your application logic when using PDOdb.
This allows for precise error handling via either:
$e->getMessage() // Native PHP exception
$db->getLastError() // Internal message from PDOdbBoth approaches work seamlessly together.
Note on getLastError()
getLastError() may return the internal PDOdb error message or, in some cases, a raw SQL/PDO error depending on the failure context.
For most application-level handling, the recommended pattern remains:
try {
$db->insert('table', $data);
} catch (\Throwable $e) {
echo $db->getLastError(); // or $e->getMessage()
}Version 1.3.4 – Extended Placeholders, Secure Values & Debug Improvements
This release includes extended placeholder support, improved value validation, and enhanced debug traceability.
It also introduces initial deprecation warnings for legacy property usage, preparing for safer and cleaner API usage in future versions.
Changes in v1.3.4
Added
Full support for special values in:
insert(), insertMulti(), update(), replace()
Central _secureValidateInsertValues() for consistent value validation
Automatic subquery alias mapping via host for use in secureHaving() and others
Security & Validation
Improved secureWhere():
Blocks unsafe SQL functions (SLEEP(), BENCHMARK(), etc.)
Disallows aggregate functions (SUM(...), AVG(...)) inside WHERE
Validates subqueries, nulls, arrays, and column names strictly
All per-connection settings (setPageLimit(), setReturnKey(), etc.) are now instance-safe
Debug & Logging
_lastDebugQuery now tracks queries even before reset or on failure
Full restoration of logQuery(), logException(), and handleException() across all queries
Logging improvements for WHERE, JOIN, and other clause types
⚠️ Deprecated (to be removed in v1.5.0)
-
$db->pageLimit
→ usesetPageLimit()instead (instance-safe) -
$db->map
→ usesetReturnKey()instead -
$db->arrayBuilder(),$db->objectBuilder(),$db->jsonBuilder()
→ usesetOutputMode('array' | 'object' | 'json')instead -
$db->escape()
→ remains a non-functional dummy and may be removed in a future version -
Static global instance via
getInstance()
→ use named instances viagetInstance('your_name')instead
Note
This release is recommended for all users who rely on safe inserts, debug tracing, and subquery support.
Legacy property access will be phased out in upcoming versions – migrate early to ensure forward compatibility.
v1.3.3 – Optional heuristic WHERE check toggle added
Adds optional heuristic validation for suspicious WHERE values (e.g., SQL injection attempts).
Enabled by default; may add ~1–3 ms overhead in edge cases.
Can be disabled via:
define('PDOdb_HEURISTIC_WHERE_CHECK', false);PDOdb Update: Improved Security – Successful SQL Injection Tests So Far!
This release focuses on readability and maintainability:
Completely reorganized class structure: methods are now logically grouped, clearly sorted, and easier to navigate.
Improved naming: some internal methods have been renamed for better clarity and consistency.
What's Next?
Sorting of internal _secure* methods is still pending. I'll tackle this within the next few days—no separate release planned, as it won't affect functionality. Just too lazy to finish it today!
Version Note: Why the jump from 1.3.0 to 1.3.2?
I intentionally skipped version 1.3.1 because I ran multiple internal/local tests under that number but never officially released it. When I was finally ready, significant additional improvements justified a direct jump to 1.3.2. This isn't a mistake—just a result of extensive local testing and limited publishing time!
PDOdb v1.3.0 – Cleanup & Compatibility Refinement
This release removes legacy methods inherited from the original ThingEngineer class and marks a small but important shift in project philosophy: PDOdb is now a "close to 1:1 logic-compatible" replacement, rather than a strict clone.
Removed
loadData() and loadXml()
These methods relied on LOAD DATA INFILE / LOAD XML INFILE, which are:
non-portable (require special server configuration and privileges)
bypass PDO's prepared statement model
considered unsafe in modern deployments
They were part of the original MysqliDb class but do not align with the design goals of PDOdb.
If needed, use native PHP CSV/XML handling with prepared inserts.
Added
Default instance name $instance = 'default'
This ensures stable fallback behavior when no named connection is specified.
Changed
README updated to reflect the removal of legacy import methods
Reworded compatibility statement: now described as "close to 1:1 compatible" for transparency
Hotfix - wrong namespace
Hotfix - wrong namespace
Release 1.2.2 — Named Instances & Connection Management
This update introduces named instances for full multi-connection support.
Added
instance support in constructor (new PDOdb([...])) and CustDB::createInstance(...)
PDOdb::getInstance('name') retrieves specific instances
subQuery() now inherits the correct instance automatically
Changed
Internal handling of $defConnectionName and $_activeInstanceName for accurate context
getInstance() now returns the last active or default instance cleanly
Note
If you omit the instance key, your new object won't be registered – it becomes a one-time use connection.