5.14.1 (2026-06-08)
Tools
- Zephir Parser v2.0.4
- Zephir 0.23.0 (development - bae82f7bd)
Changed
- Consolidated the
Phalcon\Authdual-container handling (newPhalcon\Container\Containervs legacyPhalcon\Di\Di) behind a single internalPhalcon\Auth\Internal\ContainerResolver. #17088 [doc] - Renamed the private
Phalcon\Events\Managerdispatch hot-loop helper torunQueue(). #17006 [doc] - Reworked the
Phalcon\Authaccess 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 carrieshandler(controller / task / Micro component name),module(MVC module, when present), andparams(dispatcher or route parameters). #17088 [doc] Phalcon\Auth\Manager::access()now resolves gates throughPhalcon\Auth\Access\AccessLocatorfrom 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 aPhalcon\Acl\Adapter\AdapterInterface: the ACL component is thehandlercontext key (prefixed withmoduleand a configurable separator when present), the ACL access is the action name, andparamsare passed through to callable ACL rules. Unauthenticated requests resolve to a configurable guest role (defaultguest); authenticated users supply their role viaPhalcon\Acl\RoleAwareInterface. #17088 [doc] - Added
Phalcon\Auth\Micro\AuthMicroListenerto enforce the active Auth access gate onPhalcon\Mvc\Microroute execution (attach to themicroevent 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 ownPhalcon\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
beforeBindandafterBindhook methods toPhalcon\Forms\Form. When defined on a form subclass,beforeBind(array $data, ?object $entity)runs at the start ofbind()(returningfalsecancels the bind) andafterBind(?object $entity)runs after the data has been assigned. Both also fire whenbind()is reached throughisValid(). #14598 [doc] - Added a
syncoption to many-to-many (hasManyToMany) relations and a chainablePhalcon\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 toPhalcon\Logger\Loggertogether with a newTRACElog level (value9, labeltrace). #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
JOINclauses in PHQLUPDATEstatements (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 fromzend_inline_hash_func, eliminating hash collisions that caused different PHQL queries to return identical cached ASTs #14791 - Fixed
Phalcon\Annotations\Readerfailing 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-timehash_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 ofhash_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 simplecolumn directionpair, corrupting expressions such asCASE WHEN inv_status_flag = 1 THEN 0 ELSE 1 END ASC. The builder now only treats a trailingASC/DESCas 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 theNOTkeyword. Columns, tables, and aliases such asnotice_idwere truncated toice_id(the leadingnotwas dropped), causing the database to report the column as unknown - most visibly inPhalcon\Mvc\Model\Query\Builderjoin conditions built viacreateBuilder(). The scanner's re2c backtracking marker shared the token-start pointer, so theNOT BETWEENrule advanced it pastnot; 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) inPhalcon\Container\Container::callableGet()andcallableNew(). Both closures captured the typedstring nameparameter directly viause (name). #17078
Removed
- Removed the unfinished
{% raw %}/{% endraw %}Volt tag. It never produced output (compilation threwUnknownVoltStatement) and its body was parsed rather than emitted literally. Use{% verbatim %}instead. #17085