Releases: decMuc/PDOdb
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.
v1.2.0 – Deep SQL Expression Parsing & Injection Defense
[1.2.0] – 2025-07-03
Added
- Full SQL expression validation for 
groupBy()andorderBy()methods - Security filter: Blocks dangerous SQL constructs (e.g. 
UNION,DROP,SLEEP, etc.) - Expression parser:
- Nested function support (e.g. 
ROUND(ABS(price), 2)) - CASE WHEN validation with deep inspection
 - Literal and operator detection (e.g. 
total > 100) 
 - Nested function support (e.g. 
 - Extensive test coverage for allowed and blocked payloads
 
Changed
validateSqlExpression()is now used internally bygroupBy()andorderBy()to enforce safe syntax- Removed internal 
$this->reset()call to avoid silent data loss between method calls 
Fixed
- Fixed missing exception in CASE WHEN expressions with invalid inner content
 - Proper handling of blocked 
ORDER BY RAND(),SLEEP(),DROP TABLE, and similar injection attempts 
Version 1.1.0 – Type-safe WHERE Extensions
This release introduces several new where*() methods that allow for strict and type-safe filtering in your SQL queries – fully compatible with the existing API, but safer and more predictable when handling dynamic user input.
These extensions significantly improve SQL safety and prepare the foundation for type-specific logic in multi-tenant or user-facing environments.
✨ New Features (v1.1.0)
🔢 whereInt() / orWhereInt()
Strict integer filtering. Only clean integers are allowed.
Rejects invalid strings like "123abc" or injection attempts.
🔁 whereFloat() / orWhereFloat()
Strict float/decimal filtering. Accepts floats, integers, and numeric strings like "1.23".
Rejects malformed input and non-numeric values.
🔤 whereString() / orWhereString()
Filters only safe string values.
Blocks strings containing SQL control characters or known injection patterns.
✅ whereBool() / orWhereBool()
Strict boolean filtering. Accepts only true, false, 1, or 0.
All other values trigger an exception.
⚫️ whereIsNull() / orWhereIsNull()
Adds a IS NULL condition to the WHERE clause.
⚪️ whereIsNotNull() / orWhereIsNotNull()
Adds a IS NOT NULL condition to the WHERE clause.
📥 whereIn() / orWhereIn()
Filters using an IN (...) clause.
Accepts an array of scalar values and safely binds them via placeholders.
📤 whereNotIn() / orWhereNotIn()
Filters using a NOT IN (...) clause.
Also uses safe, parameterized bindings – input must be a non-empty array.
📚 Documentation
All new methods are documented in the updated README.md, including usage examples and input validation rules.
🧱 Internal
All where*() methods internally use secureWhere() for consistent query construction and prepared statement safety.
Version bumped from v1.0.3 to v1.1.0.
📄 Changelog
See CHANGELOG.md for a full list of changes and version history.