Skip to content

Releases: phalcon/cphalcon

v5.14.1

08 Jun 19:28
4e4bb37

Choose a tag to compare

5.14.1 (2026-06-08)

Tools

  • Zephir Parser v2.0.4
  • Zephir 0.23.0 (development - bae82f7bd)

Changed

  • Consolidated the Phalcon\Auth dual-container handling (new Phalcon\Container\Container vs legacy Phalcon\Di\Di) behind a single internal Phalcon\Auth\Internal\ContainerResolver. #17088 [doc]
  • Renamed the private Phalcon\Events\Manager dispatch hot-loop helper to runQueue(). #17006 [doc]
  • Reworked the Phalcon\Auth access gates into Specification-style policies. Phalcon\Contracts\Auth\Access\Access::isAllowed() now receives the current identity and the request context: isAllowed(Guard $guard, string $actionName, array $context = []), where context carries handler (controller / task / Micro component name), module (MVC module, when present), and params (dispatcher or route parameters). #17088 [doc]
  • Phalcon\Auth\Manager::access() now resolves gates through Phalcon\Auth\Access\AccessLocator from the container instead of constructing them directly. #17088 [doc]

Added

  • Added Phalcon\Auth\Access\Acl - an ACL-backed access gate that incorporates the role-based authorization of the old Firewall component (#14630) into the Auth layer. The gate checks the authenticated user's role against a Phalcon\Acl\Adapter\AdapterInterface: the ACL component is the handler context key (prefixed with module and a configurable separator when present), the ACL access is the action name, and params are passed through to callable ACL rules. Unauthenticated requests resolve to a configurable guest role (default guest); authenticated users supply their role via Phalcon\Acl\RoleAwareInterface. #17088 [doc]
  • Added Phalcon\Auth\Micro\AuthMicroListener to enforce the active Auth access gate on Phalcon\Mvc\Micro route execution (attach to the micro event space).#17088 [doc]
  • Added Phalcon\Events\Manager::dispatch(object $event, string|array|null $name = null, ?object $source = null) for object/class-based event dispatch built on Phalcon's own Phalcon\Contracts\Events\Stoppable. Listeners are routed by an explicit name (a string, or a [class, method] array) or by the event's class name and receive the event object. #17006 [doc]
  • Added beforeBind and afterBind hook methods to Phalcon\Forms\Form. When defined on a form subclass, beforeBind(array $data, ?object $entity) runs at the start of bind() (returning false cancels the bind) and afterBind(?object $entity) runs after the data has been assigned. Both also fire when bind() is reached through isValid(). #14598 [doc]
  • Added a sync option to many-to-many (hasManyToMany) relations and a chainable Phalcon\Mvc\Model::setSync() method to synchronize related records on save. When enabled, saving deletes the intermediate rows for records no longer present in the assigned array (add/update/delete), instead of only appending. #17071 [doc]
  • Added a trace() method to Phalcon\Logger\Logger together with a new TRACE log level (value 9, label trace). #17047 [doc]
  • Added a {% verbatim %}/{% endverbatim %} tag to Volt. Its body is emitted exactly as written, without being parsed by Volt, so {{ ... }}, {% ... %}, {# ... #} and constructs such as <?xml ... ?> or client-side templates (Handlebars, Mustache, Angular) pass through untouched. #17085 [doc]
  • Added support for JOIN clauses in PHQL UPDATE statements (e.g. UPDATE Invoices INNER JOIN Customers ON ... SET ... WHERE Customers.cst_id = :id:). The join is used to filter the records to update; the statement still targets a single model. #16984 [doc]

Fixed

  • Fixed PHQL parser cache to use string-keyed lookups (zend_hash_str_find/zend_hash_str_update) instead of integer keys derived from zend_inline_hash_func, eliminating hash collisions that caused different PHQL queries to return identical cached ASTs #14791
  • Fixed Phalcon\Annotations\Reader failing to parse a docblock when an annotation argument is a string literal containing a parenthesis (e.g. @SomeAnnotation(key='value(')). The docblock pre-scan that locates each @Annotation(...) span counted every (/), including those inside quoted values, so an unbalanced parenthesis in a string consumed the rest of the comment and produced a "Scanning error". #16084
  • Fixed Phalcon\Di\Injectable::__get() to no longer cache resolved services as dynamic object properties. Services accessed via magic properties (e.g. $this->request) are now re-resolved through the container on each access, so replacing or updating a service in the container is reflected in controllers, views, and other injectable classes. Properties already declared on the class continue to be populated. #17052
  • Fixed Phalcon\Encryption\Crypt::decrypt() to verify the HMAC tag with the constant-time hash_equals() instead of the identity operator, removing an observable timing discrepancy in the tag comparison (CWE-208, CWE-347) . The tag is now also verified before the decrypted text is unpadded, and truncated tags are rejected by the unequal-length path of hash_equals(). #17090 [doc]
  • Fixed Phalcon\Mvc\Model\Query\Builder::orderBy() when the array syntax is used with complex PHQL expressions. Previously any array item containing a space was split as a simple column direction pair, corrupting expressions such as CASE WHEN inv_status_flag = 1 THEN 0 ELSE 1 END ASC. The builder now only treats a trailing ASC/DESC as the direction (autoescaping a simple column) and preserves complex expressions verbatim. #17077
  • Fixed Phalcon\Mvc\Model\Query (PHQL) parsing of identifiers whose name begins with the NOT keyword. Columns, tables, and aliases such as notice_id were truncated to ice_id (the leading not was dropped), causing the database to report the column as unknown - most visibly in Phalcon\Mvc\Model\Query\Builder join conditions built via createBuilder(). The scanner's re2c backtracking marker shared the token-start pointer, so the NOT BETWEEN rule advanced it past not; escaped identifiers containing internal escapes (e.g. [col\[0\]]) were corrupted by the same root cause. #16831 #17087
  • Fixed the compilation failure ('name_zv' undeclared) in Phalcon\Container\Container::callableGet() and callableNew(). Both closures captured the typed string name parameter directly via use (name). #17078

Removed

  • Removed the unfinished {% raw %}/{% endraw %} Volt tag. It never produced output (compilation threw UnknownVoltStatement) and its body was parsed rather than emitted literally. Use {% verbatim %} instead. #17085

v5.14.0

04 Jun 15:07
33617b0

Choose a tag to compare

5.14.0 (2026-06-04)

Tools

  • Zephir Parser v2.0.2
  • Zephir 0.22.0 (development - 9d2def774)

Changed

  • Alignment with v6; docblocks; sorting; return types; minor fixes (image watermark opacity calc, serializer/helpers, readonly-becoming-mutable, ACL local access). #17055
  • Changed return types to -> <static> or -> <self> in various components. The change is a covariant narrowing on implementation methods and does not touch any interface contracts, so userland classes that implement Phalcon interfaces and return the interface type continue to work unchanged. #17035
  • Internal performance work across Autoload, Dispatcher, Annotations, Db, Mvc\Model, Mvc\Model\Query, Tag, Assets, Acl\Adapter\Memory, Http\Request, Encryption\Crypt. Behavior preserved. #17049
  • Moved CI tools/scripts in resources/ removed unused ones #17066
  • Moved docker in resources/ #17066
  • Refactored docker images (more flexible less cruft) #17066
  • Reorganization of quality tool config files (in resources/) #17066
  • Phalcon\Autoload\Loader getters (getDirectories, getExtensions, getFiles) return arrays keyed by the value string instead of by a SHA256 hash of it; iteration order and contents are unchanged. #17049 [doc]
  • Phalcon\Mvc\Router::handle() internal optimizations: O(1) hash lookup for literal-URI routes; per-HTTP-method buckets; hot-loop reads; PCRE patterns chunked; per-route metadata cache deduplicated by route id. #17012 [doc]
  • Phalcon\Mvc\Router\Route::getCompiledHostName() now uses cache for hostname/converters. #17012 [doc]

Added

  • Added a new dependency-injection container under Phalcon\Container, with its contracts under Phalcon\Contracts\Container. It adds:
    • Phalcon\Container\Container / Phalcon\Container\ContainerFactory - the container and its factory, configured through Phalcon\Contracts\Container\Service\Provider providers (Phalcon\Container\Provider\Web, Phalcon\Container\Provider\Cli).
    • Phalcon\Container\Definition\ServiceDefinition - fluent service definitions with autowiring, factories, extenders, tags, aliases, and configurable service lifetimes (Phalcon\Container\Definition\ServiceLifetime).
    • Phalcon\Container\Resolver\Resolver - reflection-based constructor / method / parameter autowiring, plus the Phalcon\Container\Resolver\Lazy\* family for lazy resolution (Get, GetCall, NewInstance, Call, Env, CsEnv, ArrayValues, etc.).
    • Phalcon\Container\Exceptions\* - granular, per-cause exceptions (ServiceNotFound, CircularAliasFound, FrozenDefinition, CannotResolveParameter, NoProcessorFound, etc.). #16897 [doc]
  • Added a new authentication and authorization layer under Phalcon\Auth, with its contracts under Phalcon\Contracts\Auth. Built on top of Phalcon\Container, it adds:
    • Phalcon\Auth\Manager / Phalcon\Auth\ManagerFactory - the central manager that wires guards and access gates together, and its factory.
    • Phalcon\Auth\AuthUser - a lightweight user value object returned by array-backed adapters when no application model class is configured.
    • Guards under Phalcon\Auth\Guard - Session and Token (with AbstractGuard and UserRemember), resolved via Phalcon\Auth\Guard\GuardLocator and configured through Phalcon\Auth\Guard\Config\* (AbstractGuardConfig, SessionGuardConfig, TokenGuardConfig).
    • Adapters under Phalcon\Auth\Adapter - Memory, Model, and Stream user providers (with AbstractAdapter and AbstractArrayAdapter), resolved via Phalcon\Auth\Adapter\AdapterLocator and configured through Phalcon\Auth\Adapter\Config\* (AbstractAdapterConfig, MemoryAdapterConfig, ModelAdapterConfig, StreamAdapterConfig).
    • Access gates under Phalcon\Auth\Access - Auth and Guest (with AbstractAccess), resolved via Phalcon\Auth\Access\AccessLocator.
    • Dispatcher listeners Phalcon\Auth\Mvc\AuthDispatcherListener and Phalcon\Auth\Cli\AuthDispatcherListener (with Phalcon\Auth\AbstractAuthDispatcherListener) to guard MVC and CLI dispatch.
    • Phalcon\Auth\Exception plus granular Phalcon\Auth\Exceptions\* (AccessDenied, ConfigRequiresNonEmptyValue, DataMustContainIdKey, DoesNotImplement, FileCannotRead, FileDoesNotContainJson, FileDoesNotExist, FileNotValidJson).
    • Contracts under Phalcon\Contracts\Auth - Manager, AuthUser, AuthRemember, RememberToken, Access\Access, Adapter\Adapter, Adapter\AdapterConfig, Adapter\RememberAdapter, Guard\Guard, Guard\GuardConfig, Guard\GuardStateful, Guard\BasicAuth.
    • Phalcon\Support\AbstractLocator - the shared service-locator base extended by the guard, adapter, and access locators. #16273 [doc]
  • Added granular exception classes across the framework. Every namespace that previously surfaced failures through a single umbrella Phalcon\<Namespace>\Exception (or its sub-namespace counterpart) now ships per-cause classes under a sibling Exceptions/ folder. Each new class extends the existing per-namespace parent so catch (Phalcon\<Namespace>\Exception $e) continues to work unchanged. New classes:
    • Phalcon\Acl\Exceptions
      • AccessRuleNotFound
      • CircularInheritanceError
      • ElementNotFound
      • ForbiddenWildcard
      • InvalidAccessList
      • InvalidComponentImplementation
      • InvalidRoleImplementation
      • InvalidRoleType
      • MissingFunctionParameters
      • ParameterTypeMismatch
      • RoleNotFoundException
    • Phalcon\Annotations\Exceptions
      • AnnotationNotFound
      • AnnotationsDirectoryNotWritable
      • CannotReadAnnotationData
      • UnknownAnnotationExpression
    • Phalcon\Application\Exceptions
      • ModuleNotRegistered
    • Phalcon\Assets\Exceptions
      • AssetSourceTargetCollision
      • CannotReadAsset
      • CollectionNotFound
      • InvalidAssetSourcePath
      • InvalidAssetTargetPath
      • InvalidFilter
      • InvalidTargetPath
      • TargetPathIsDirectory
    • Phalcon\Autoload\Exceptions
      • LoaderDirectoriesNotArray
      • LoaderMethodNotCallable
    • Phalcon\Cache\Exception
      • CacheKeysNotIterable
      • InvalidCacheKey
    • Phalcon\Cli\Console\Exceptions
      • ConsoleModuleNotRegistered
      • ContainerRequired
      • InvalidModuleDefinitionPath
      • ModuleDefinitionPathNotFound
    • Phalcon\Cli\Router\Exceptions
      • BeforeMatchNotCallable
      • InvalidRoutePaths
      • RouterArgumentsInvalidType
    • Phalcon\Config\Exceptions
      • CannotLoadConfigFile
      • ConfigNotArrayOrObject
      • GroupedAdapterRequiresArray
      • InvalidMergeData
      • MissingConfigOption
      • MissingFileExtension
      • MissingYamlExtension
    • Phalcon\DataMapper\Pdo\Exception
      • DriverNotSupported
      • UnknownDriverMethod
      • UnknownQueryMethod
    • Phalcon\Db\Exceptions
      • CannotInsertWithoutData
      • CannotPrepareStatement
      • CheckExpressionRequired
      • ColumnTypeRejectsAutoIncrement
      • ColumnTypeRejectsScale
      • ColumnTypeRequired
      • ConflictTargetColumnRequired
      • ConflictUpdateColumnRequired
      • ForeignKeyColumnsRequired
      • GeneratedAutoIncrementConflict
      • GeneratedDefaultConflict
      • IncompleteBindTypes
      • InvalidBindParameter
      • InvalidCheckExpression
      • InvalidGenerationExpression
      • InvalidGroupByExpression
      • InvalidIndexColumns
      • InvalidIndexDirections
      • InvalidIndexWhere
      • InvalidListExpression
      • InvalidOrderByExpression
      • InvalidSqlExpression
      • InvalidSqlExpressionType
      • InvalidUnaryExpression
      • InvalidWhereConditions
      • MatchedParameterNotFound
      • MaterializedViewsNotSupported
      • MissingDefinitionKey
      • MissingForeignKeyChecks
      • MissingSqliteDatabase
      • MysqlOnConflictNotSupported
      • NestedTransactionChangeBlocked
      • NoActiveTransaction
      • ReferencedColumnCountMismatch
      • ReferencedColumnsRequired
      • ReferencedTableRequired
      • ReturningNotSupported
      • ReturningRequiresColumn
      • SavepointsNotSupported
      • SqliteAlterCheckNotSupported
      • SqliteAlterColumnNotSupported
      • SqliteAlterForeignKeyNotSupported
      • SqliteAlterPrimaryKeyNotSupported
      • SqliteDropCheckNotSupported
      • SqliteDropForeignKeyNotSupported
      • SqliteDropPrimaryKeyNotSupported
      • TableMustHaveColumn
      • UnrecognizedDataType
      • UpdateFieldCountMismatch
    • Phalcon\Di\Exceptions
      • AliasAlreadyInUse
      • AliasNameMustBeString
      • ArgumentTypeRequired
      • CallArgumentsMustBeArray
      • `CircularAliasRefere...
Read more

v5.13.0

18 May 15:20
70ec768

Choose a tag to compare

5.13.0 (2026-05-18)

Changed

  • Changed Phalcon\Contracts\Support\Collection to declare the expanded method surface (column, each, filter, first, getType, isEmpty, keys, last, map, reduce, replace, sort, values, where) so the contract matches the implementation #17000 [doc]
  • Changed Phalcon\Events\Event to be declared final. The class is a value object holding type, source, data, cancelable, and stopped; no subclasses exist in the cphalcon tree and any future typed-event work would add new sibling classes implementing EventInterface rather than extending Event. Marking it final lets the C extension fold the per-fire getters (getType, getSource, getData, isCancelable, isStopped, isPropagationStopped) into direct dispatch. BC note: any userland class MyEvent extends Event now fails #17006 [doc]
  • Changed Phalcon\Events\Manager::attach() to classify the handler kind once at attach time so the dispatch loop can route via a single branch instead of running instanceof Closure / is_callable / typeof per fire per listener. Four kinds are recognized: 0 = Closure (direct invocation via Zephir's {handler}(args) syntax, no arg-array allocation), 1 = [obj, method] array callable (direct dynamic dispatch handler[0]->{handler[1]}(args), no call_user_func_array overhead), 2 = plain object with method named after the event - the classic Phalcon listener pattern (class name is captured at attach time and stored on the tuple to skip get_class() per fire), 3 = generic callable (string function name, invokable object, [class, staticMethod]) routed through call_user_func_array. The subscriber array-form attach paths ([methodName, priority] and [[methodA, priorityA], [methodB, priorityB]]) now route through insertHandlerEntry directly with kind=1, bypassing the classification cascade since the resulting handler shape is already known. methodExistsCache access in the dispatch loop is tightened to a single isset fast path #17006 [doc]
  • Changed Phalcon\Events\Manager::detach() to drop the eventType key entirely when its queue empties (removing the last listener), so hasListeners() and fire()'s short-circuit tell the truth. Previously an emptied queue left the key in place with an empty array value, causing isset($this->events[$eventType]) to return true with no actual listeners to dispatch to. The matching DetachTest expectations are updated to reflect the new contract #17006 [doc]
  • Changed Phalcon\Events\Manager::detachAll(null) to reset events to [] instead of null. The previous null reset broke isset($this->events[$key]) semantics inside attach() and fire() until the next assignment refilled the property; the empty-array form keeps all access paths consistent #17006 [doc]
  • Changed Phalcon\Events\Manager::fire() and Phalcon\Events\Manager::fireAll() to wrap dispatch in try { ... } catch \Throwable, ex { cleanup; throw ex; }. A throwing listener restores stashed responses (if nested) and decrements fireDepth back to its pre-call value before the exception propagates, so manager state stays clean for the next fire - important for long-lived managers (workers, daemons) where a single dirty teardown would poison every subsequent fire #17006 [doc]
  • Changed Phalcon\Events\Manager::fire() to parse the eventType only once per unique string and cache the result ([typePrefix, eventName]) in an internal eventNameCache keyed by the original string. Repeated fires of the same event name (db:beforeQuery × N per request, model lifecycle events on hot save paths) collapse to a single hash lookup after warm-up. The cache never needs invalidation because the parse is deterministic for a given input string. fire() also short-circuits before allocating the Event instance when no listeners match either the type queue or the fully-qualified queue - in production most fires have zero matching listeners (model lifecycle events with no user-attached behavior, DB events without a tracer), so the avoided allocation is significant in hot paths #17006 [doc]
  • Changed Phalcon\Events\Manager::fire(), Phalcon\Events\Manager::attach(), and Phalcon\Events\Manager::fireQueue() to be declared final. The class itself stays open so genuine subclasses that only add new methods continue to work, but the dispatch hot path is locked to enable C-level direct dispatch on the three methods that run per-event. The remaining public surface (addSubscriber, removeSubscriber, detach, detachAll, getListeners, getResponses, getSubscribers, hasListeners, isCollecting, isValidHandler, collectResponses, enablePriorities, arePrioritiesEnabled, isStrict, setStrict, fireAll, clearSubscribers) is left non-final so decorator-style subclasses that wrap these less-hot methods can still override them. BC note: subclasses that override fire, attach, or fireQueue now fail #17006 [doc]
  • Changed Phalcon\Events\Manager::fireQueue() to be a thin BC-preserving wrapper around a new private Phalcon\Events\Manager::dispatch() helper. The public signature fireQueue(array $queue, EventInterface $event) is unchanged; the framework's own fire() path bypasses fireQueue and calls dispatch() directly with hoisted arguments (eventName, source, data, cancelable, collect) so the second dispatch leg of a two-queue fire() (type queue + fully-qualified queue) does not re-extract identical values from the event. The dispatch path also applies a single-handler fast path: when the queue has exactly one listener (very common in unit tests and lightly-instrumented production), the foreach plus per-iteration cancelable/isStopped check is skipped and dispatch goes straight through the type-branch #17006 [doc]
  • Changed Phalcon\Events\Manager dispatch return-value contract to last non-null wins. Previously every listener return overwrote the running status, so a chain of ("value", null) ended with fire() returning null and silently losing the earlier real value. The new contract only updates status when the listener return is non-null - the last meaningful return survives. The stop() determinism rule overrides last-non-null: when a listener calls $event->stop() (and cancelable=true), that listener's return value is what fire() returns - even if null - because the caller asked for the stopping listener's verdict explicitly. Returning false from a listener does not short-circuit the queue; callers wanting to stop downstream listeners must call $event->stop(). Consumers like Phalcon\Mvc\Dispatcher that interpret a false return from fire() as a cancel signal are unaffected because that check happens in their own dispatch logic, not in the events manager #17006 [doc]
  • Changed Phalcon\Events\Manager listener storage from SplPriorityQueue to a sorted array of [handler, type, priority] tuples (with an additional className element on type=2 tuples). The SplPriorityQueue::EXTR_BOTH clone-per-fire and O(n) setExtractFlags() rebuild on detach are eliminated; the "empty heap" warnings produced by SplPriorityQueue on never-fired event types disappear as a side effect. Insert order under the same priority is preserved (FIFO). When enablePriorities is off (the default), insertHandlerEntry short-circuits to a plain append - the sorted-insert loop only runs when priorities are explicitly enabled. When it does run, the insert uses array_splice instead of a per-element rebuild #17006 [doc]
  • Changed Phalcon\Html\Escaper into a façade over five per-context escapers (Phalcon\Html\Escaper\HtmlEscaper, AttributeEscaper, CssEscaper, JsEscaper, UrlEscaper); each is reachable via getXxxEscaper()/setXxxEscaper() so individual contexts can be swapped without subclassing the façade. The legacy setEncoding(), setFlags(), and setDoubleEncode() setters fan out to all sub-escapers so existing code keeps working #16971 [doc]
  • Changed Phalcon\Html\Helper\AbstractSeries::__toString() to ksort() its store before rendering so entries are emitted in position order rather than registration order. #16971 [doc]
  • Changed Phalcon\Html\Helper\Input\Checkbox and Phalcon\Html\Helper\Input\Radio to extend a new shared Phalcon\Html\Helper\Input\AbstractChecked; Radio no longer extends Checkbox. Two paths now render checked="checked": the unconditional opt-ins ["checked" => "checked"] (case-insensitive) and ["checked" => true], and a value-match path comparing the supplied checked attribute against the input's value (== by default for mixed i...
Read more

v5.12.1

30 Apr 21:34
bfe646a

Choose a tag to compare

5.12.1 (2026-04-30)

Added

  • Added Phalcon\Db\Column::TYPE_UUID constant (value 29) and added support for PostgreSQL native uuid column type in Phalcon\Db\Adapter\Pdo\Postgresql and Phalcon\Db\Dialect\Postgresql #16840
  • Added support for Phalcon\Mvc\Url static base URI in Phalcon\Assets\Manager; when a DI container is set and a url service is available, local asset paths are now resolved via getStatic() instead of a bare / prefix #16570

Fixed

  • Fixed Phalcon\Mvc\Model\MetaDataInterface::readMetaDataIndex() and Phalcon\Mvc\Model\MetaData::readMetaDataIndex() declaring return type as array|null when the method can also return a string (e.g. for MODELS_IDENTITY_COLUMN), causing a PHP fatal error on PHP 8+ #16613
  • Fixed Phalcon\Mvc\View\Engine\Volt\Compiler::statementList() returning null instead of string when processing templates that consist entirely of block-mode statements, causing a PHP fatal error on PHP 8+ #16613
  • Fixed Phalcon\Forms\Element\Select::render() multiselect regression introduced in v5.12.0 (#16894) by reverting to Phalcon\Tag\Select::selectField(); the new Html\Helper\Input\Select only supports a single selected value and does not handle array values required for multiselect #16946
  • Fixed Phalcon\Html\Helper\Input\AbstractInput::setValue() ignoring empty string "" as a valid value, causing Checkbox and Radio inputs with value="" to never render checked="checked" even when the checked attribute matched #16648
  • Fixed Phalcon\Http\Response\Cookies::get() throwing an opaque fatal error when no DI container has been set; it now throws Phalcon\Http\Cookie\Exception with a descriptive message before accessing the container #16650
  • Fixed Phalcon\Mvc\Model\MetaData::writeMetaDataIndex() prematurely initializing a child model's metadata with the parent's source table when skipAttributes() (or skipAttributesOnCreate()/skipAttributesOnUpdate()) is called inside a parent model's initialize() and the child calls parent::initialize() before setting its own source, corrupting the child's attribute list and breaking relationship resolution #16544
  • Fixed Phalcon\Storage\Serializer\Json::serialize() rejecting plain objects (e.g. stdClass) that do not implement JsonSerializable; json_encode() handles such objects natively and the guard was unnecessary #16630
  • Fixed Phalcon\Mvc\Model\Manager retaining a model instance in lastInitialized after initialization and Phalcon\Mvc\Model not clearing the reusable-records cache after save(), causing memory to grow unboundedly in long-running processes #16566
  • Fixed Phalcon\Paginator\Adapter\QueryBuilder::paginate() returning wrong total item count when the query uses DISTINCT columns; the count now uses COUNT(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 in groupBy(), 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

v5.12.0

29 Apr 21:21
83ee909

Choose a tag to compare

5.12.0 (2026-04-29)

Changed

  • Changed Phalcon\Assets\Manager filter type check from is_object() to typeof and updated the error message to "The filter is not valid" #16889
  • Changed Phalcon\Cache\AbstractCache::doDeleteMultiple() to delegate to the storage adapter's deleteMultiple() instead of looping over individual delete() calls #16859
  • Changed Phalcon\Di\Exception message for missing services from "was not found in the dependency injection container" to "is not registered in the container" #16889
  • Changed Phalcon\Di\Service\Builder error messages for service parameters to use double quotes instead of single quotes #16889
  • Changed Phalcon\Forms\Element\AbstractElement::getLocalTagFactory() to throw Phalcon\Forms\Exception instead of silently creating a new TagFactory when neither setTagFactory() nor a parent Form provides one #16894
  • Changed Phalcon\Forms\Element\Select::render() to use TagFactory-based Html\Helper\Input\Select instead of the deprecated Phalcon\Tag\Select #16894
  • Changed Phalcon\Html\TagFactory to accept an optional ResponseInterface in the constructor (useful for preload) #16892
  • Changed Phalcon\Mvc\Controller and Phalcon\Mvc\View\Engine\AbstractEngine to be events aware #16890
  • Changed Phalcon\Mvc\View\Engine\Volt\Compiler::setOptions to return $this now #16891
  • Changed calls to globals_get and globals_set in the code with Phalcon\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\Uuid factory and versioned adapters (Version1Version7) with a UuidInterface carrying standard RFC 4122 namespace constants; each version is a singleton cached by the factory, invoked via v1()v7() #16326
  • Added Phalcon\Html\Helper\FriendlyTitle - available via TagFactory as friendlyTitle [#16892(https://github.com//issues/16892)
  • Added Phalcon\Html\Helper\Input\Select::fromData() to populate select options from a SelectDataInterface provider, with optgroup support #16894
  • Added Phalcon\Html\Helper\Input\Select\SelectDataInterface, Phalcon\Html\Helper\Input\Select\ArrayData, and Phalcon\Html\Helper\Input\Select\ResultsetData as data providers for the Select helper #16894
  • Added Phalcon\Html\Helper\Preload - available via TagFactory as preload; TagFactory now accepts an optional ResponseInterface as its third constructor parameter [#16892(https://github.com//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() to Phalcon\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() throwing InvalidArgumentException for keys containing invalid characters #16859
  • Added named static factory methods Phalcon\Forms\Exception::tagFactoryNotFound() and Phalcon\Forms\Exception::usingParameterRequired() #16894

Fixed

  • Fixed Phalcon\Db\Dialect\Postgresql::modifyColumn() to generate correct SQL when changing a boolean column's default value: replaced empty check with hasDefault() to avoid treating false as "no default", removed the boolean-only branch that omitted the ALTER TABLE prefix, and fixed castDefault() to return PostgreSQL literals true/false instead of raw PHP booleans #15829
  • Fixed Phalcon\Db\Result\PdoResult::$rowCount to use null as the uninitialised sentinel instead of false, preventing a count of 0 rows being confused with "not yet counted" #16409
  • Fixed Phalcon\Dispatcher\AbstractDispatcher::dispatch() to refresh the local eventsManager and hasEventsManager variables after initialize() returns, so that an events manager attached to the dispatcher inside initialize() 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 when data is empty, preventing unnecessary Di\Exception errors #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 to Phalcon\Messages\Message instead of the raw array when multiple fields are provided #16889
  • Fixed Phalcon\Filter\Validation\Validator\Alpha::validate() to return false when allowEmpty is explicitly set to false and the submitted value is null or 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() and Phalcon\Html\Escaper::js() to return an empty string instead of false when 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 of async="1" when the attribute value is true #16304
  • Fixed Phalcon\Html\Helper\Breadcrumbs to support subdirectory installs: added getPrefix()/setPrefix() for a manual string prefix, and an optional UrlInterface constructor parameter that resolves links through url->get() (including base URI prepending and double-slash normalisation); TagFactory accepts an optional fourth UrlInterface argument and passes it to Breadcrumbs automatically #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 handle offsetX = 0 and offsetY = 0 by changing the parameter types from int to var; the previous int typing caused Zephir to compile the null check as 0 == offset in C, making explicit zero offsets indistinguishable from omitted (center) offsets #16156
  • Fixed Phalcon\Image\Adapter\Gd::processResize() to preserve PNG alpha channel transparency by replacing imagescale() with imagecreatetruecolor() + imagealphablending(false) + imagesavealpha(true) + imagecopyresampled() #16316
  • Fixed Phalcon\Image\Adapter\Imagick::processPixelate() to explicitly cast division result to int to 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() and Phalcon\Mvc\Model::unserialize() to call onConstruct() after deserialization, so typed properties initialized in onConstruct are correctly set when a model is restored from cache #15906
  • Fixed Phalcon\Mvc\Model::__unserialize() and Phalcon\Mvc\Model::unserialize() to restore snapshot as the current attributes (instead of null) when a model is deserialized with no pending changes, preventing getChangedFields() from throwing after cache retrieval #15837
  • Fixed Phalcon\Mvc\Model::cloneResultMap() to call model setter methods (e.g. setName()) during ORM hydration when orm.disable_assign_setters is false, making hydration behaviour consistent with assign(); setters in localMethods (Phalcon internals) are excluded #14810
  • Fixed Phalcon\Mvc\Model::collectRelatedToSave() to skip unmodified hasOne/hasMany related records that have snapshot data, preventing spurious INSERT/UPDATE statem...
Read more

v5.11.1

04 Apr 19:19
a05f6aa

Choose a tag to compare

5.11.1 (2026-04-04)

Changed

Added

  • Added Phalcon\Storage\Adapter\RedisCluster adapter to support Redis Cluster connections #16867
  • Added Phalcon\Support\Settings to be used for ini settings throughout the framework #16873

Fixed

  • Fixed Phalcon\Encryption\Security::computeHmac() to catch \ValueError thrown by PHP 8.1+ when an unknown hashing algorithm is passed #16893

Removed

v5.11.0

03 Apr 23:45
2a6a764

Choose a tag to compare

5.12.0 (2026-04-03)

Changed

  • Changed Phalcon\Filter\Sanitize\IP to optimize the sanatization of IP address #16838
  • Changed Phalcon\Encryption\Security\JWT\Builder::setPassphrase() to require digits and special characters #16847
  • Changed Phalcon\Encryption\Security\JWT\Builder::getAudience() to return an empty array if not set #16846
  • Changed Phalcon\Encryption\Security\Random::base() to use 16 bits by default #16845
  • Changed Phalcon\Logger\Logger to use lowercase when reporting log levels (previously uppercase) #16852
  • Changed Phalcon\Logger\Adapter\Stream to use a more efficient way to write messages in the logger instead of opening and closing the stream per message #16852
  • Changed Phalcon\Logger\Adapter\Syslog to use the Enum instead of Logger constants #16852
  • Changed the whole testing suite to run on phpunit only #16860 #16861 #16862

Added

  • Added PIE (PHP Installer for Extensions) support #16832
  • Added the ability to specify aliases for Phalcon\Di\Di services.#13042
  • Added Phalcon\Encryption\Security\JWT\Validator::validateClaim() to validate custom claims #16843

Fixed

  • Fixed Phalcon\Forms\Form::isValid() to apply field filters even when no validators are specified #16936
  • Fixed Phalcon\Http\Request method getClientAddress() when using trustForwardedHeader #16836
  • Fixed Phalcon\Acl\Adapter\Memory::isAllowed() and Phalcon\Mvc\Model\Binder to handle PHP 8.1+ union and intersection types by checking for ReflectionNamedType before calling getName() #16261
  • Fixed memory leak in PHQL parser (phql_internal_parse_phql()) during repeated query execution. #16854
  • Fixed a deadlock issue when running the db suite #16862

Removed

v5.10.0

24 Dec 19:33
011b92b

Choose a tag to compare

5.10.0 (2025-12-25)

Changed

  • Changed bind() and validate() method in Phalcon\Filter\Validation and Phalcon\Filter\Validation\ValidationInterface to accept $whitelist array of only allowed fields to be mutated when using entity #16800
  • Changed Phalcon\Storage\Adapters\Libmemcached::getAdapter() to use 50ms for \Memcached::OPT_CONNECT_TIMEOUT #16818
  • Changed Phalcon\Html\Helper\Input\* to honor Docbloc directives #16778

Added

  • Added fails() method helper to Phalcon\Filter\Validation useful for standalone validation #16798

Fixed

  • Fixed Phalcon\Config\Adapter\Yaml constructor to handle null return values from yaml_parse_file(), ensuring empty configuration files are treated as empty arrays instead of throwing errors.
  • Fixed Phalcon\Http\Request method getClientAddress(true) to return correct IP address from trusted forwarded proxy. #16777
  • Fixed Phalcon\Http\Request method getPost() to correctly return json data as well and unified both getPut() and getPatch() to go through the same parsing method. #16792
  • Fixed Phalcon\Filter\Validation method bind() and validate() to correctly bind data when using entity as well as skip binding of fields not included in $whitelist #16800
  • Fixed Phalcon\Http\Request method getPostData() when Content-Type header is not set #16804
  • Fixed Phalcon\Events\ManagerInterface adding priority property #16817
  • Fixed Phalcon\Storage\Adapters\Libmemcached::getAdapter() to correctly merge adapter options #16818
  • Fixed Phalcon\Encryption\Crypt method checkCipherHashIsAvailable(string $cipher, string $type) to correctly check the cipher or hash type #16822
  • Fixed Phalcon\Mvc\Model docblocks #16825

v5.9.3

22 Apr 18:56
a58902f

Choose a tag to compare

5.9.3 (2025-04-19)

Changed

  • Added Multi-Stage Dockerfile and Github action for release Docker images to ghcr.io and Docker Hub. #16752

Added

  • Added Phalcon\Mvc\Router::setUriSource() to allow URI processing from $_GET['url'] or $_SERVER['REQUEST_URI'] as it was in v3 #16741

Fixed

  • Fixed Phalcon\Mvc\Router to correctly handle numeric URI parts as it was in v3 #16741
  • Fixed Phalcon\Mvc\Model\Binder to use ReflectionParameter::getType() instead of deprecated method, PHP 8.0 or higher issue. #16742
  • Fixed Phalcon\Mvc\Model\Query to check if cache entry exists. #16747
  • Fixed Phalcon\Mvc\Router to correctly match route when using query string URIs. #16749
  • Fixed Phalcon\Mvc\Model::cloneResultset to properly parse fields that do not accept null values #16736

Removed

v5.9.2

03 Apr 17:06
dd37339

Choose a tag to compare

5.9.2 (2025-04-03)

Fixed

  • Fixed Phalcon\Translate\Adapter\Csv the escape argument is explicitly required in PHP 8.4 #16733
  • Fixed Phalcon\Mvc\Model\Query to use the cacheOptions lifetime over the "cache" service lifetime