This file shows the changes in this release of xPDO.

xPDO 3.1.7 (December 4, 2025)
====================================
- PHP 8.5 compatibility (#264)

xPDO 3.1.6 (October 3, 2025)
====================================
- xPDORedisCache expire can't be zero (#261)
- Remove realpath to enable xPDO usage in PHARs (#262)

xPDO 3.1.5 (December 9, 2024)
====================================
- Validate Directory resource in xPDOCacheManager->deleteTree() (#255)

xPDO 3.1.4 (September 26, 2023)
====================================
- Do not send expire to Redis when value is zero (#250)
- Add ability to forward logs to classes implementing ArrayAccess (#233)
- Add AllowDynamicProperties attribute to xPDO class (#252)
- Change path references from om/ to Om/ (#248)
- Fix update=2 option in xPDOGenerator::parseSchema (#246)

xPDO 3.1.3 (January 12, 2023)
====================================
- Force query operator to the uppercase (#243)
- Add public xpdo property to xPDOCriteria (#241)
- Add PHP 8.2 to the ci matrix (#238)
- Implement generics syntax to allow improved static analysis (#237)

xPDO 3.1.2 (July 12, 2022)
====================================
- Fix passing null to int param is deprecated (#230)
- Allow psr/container ^1.1 || ^2.0.1 (#231)

xPDO 3.1.1 (April 20, 2022)
====================================
- Implement write-schema CLI command (#225)
- Fix param type for cache entries from string to mixed (#211)

xPDO 3.1.0 (March 8, 2022)
====================================
- Add missing #[\ReturnTypeWillChange] to xPDOMap::offsetExists()
- Fix deprecated warnings for PHP 8.1 (#218)
- PHP 8.1+ compat and various dependency updates (#216)
- Fix deprecation warnings in unit tests for 8.1
- Remove unnecessary uses of deprecated strftime() (#217)

xPDO 3.0.0 (December 28, 2021)
====================================
- Fix error merging driverOptions when creating an xPDOConnection
- Add float phptype to quotable list to prevent quoting floats as integer
- Add ability to define RegEx excludes in xPDOZip->pack()
- Fix bug when generating platform files from classes with constants
- Add __debugInfo() magic method
- Load related object attributes correctly into nested relations
- Add option to restrict extraction of files by extension to xPDOZip
- Fix invalid datetime value being used when formatting with regex
- Fix xPDOFileCache->delete() when a directory with same cache key exists
- Improve PSR-4 support by allowing namespace prefixes to be removed from package name
- Fix PHP 7.4 deprecation warnings for array and string offset access with curly braces
- Add xPDO->getAlias() method to get a class name without the namespace
- Quote default column values for MySQL
- Add JSON extension as composer dependency
- Add PostgreSQL support via the pgsql PDO driver
- Refactor xPDOObject->remove() to use key=>value for primary key criteria
- Add missing return statement to xPDOMaxLengthValidationRule
- Fix xPDOIterator failure when fetch returns null due to object hydration
- Improve PSR-4 support by introducing namespace prefixes to model metadata
- Introduce PSR-11 support to replace deprecated container-interop support
- Make json and array phptypes quotable in xPDOQuery
- Replace usages of each with foreach to avoid PHP 7.2 deprecated warnings
- Fix "reference expected" warnings in PHP 7.1
- Introduce xPDOContainer providing container-interop implementation
- Add xPDO\Cache\xPDORedisCache implementation
- Adopt symfony\console 2.x for CLI scripts
- Add phpdoc @property to classes for each field
- Add option to respect PSR-4 namespaces
- Refactor for 5.3+ in the \xPDO namespace

xPDO 2.5.3-pl (January 3, 2017)
====================================
- Make sure xPDOQuery base class is always loaded

xPDO 2.5.2-pl (December 16, 2016)
====================================
- Sanitize and prevent SQLi in getObject calls expecting PK values
- Make isValidClause a public static method in xPDOQuery
- Allow empty sort direction
- Revert getCriteria change to force scalar params to be PK values
- Make xPDOQuery::isValidClause a public method
- Change constructor in PclZip to be PHP 7 compatible

xPDO 2.5.1-pl (November 14, 2016)
====================================
- Force scalar parameters to be PK values in getCriteria
- Prevent SQLi in sortby, sort direction, and limit clauses
- Detect SQLi clauses when no space exists after UNION

xPDO 2.5.0-pl (February 3, 2016)
====================================
- Optimize and improve logging
- Fix undefined index notice for model indexes
- Allow definition of primary key fields without legacy index="pk"
- Add ARRAY_EXTENDED logTarget that stores an array of data for each log entry

xPDO 2.4.1-pl (September 22, 2015)
====================================
- Optimize xPDO::getOption() for significant performance increase

xPDO 2.4.0-pl (June 26, 2015)
====================================
- Add ability to override mysql table engine via options
- Allow custom attributes in schema object elements
- Allow silent failure of xPDOVehicle validators
- Only show parseBindings results if debug set
- Accept benign values that contain the word union
- Allow xPDOObject::remove() to remove dependents with composite relationships
- Respect engine specified in mysql schema

xPDO 2.3.0-pl (July 15, 2014)
====================================
- Allow recursive xPDOObject::remove() beyond one level
- Allow schema to define callbacks for field
- Reject xPDOQuery conditions with SQL injection attempts
- Resolve xPDO::parseBindings() error from /e modifier fix
- Fix xPDO::getCount() to work with queries containing group by
- Replace reflection of PHP_SELF with SCRIPT_NAME in xPDO::log()
- Replace use of deprecated /e preg_replace modifier
- Add dependency metadata/API to xPDOTransport

xPDO 2.2.9-pl (September 12, 2013)
====================================
- Improve accuracy of query statistics
- [#10183] Fix cache entry removal on xPDOObject::remove()

xPDO 2.2.8-pl (August 26, 2013)
====================================
- [#9864] Identify compound PK's properly in xPDOQuery::parseConditions()

xPDO 2.2.7-pl (April 9, 2013)
====================================
- [#9793] Preserve numeric keys in xPDOObject::getMany()

xPDO 2.2.6-pl (March 16, 2013)
====================================
- [#7452] Fix sql errors with decimal fields in some locales
- [#9062] Fix problem loading multiple xPDOQuery driver classes
- [#9629] Fix notice in xPDOConnection::getOption() when $options not an array
- [#9020] Prevent xPDOZip from attempting to add empty targets
- [#9272] Implement NFS-safe flock-independent file locking
- [#9020] Allow xPDOZip to pack empty directories
- [#9023] Fix removal of subdirs when deleteTop = false in xPDOCacheManager::deleteTree()
- [#8962] Prevent xPDOSimpleObject from overwriting xPDOObject map entry
- [#8423] Fix tmpLockFile naming issue in xPDOCacheManager::lockFile()
- [#8440] Add memcached implementation of xPDOCache (xPDOMemCached)
- [#8423] Add flock-independent file locking to xPDOCacheManager::writeFile()
- [#6930] Fix improper escaping of $ in parseBindings
- [#8148] Prevent update from criteria using raw NATIVE_KEY on install
- [#8320] Preserve order of $columns in xPDOObject::getSelectColumns()
- Deprecate xPDO->getMicroTime()
- [#8176] xPDO->updateCollection() fails to set NULL values

xPDO 2.2.5-pl (June 1, 2012)
====================================
- [#8060] Implement __isset() to fix empty()/isset() on hydrated vars
- [#7797] Avoid E_NOTICE from array_diff_assoc change in 5.4
- Prefix indexes with table name for sqlite
- Fix lazy loading of fields on demand
- [#7607] xPDOObject->get(array) triggering invalid lazy loading
- xPDOObject/xPDOSimpleObject support for xPDO::getDescendants()
- Refactor HYDRATE_FIELDS/HYDRATE_RELATED_OBJECTS using __get()/__set()

xPDO 2.2.4-pl (April 2, 2012)
====================================
- Refactor aggregate/composite criteria support for targeting local/foreign
- Fix xPDOObject::_loadInstance for proper loading of graph objects
- Refactor schema parsing using SimpleXML
- Fix related_object installation in xPDOObjectVehicle
- Add criteria support to aggregates and composites

xPDO 2.2.3-pl (March 15, 2012)
====================================
- Add xPDOScriptVehicle for manifest-based PHP scripts
- Implement xPDOQuery::having() method
- Add xPDO::getInstance() for singleton support

xPDO 2.2.2-pl (January 4, 2012)
====================================
- Fix handling of driverOptions in xPDOConnection instances
- [#6240] Derivative-only fields marked lazy in base classes

xPDO 2.2.1-pl (December 2, 2011)
====================================
- [#3976] Trigger SQL error when empty IN condition encountered
- [#6152] Optionally skip empty string values in xPDO::getOption()
- [#4523] Log SQL errors in xPDOObject::loadCollectionGraph
- [#6095] Log errors when xPDOManager::addField/alterField fail
- [#6098] Fix issue with preg_replace in xPDO::parseBindings() method
- [#5970] Fix issue with subpackages and derived classes that would cause nonsensical error

xPDO 2.2.0-pl (November 15, 2011)
====================================
- [#6046] Fix getDescendants() to recursively get derivative classes
- [#6045] Fix setPackageMeta() to work with sub-packages
- [#6044] Allow STI derivatives to supplement base class metadata
- [#6043] Allow xPDO->getSelectColumns() to be overridden in xPDOObject derivatives

xPDO 2.2.0-rc2 (October 20, 2011)
====================================
- Fix xPDOAPCCache and xPDOWinCache multiple_object_delete features
- Fix various bugs with database result set caching
- Refactor escapeChar and quoteChar to xPDODriver classes
- [#2432] Store relative path of files preserved by xPDOVehicle file resolvers
- [#5904] Provide access to name and version in Transport packages

xPDO 2.2.0-rc1 (September 7, 2011)
====================================
- Add support for field aliases to be defined in the schema
- Add xPDOObject->addFieldAlias for creating references to actual fields
- Add xPDOObject->getIterator for iterating aggregate and composite relations
- [#4590] Allow multiple connections to be defined for master/slave configurations
- [#2883] Add xPDOObject->getGraph() and add feature in toArray() to return related object fields
- Add xPDO->getGraph() method to infer relation graphs
- [#4275] Add xPDO->updateCollection() method
- [#4275] Enable UPDATE support and add set() method to xPDOQuery
- [#5306] Add xPDOQuery->distinct() method
- Change xPDO::getService() method to indicate it returns a reference
- Avoid unsetting the current package if xPDO->addPackage() fails
- Avoid updates to existing package definitions unless path/prefix changes
- Remove redundant call to $this->setPackageMeta() in xPDO->setPackage()
- Ensure xPDO::$classMap derivative entries are unique
- [#5197] Provide map of derivative classes for introspection
- [#5147] Fix xPDOIterator ignoring criteria that is not already an object

xPDO 2.1.5-pl (July 5, 2011)
====================================
- [#5084] Fix various xPDO::exec() calls to check for false value properly
- [#4877] Fix nested conjunctions infecting parent conjunctions
- [#5042] Prevent additional columns defined in derivative classes being marked lazy

xPDO 2.1.4-pl (May 23, 2011)
====================================
- Default cacheFlag to true in getObject/getCollection/getIterator methods
- [#4787] Add ability to determine the table class of derivatives
- [#4772] Do not flush related objects from cache on read operations
- [#4682] Fix support for driver-specific attributes on object element in schema

xPDO 2.1.3-pl (May 3, 2011)
====================================
- [#4577] Remove calls to xPDO->log() in xPDOCacheManager->writeFile()
- Add options to xPDOCacheManager::writeFile() to allow multiple attempts when exclusive locks fail

xPDO 2.1.2-pl (March 25, 2011)
====================================
- Add xPDO->getValue() convenience method for retrieving single column from single row
- [#4128] Fix Windows drive issue in xPDOTransport->pack()
- [#4125] Fix bug getting composites in xPDOObject->getFKClass()
- [#4097] Make constraint names unique for sqlsrv
- [#4047] Fix bug adding single file to zip in xPDOZip
- [#3971] Add xPDOManager API for manipulating table structures

xPDO 2.1.1-pl2 (February 21, 2011)
====================================
- Fix error in xPDOObject->getOption() when handling option overrides

xPDO 2.1.1-pl (February 20, 2011)
====================================
- [#2909] Make auto-table creation optional
- Add options and default parameters to xPDOObject->getOption()
- [#3174] Use xPDOQuery alias to build join criteria for getMany()
- [#3342] Fix use of deprecated split() function in xPDORevisionControl
- Fix modCacheManager->writeFile() not to truncate files if a mode is specified

xPDO 2.1.0-pl2 (February 16, 2011)
====================================
- Avoid PHP warning from fopen in xPDOFileCache->get()

xPDO 2.1.0-pl (February 16, 2011)
====================================
- Add proper cache file locking and format handling
- Add OPT_CACHE_FORMAT constant to xPDO
- Add support for CACHE_SERIALIZE format in xPDOFileCache implementation
- Force flush() for multiple_object_delete option in xPDOMemCache->delete()
- Implement multiple_object_delete support in xPDOAPCCache->delete()
- Return boolean from xPDOFileCache->delete() and ->flush()
- Fix xPDOFileCache->delete() and invalid calls to cacheManager->deleteTree()
- Use APCIterator to support per instance flush()'ing in xPDOAPCCache
- Prefix all cache keys with provider key
- Fix db cache methods to be called with proper options
- Add xPDOCacheManager->refresh() to empty all active cache providers
- Fix limit with sortby functionality for sqlsrv
- Fix xPDO->literal() and add missing xPDO->$_quoteChar
- Fix date/time issues (sqlsrv)

xPDO 2.0.1-pl (December 27, 2010)
====================================
- [#2927] Add ENGINE option to xPDOManager_mysql->createObjectContainer()
- Allow columns with explicit AS, ., or ( to not be escaped
- Override xPDOQuery->parseConditions() for sqlsrv
- Use SET IDENTITY INSERT in xPDOObjectVehicle->install() (sqlsrv)
- Remove LIMIT from UPDATE in xPDOObject->save()
- Fix handling of bigint with timestamp phptype (sqlsrv)
- Set host property from server property in xPDO::parseDSN()
- Remove check for errorCode on xPDO->connect()
- [#2901] Add transient parameter to xPDO->call() for use with non-db classes

xPDO 2.0.0-pl (November 27, 2010)
====================================
- [#2788] Escape database name in xPDOGenerator_mysql::writeSchema()
- Add xPDOAPCCache implementation of xPDOCacheProvider
- Refactor usages of xPDO::getObjectLoader() to use xPDO::call()
- Add xPDO::call() allowing db-specific static method overrides
- Add xPDOWinCache implementation of xPDOCache provider
- Add workaround to drop a database for sqlsrv
- Add workaround to trim PDO::quote() results for sqlsrv
- Add virtual support for LIMIT..OFFSET to sqlsrv driver
- Set dbname property from database property in xPDO::parseDSN()
- Introduce pdo_sqlsrv driver support

xPDO 2.0.0-rc3 (October 25, 2010)
====================================
- Add xPDO::getIterator() allowing more efficient traversing of result sets
- Add script to upgrade older xPDO mysql models to SCHEMA_VERSION 1.1
- Add missing attributes in xPDOSimpleObject map index element
- Do not generate primary key index for xPDOSimpleObject derivatives
- Introduce xPDO::SCHEMA_VERSION and set it to 1.1 to distinguish new format for indexes

xPDO 2.0.0-rc2 (October 20, 2010)
====================================
- Use new index metadata in maps to generate indexes for mysql
- Fix mkdir issue in xPDOGenerator::parseSchema()
- Improve schema representation of mysql indexes in xPDOGenerator_mysql::writeSchema()
- [#2579] Fix xPDOFileVehicle not calling resolve on uninstall()
- [#2414] Fix phpdoc inaccuracy and redundant assignment
- Fix caching error in xPDOObject::loadCollectionGraph()
- Make xPDOQuery::prepare() always call construct()
- [#2401] Clarify xPDO::removeCollection() return value
- [#2419] Add defined join criteria when custom criteria is provided to xPDOObject::getMany()
- [#2416] Add :NOT IN operator support for array value bindings
- [#2404] Allow xPDOTransport::install to be aborted when an xPDOVehicle fails to install
- [#XPDO-64] Fix xPDO::logTarget not to lose options after first call to xPDO::_log()
- Fix xPDOObject::_loadInstance() to work with xPDOQuery::hydrateGraphNode() properly
- Fix bug in xPDOObject::load() when caching by primary key
- Fix bug with xPDOManager_sqlite::removeSourceContainer()
- [#XPDO-62] Make getObjectGraph() respect class_key
- Add IF EXISTS to DROP DATABASE statement in xPDOManager_mysql::removeSourceContainer()
- Add xPDO::getIndexMeta() method
- Fix invalid call to xPDOManager::getPhpType() in xPDOGenerator::writeSchema()
- [#XPDO-74] Fix bug in xPDOObject::save() when updating rows with compound primary keys
- [#XPDO-72] Fix invalid call to xPDOManager::getPhpType() in xPDOGenerator::parseSchema()
- Fix accessibility of xPDOTransport::_unpack()
- Divert excess log output from xPDOVehicle classes to LOG_LEVEL_DEBUG
- Remove extra slash in path when generating class files
- Simplify includes of parent classes
- Add xPDODriver to better abstract driver-specific attributes and behavior
- Fix several references to xPDO::$_escapeChar in xPDOQuery
- Split xPDO::$_escapeChar into $_escapeCharOpen and $_escapeCharClose
- Simplify path for require_once in platform class templates
- Trim property keys and values in xPDO::parseDSN()
- [#XPDO-66] Add xPDOTransport::ARCHIVE_WITH to allow explicit use of PclZip even if ZipArchive exists
- Add xPDO::literal() method for SQL literal strings
- Introduce pdo_sqlite driver support
- Fix exception logging in xPDOManager_mysql
- Fix xPDO::parseDSN() for sqlite DSN format
- Remove xpdo.connect.inc.php (no longer used)
- Migrate to git
- [#XPDO-46] Implement support for IN operator using array as binding
- [#XPDO-51] Fix incorrect criteria variable when caching individual rows from collections by primary key
- [#XPDO-53] Fix bug preventing save of related objects when a key owner is foreign and not new
- Refactor xPDO::getManager() and xPDOManager::getGenerator() to work with other db engines besides mysql
- Replace direct references to xPDO::$_escapeChar with new xPDO::escape()
- Move xPDOGenerator::getPHPType() to xPDOManager
- Fix non-static call to xPDO::parseDSN() in xPDO::__construct()
- Make sure db-specific connect_file exists before trying to include it
- Remove unnecessary overridden _initFields() in xPDOObject derivatives
- [#XPDO-54] Fix xPDOQuery to properly recognize MySQL Fulltext search expression as a conditional clause
- [#XPDO-52] Fix fatal error when xPDOManager::createObjectContainer() cannot prepare a PDOStatement
- xPDO::log() and xPDO::$logTarget will now accept an ARRAY target with an option that is a reference to the var
- xPDO::connect() should return false on Exception
- Ensure driverOptions is set as an empty array if the value passed is not an array
- Log PDOException messages in xPDO::connect()
- [#XPDO-49] Fix inconsistent static and non-static calls to fromJSON() and toJSON() methods on xPDO
- Added xPDO::getService to load service classes
- Commented out deprecated code in xPDOVehicle dealing with PHP validators
- Fixed typo in xPDOVehicle where $r should be $v
- [#XPDO-40] Fixed getCount to work when passing a criteria with a class alias set.
- xPDOTransport: Fix several E_STRICT errors caused by static calls to methods not defined as static.
- xPDOTransportVehicle: Implemented missing _compilePayload(), and fixed errors in _installTransport() and uninstall().
- xPDOValidator: Fix several E_STRICT errors caused by inconsistent method signatures in xPDOValidationRule::isValid() derivatives.
- Fixed bug in xPDOTransportVehicle::put() referring to xPDOFileVehicle.
- xPDOManager_mysql::createSourceContainer() and removeSourceContainer() will use provided connection properties or default to current xPDO instance configuration.
- Removed dependency on mysql extension for xPDOManager_mysql::createSourceContainer() and removeSourceContainer() implementations.
- Refactored xPDOManager to be an abstract class rather than calling static methods in derivatives.
- Added support for xPDOObjectVehicle::uninstall() to operate on related_objects.
- Fixed bug in xPDOObjectVehicle::uninstall() not getting UPDATE_OBJECT value properly.
- Added xPDOTransport::UNINSTALL_OBJECT attribute and support in xPDOObjectVehicle::uninstall() [default value is true].
- Refactored xPDOCacheManager to determine new folder/file permissions via umask().
- Modified xPDOQuery_mysql::select() to trim the comma-delimited fields specified after explode()'ing the string.
- Modified xPDOVehicle::resolve() to return true if xPDOTransport::RESOLVE_FILES is set to false with file resolver types.
- Modified xPDOFileVehicle::install() to check xPDOTransport::INSTALL_FILES is not set to false before checking for the existence of the defined fileSource.
- Fixed xPDOVehicle::resolve() and xPDOFileVehicle::install() to prevent them from attempting to preserve files that do not exist.
- Added xPDOTransport::INSTALL_FILES and xPDOTransport::UNINSTALL_FILES to make xPDOFileVehicle behavior consistent with RESOLVE_FILES and RESOLVE_FILES_REMOVE.
- Updated xPDOVehicle::resolve() to properly detect if resolved when restoring preserved file archives.
- Updated xPDO and xPDOCriteria constructors to use proper PHP5 __construct().
- Added xPDOTransportVehicle class to allow transport packages to be embedded in other transport packages.
- Updated copyright dates.
- [#XPDO-42] Fixed error with xPDOQuery::parseConditions() ignoring NULL bound values.
- Fixed xPDOCriteria::toSQL() to properly handle NULL values.
- Updated/fixed xPDOZip implementation and enabled it as the preferred zip provider for xPDOTransport.
- Made xPDOVehicle an abstract class.
- [#XPDO-4] Allow nesting conditions arbitrarily deep using simple nested array format.

xPDO 2.0.0-rc1 (LastChangedRevision: 389, LastChangedDate: 2009-12-31 11:18:40 -0700 (Thu, 31 Dec 2009))
====================================
- [#XPDO-27] Allow getCount() to work with Collection Graphs.
- [#XPDO-38] Modified xPDOQuery to determine conjunctions for condition groups based on conjunction specified for first condition in the group.
- Added changelog.txt
