We are happy to announce that Phalcon v5.12.0 has been released!
… and so was 5.11.0 and 5.11.1. All we can do is apologize for the lack of blog posts.
This is going to be a long blog post
TLDR: We fixed a lot of stuff, v6 alpha coming out in the next week or so
It has been a year since 5.9.2 has been released and a lot has happened since then especially in our personal lives. In most cases, those events were the primary reason that the project has not been moving as fast as it should. Hopefully we will have a lot more time in the future to move the project forward.
So what has happened since our last blog post, which was our 5.10.0 Christmas release. We released 5.11.0 which addressed 17 issues and 5.11.1 which addressed another 4. This release (5.12.0) addresses 65 issues, one of our biggest releases ever. I do believe though that there was one release back in v1.3 that had over 100, so that record still remains! You can check the changelog entries at the bottom of this post.
v5
A lot has happened in v5.
Zephir
Significant work in Zephir allowed us to produce a much cleaner compilation process. The dreaded warnings about incompatible pointers are nearly gone - we just need a small cleanup, and the extension compiles fine in PHP 8.5.
ORM / Model Layer / Database
The most heavily worked area across all three releases. Numerous long-standing issues were resolved, such as relation caching, snapshots, COUNT, CRUD operations, PHQL, Hydration, RawValue and a lot of Postgres fixes.
Pagination
Fixed the paginate() to understand DISTINCT and return proper counts, as well as GROUP BY for multi columns.
Forms & Validation
Fixes in the Alpha validator to work better with allowEmpty, filters with validators, whitelist for entity binding, mutli field messages and a new Validation::fails() convenience method for standalone validation checks.
HTML, Assets & Image
Breadcrumbs now honor sub directories (URL or set), Assets\Manager also uses the URL when available, PNG transparency, GD crop, new helpers FriendlyTitle and Preload for TagFactory and new Select::fromData() added to populate select options from a SelectDataInterface provider, with optgroup support. This mimics the Tag::Select with using
Cache & Storage
New RedisCluster adapter, new deleteMultiple, corrected key prefix calculation, additional corrections with the serializers and options
Security & Encryption
Added Encryption\Security\Uuid factory with versioned adapters (v1–v7), JWT: Validator::validateClaim() for custom JWT claim validation, JWT: Builder::setPassphrase() is more strict now, computeHmac now catches exceptions better, Random: base() now defaults to 16 bits.
Dependency Injection
Introduced named aliases for registered services, minor correction to exception text
Logger
Log levels are now reported in lowercasem, improved performance for Stream
Dispatcher / MVC
Corrected Dispatcher::dispatch() to refresh the events manager where needed, made Mvc\Controller and View\Engine\AbstractEngine events-aware.
HTTP Request
Fixes to getPostData() when type is missing from the headers, getPost() correctly returns JSON, getClientAddress() fixed for trusted forwarded proxy header handling.
Infrastructure
- PIE support: Added PHP Installer for Extensions (PIE) support and removed PECL references (since it is deprecated)
- Added
Phalcon\Support\SettingsNew centralized class replacingglobals_get/globals_setthroughout the framework for managing INI-style settings. - PHQL parser memory leak: Fixed a memory leak in
phql_internal_parse_phql()during repeated query execution. - Checked compatibility with PHP 8.1-8.5 for types and warnings.
- We changed the testing suite to use phpunit. This allows us to do a direct comparison of the code between v5 and v6, after all the same test has to pass both versions, ensuring that we have alignment. That was a huge task with a lot of hiccups.
- Introduced a nore verbose and faster CI runs, testing the extension installation as well as tests. Also enabled all the database tests including Sqlite and Postgresql. The latter was a bit tough, because there were a lot of issues with the tests. However, doing so, helped us identify a few key bugs with the Posgresql adapter and those have been fixed.
v6
The code is fully aligned with v5 and the testing suites are (nearly) identical. Tests pass on both projects.
We have ported the PHQL and Volt parsers in its own PHP implenentations. Those are separate library repositories and are dependencies for v6. A bit more work in terms of propagating changes from v5 to these two repositories, when a change is needed to the parsers, but so far so good.
Migrated one of our sample applications to v6 and thus far no issues. More testing required of course.
An alpha version will be released in the next week or so.
AI
We have been leveraging AI to identify bugs in the framework. Thus far, the results have been promising, because bugs lingering for years have now been addressed. We are planning on using it for complex tasks and deep analysis in the future. Our approach is trust but verify, so AI will never produce code that has not been vetted by us (and tested).
Community
A huge thanks to our community for helping out with bug fixing and more importantly bug reporting! We also thank you for your patience since some of these bugs and pull requests have been open for a few years (yes, years :/).
Changelog
5.12.1 (xxxx-xx-xx)
Added
- Added
Phalcon\Db\Column::TYPE_UUIDconstant (value29) and added support for PostgreSQL nativeuuidcolumn type inPhalcon\Db\Adapter\Pdo\PostgresqlandPhalcon\Db\Dialect\Postgresql#16840 - Added support for
Phalcon\Mvc\Urlstatic base URI inPhalcon\Assets\Manager; when a DI container is set and aurlservice is available, local asset paths are now resolved viagetStatic()instead of a bare/prefix #16570
Fixed
- Fixed
Phalcon\Mvc\Model\Managerretaining a model instance inlastInitializedafter initialization andPhalcon\Mvc\Modelnot clearing the reusable-records cache aftersave(), causing memory to grow unboundedly in long-running processes #16566 - Fixed
Phalcon\Paginator\Adapter\QueryBuilder::paginate()returning wrong total item count when the query usesDISTINCTcolumns; the count now usesCOUNT(DISTINCT ...)for a single column and a subquery for multiple columns #16581 - Fixed
Phalcon\Mvc\Model\Query\Builder::autoescape()incorrectly wrapping function expressions (e.g.DATE_PART(...)) in brackets when used ingroupBy(), causing a"Column does not belong to any of the selected models"exception #16599 - Fixed
Phalcon\Mvc\Model- saving a model with multiple fields relations threw"Not implemented"#16029
5.12.0 (2026-04-29)
Changed
- Changed
Phalcon\Assets\Managerfilter type check fromis_object()totypeofand updated the error message to"The filter is not valid"#16889 - Changed
Phalcon\Cache\AbstractCache::doDeleteMultiple()to delegate to the storage adapter’sdeleteMultiple()instead of looping over individualdelete()calls #16859 - Changed
Phalcon\Di\Exceptionmessage for missing services from"was not found in the dependency injection container"to"is not registered in the container"#16889 - Changed
Phalcon\Di\Service\Buildererror messages for service parameters to use double quotes instead of single quotes #16889 - Changed
Phalcon\Forms\Element\AbstractElement::getLocalTagFactory()to throwPhalcon\Forms\Exceptioninstead of silently creating a newTagFactorywhen neithersetTagFactory()nor a parentFormprovides one #16894 - Changed
Phalcon\Forms\Element\Select::render()to useTagFactory-basedHtml\Helper\Input\Selectinstead of the deprecatedPhalcon\Tag\Select#16894 - Changed
Phalcon\Html\TagFactoryto accept an optionalResponseInterfacein the constructor (useful forpreload) #16892 - Changed
Phalcon\Mvc\ControllerandPhalcon\Mvc\View\Engine\AbstractEngineto be events aware #16890 - Changed
Phalcon\Mvc\View\Engine\Volt\Compiler::setOptionsto return$thisnow #16891 - Changed calls to
globals_getandglobals_setin the code withPhalcon\Support\Settings::get()/set()#16884 - Changed exception messages across multiple components to use
"does not"instead of"doesn't"for consistency #16889
Added
- Added
Phalcon\Encryption\Security\Uuidfactory and versioned adapters (Version1–Version7) with aUuidInterfacecarrying standard RFC 4122 namespace constants; each version is a singleton cached by the factory, invoked viav1()–v7()#16326 - Added
Phalcon\Html\Helper\FriendlyTitle- available viaTagFactoryasfriendlyTitle[#16892(https://github.com/phalcon/cphalcon/issues/16892) - Added
Phalcon\Html\Helper\Input\Select::fromData()to populate select options from aSelectDataInterfaceprovider, with optgroup support #16894 - Added
Phalcon\Html\Helper\Input\Select\SelectDataInterface,Phalcon\Html\Helper\Input\Select\ArrayData, andPhalcon\Html\Helper\Input\Select\ResultsetDataas data providers for theSelecthelper #16894 - Added
Phalcon\Html\Helper\Preload- available viaTagFactoryaspreload;TagFactorynow accepts an optionalResponseInterfaceas its third constructor parameter [#16892(https://github.com/phalcon/cphalcon/issues/16892) - Added
Phalcon\Mvc\Model\Resultset::refresh()to re-execute the underlying query and update the resultset with fresh data from the database #16409 - Added
deleteMultiple()toPhalcon\Storage\Adapter\*to delete multiple keys in a single operation using native batch capabilities per adapter #16859 - Added key validation per entry in
Phalcon\Cache\AbstractCache::doDeleteMultiple()throwingInvalidArgumentExceptionfor keys containing invalid characters #16859 - Added named static factory methods
Phalcon\Forms\Exception::tagFactoryNotFound()andPhalcon\Forms\Exception::usingParameterRequired()#16894
Fixed
- Fixed
Phalcon\Db\Dialect\Postgresql::modifyColumn()to generate correct SQL when changing a boolean column’s default value: replacedemptycheck withhasDefault()to avoid treatingfalseas “no default”, removed the boolean-only branch that omitted theALTER TABLEprefix, and fixedcastDefault()to return PostgreSQL literalstrue/falseinstead of raw PHP booleans #15829 - Fixed
Phalcon\Db\Result\PdoResult::$rowCountto usenullas the uninitialised sentinel instead offalse, preventing a count of0rows being confused with “not yet counted” #16409 - Fixed
Phalcon\Dispatcher\AbstractDispatcher::dispatch()to refresh the localeventsManagerandhasEventsManagervariables afterinitialize()returns, so that an events manager attached to the dispatcher insideinitialize()is honoured for all subsequent dispatch events (afterInitialize,afterExecuteRoute,afterDispatch,afterDispatchLoop, etc.) #16440 - Fixed
Phalcon\Filter\Validation::bind()to skip the dependency injection container lookup whendatais empty, preventing unnecessaryDi\Exceptionerrors #16889 - Fixed
Phalcon\Filter\Validation\AbstractValidator::allowEmpty()to support a value-list array (e.g.[null, '']) in addition to the per-field map syntax, using strict===comparison so that'0'is never silently treated as empty #15491 - Fixed
Phalcon\Filter\Validation\AbstractValidator::messageFactory()to pass the joined field string toPhalcon\Messages\Messageinstead of the raw array when multiple fields are provided #16889 - Fixed
Phalcon\Filter\Validation\Validator\Alpha::validate()to returnfalsewhenallowEmptyis explicitly set tofalseand the submitted value isnullor an empty string #16200 - Fixed
Phalcon\Forms\Form::isValid()to apply field filters even when no validators are specified (again) #16830 - Fixed
Phalcon\Html\Escaper::css()andPhalcon\Html\Escaper::js()to return an empty string instead offalsewhen the input is empty or contains only a null codepoint #16889 - Fixed
Phalcon\Html\Helper\AbstractHelper::renderAttributes()to emit boolean HTML5 attributes (e.g.async,defer) as standalone attribute names instead ofasync="1"when the attribute value istrue#16304 - Fixed
Phalcon\Html\Helper\Breadcrumbsto support subdirectory installs: addedgetPrefix()/setPrefix()for a manual string prefix, and an optionalUrlInterfaceconstructor parameter that resolves links throughurl->get()(including base URI prepending and double-slash normalisation);TagFactoryaccepts an optional fourthUrlInterfaceargument and passes it toBreadcrumbsautomatically #14957 - Fixed
Phalcon\Http\Response::setStatusCode()exception message from"Non-standard statuscode given without a message"to"Non-standard status-code given without a message"#16889 - Fixed
Phalcon\Image\Adapter\AbstractAdapter::crop()to correctly handleoffsetX = 0andoffsetY = 0by changing the parameter types frominttovar; the previousinttyping caused Zephir to compile thenullcheck as0 == offsetin C, making explicit zero offsets indistinguishable from omitted (center) offsets #16156 - Fixed
Phalcon\Image\Adapter\Gd::processResize()to preserve PNG alpha channel transparency by replacingimagescale()withimagecreatetruecolor()+imagealphablending(false)+imagesavealpha(true)+imagecopyresampled()#16316 - Fixed
Phalcon\Image\Adapter\Imagick::processPixelate()to explicitly cast division result tointto prevent implicit float-to-int deprecation #16889 - Fixed
Phalcon\Mvc\Model::__get()to return the already-loaded related record instead of re-fetching from the database, preventing modifications to relation properties from being discarded #15554 - Fixed
Phalcon\Mvc\Model::__unserialize()andPhalcon\Mvc\Model::unserialize()to callonConstruct()after deserialization, so typed properties initialized inonConstructare correctly set when a model is restored from cache #15906 - Fixed
Phalcon\Mvc\Model::__unserialize()andPhalcon\Mvc\Model::unserialize()to restore snapshot as the current attributes (instead of null) when a model is deserialized with no pending changes, preventinggetChangedFields()from throwing after cache retrieval #15837 - Fixed
Phalcon\Mvc\Model::cloneResultMap()to call model setter methods (e.g.setName()) during ORM hydration whenorm.disable_assign_settersisfalse, making hydration behaviour consistent withassign(); setters inlocalMethods(Phalcon internals) are excluded #14810 - Fixed
Phalcon\Mvc\Model::collectRelatedToSave()to skip unmodifiedhasOne/hasManyrelated records that have snapshot data, preventing spuriousINSERT/UPDATEstatements when a relation is read but not changed #16000 - Fixed
Phalcon\Mvc\Model::doLowInsert()to also resetuniqueKey(in addition touniqueParams) after an auto-increment INSERT so that a subsequenthas()call on the same record rebuilds the primary-key condition from current attribute values; previously,uniqueParamswas cleared butuniqueKeywas kept, causinghas()to query with anullparameter and returnfalse, which madeSoftDeleteattempt to INSERT an already-existingbelongsTorelated record instead of updating it #16453 - Fixed
Phalcon\Mvc\Model::doLowUpdate()to skip columns whose string value matches the column’s function-call DB default (e.g.gen_random_uuid()) in the non-dynamic update path, preventing the function name from being passed as a bound string parameter and causing a DB type error #15828 - Fixed
Phalcon\Mvc\Model::doSave()to capture the model snapshot before the INSERT/UPDATE and restore it whenpostSaveRelatedRecordsfails and rolls back the transaction; previously, withorm.update_snapshot_on_saveenabled, the snapshot was permanently updated insidedoLowInsert/doLowUpdateeven when the transaction was rolled back, causing Dynamic Update to silently skip the write on the next save attempt #16410 - Fixed
Phalcon\Mvc\Model::getRelated()to return already-fetched relations from the internal cache (dirtyRelatedfirst, thenrelated) instead of always querying the database; cache is cleared aftersave()anddelete()to prevent stale results #16409 - Fixed
Phalcon\Mvc\Model::toArray()to catchErrorthrown by a getter that accesses an uninitialized typed PHP property (can occur whencloneResultMap()skips a null value for aNOT NULLcolumn, e.g. via aLEFT JOIN), returningnullinstead of propagating the error #15711 - Fixed
Phalcon\Mvc\Model::unserialize()to catchTypeErrorwhen assigning a serialisednullback to a typed non-nullable PHP property, preventing a crash on the second request when the model is loaded from a cache like APCu #15711 - Fixed
Phalcon\Mvc\Model\Manager::getRelationRecords()to apply reusable caching forhasManyToManyandhasOneThroughrelations;reusable: truewas previously ignored for through-relations #15934 - Fixed
Phalcon\Mvc\Model\Query::executeSelect()to embedPhalcon\Db\RawValuebind parameters directly in the SQL string instead of passing them to PDO #16350 - Fixed
Phalcon\Mvc\Model\Query::executeSelect()to use the write connection when the query contains aFOR UPDATEclause, instead of always using the read connection #16032 - Fixed
Phalcon\Mvc\Model\Query::getExpression()to emitNOT BETWEENinstead ofBETWEEN NOTfor thePHQL_T_BETWEEN_NOTtoken, producing valid SQL #16812 - Fixed
Phalcon\Mvc\Model\Query::getSelectColumn()to use the full model class name as thebaliaskey in a complex resultset when the model is namespaced (e.g.App\Models\Users), instead of incorrectly applyinglcfirst()to the fully-qualified name; non-namespaced models (e.g.Robots) retain the existinglcfirst()behaviour (robots) #16052 - Fixed
Phalcon\Mvc\Model\Query\Builder::getPhql()to use a named bind parameter (:APK0:) instead of embedding the raw primary-key value in the PHQL string whenfindFirst()is called with a numeric or numeric-string argument; this prevents unbounded growth of the internal PHQL AST cache (Query::$internalPhqlCache) in long-running CLI processes #14656 - Fixed
Phalcon\Mvc\Model\Resultset\Complex::current()to returnnullinstead of an empty model instance when aLEFT JOINproduces no matching row (all column values arenull) #16239 - Fixed
Phalcon\Mvc\Model\Transaction\Manager::collectTransaction()to keep the correct transactions when rebuilding the list after removal #16522 - Fixed
Phalcon\Mvc\Model\Transaction\Manager::commit()to remove each transaction from the pool after committing so that subsequentget()calls return a fresh transaction #16522 - Fixed
Phalcon\Mvc\Modelto handle the `lastInsertId correctly under Postgres #16920 #16436 #15775 - Fixed
Phalcon\Mvc\Router\Annotations::handle()to strip thecontrollerSuffixfrom the class name when a fully-qualified class name already includes it (e.g.App\Controllers\InvoicesController), preventing the doubled suffixInvoicesControllerController#16238 - Fixed
Phalcon\Paginator\Adapter\QueryBuilder::paginate()to correctly count groups whengroupBy()receives a multi-column array, using aSELECT DISTINCTsubquery instead of the PostgreSQL-incompatibleCOUNT(DISTINCT col1, col2)form #15912 - Fixed
Phalcon\Paginator\Adapter\QueryBuilder::paginate()to use thecolumnsoption as theCOUNT(DISTINCT ...)argument when aGROUP BYis present, allowing NULL-safe expressions to be supplied #15266 - Fixed
Phalcon\Storage\Adapter\Libmemcached,Phalcon\Storage\Adapter\RedisandPhalcon\Storage\Adapter\Weakto callinitSerializer()during construction #16889 - Fixed
Phalcon\Storage\Adapter\Redisto initializelifetimefrom options during construction #16889 - Fixed
Phalcon\Support\Helper\Json\Encodeto prefix theInvalidArgumentExceptionmessage with"json_encode error: "for consistency #16889 - Fixed the CI run to correctly use updated changes, and reuse artifacts #16920
- Fixed the CI run to now run Postgresql tests #16920
- Fixed the CI run to now run Sqlite tests #16920
Removed
Changelog
5.11.1 (2026-04-04)
Changed
Added
- Added
Phalcon\Storage\Adapter\RedisClusteradapter to support Redis Cluster connections #16867 - Added
Phalcon\Support\Settingsto be used for ini settings throughout the framework #16873
Fixed
- Fixed
Phalcon\Encryption\Security::computeHmac()to catch\ValueErrorthrown by PHP 8.1+ when an unknown hashing algorithm is passed #16893 - Fixed
Phalcon\Translate\Adapter\Gettext::setLocale()to callsetlocalewhen it is available, removing warnings in PHP 8.5 #16886
Removed
Upgrade
Developers can upgrade using PIE
pie install phalcon/cphalcon-5.12.0
To compile from source, follow our installation document
Chat - Q&A
Support
Social Media
Videos
<3 Phalcon Team