5.17.0 (2026-07-17)
Tools
- Zephir 1.1.0 (80d17e6a0)
Changed
- Changed
Phalcon\Acl\Adapter\Memoryso a freshly constructed adapter returns an empty array instead ofnullfromgetRoles(),getComponents()andgetInheritedRoles(). #17220 [doc] - Changed the
Phalcon\Autharray adapters (Memory,Stream) to compare non-password credential fields against configured row values as strings, so string input from a request (e.g.'1') matches a typed row value (e.g.1ortrue) instead of failing a strict type comparison. #17220 [doc] - Changed the
Phalcon\Authlayer to throw granularPhalcon\Auth\Exceptions\*subclasses instead of the basePhalcon\Auth\Exception:AccessNotRegistered,ActiveAccessRequired,DefaultGuardNotRegisteredandGuardNotDefined(Phalcon\Auth\Manager),UnknownAdapterandUnknownGuard(Phalcon\Auth\ManagerFactory),OptionRequiresArrayandOptionRequiresString(fromOptions()option parsing),SessionNamesMustDiffer(Phalcon\Auth\Guard\Config\SessionGuardConfig), andMissingHandlerContext(Phalcon\Auth\Access\Acl). Each extendsPhalcon\Auth\Exception, so existingcatchblocks keep working. #17220 [doc] - Deprecated
Phalcon\Acl\Adapter\Memory::getActiveKey()(usegetActiveRole(),getActiveComponent()andgetActiveAccess()) and the legacy ACL interfacesPhalcon\Acl\Adapter\AdapterInterface,Phalcon\Acl\RoleInterface,Phalcon\Acl\ComponentInterface,Phalcon\Acl\RoleAwareInterfaceandPhalcon\Acl\ComponentAwareInterfacein favour of theirPhalcon\Contracts\Acl\...equivalents. #17220 [doc] - Note:
Phalcon\Auth\ManagerFactoryvalidates the required guard configuration up front and throws aPhalcon\Auth\Exceptionsubclass on a missing key, where earlier versions emitted a PHP notice followed by aTypeError; handlers that caughtTypeErrorthere should catchPhalcon\Auth\Exceptioninstead. #17220 [doc] - Refactored the read path of model hydration by extracting
Phalcon\Mvc\Model::cloneResultMapHydrate()into the dedicatedPhalcon\Mvc\Model\Hydration\CloneResultMapHydrateclass and the case-insensitive column-map lookup intoPhalcon\Mvc\Model\Hydration\CaseInsensitiveColumnMap. #17228 [doc] Phalcon\Support\Helper\Str\Concat::__invoke()was reimplemented as a typed variadic(string $delimiter, string ...$many)- previously it declared no parameters and read them throughfunc_get_args()- with unchanged behavior. #17273 [doc]
Added
- Added
@Connect,@Head,@Purgeand@Tracemethod annotations toPhalcon\Mvc\Router\Annotations, matching the existing@Get,@Post,@Put,@Patch,@Deleteand@Optionsshortcuts. [doc] - Added
Phalcon\Acl\Adapter\Storage, a storage-backed ACL adapter that persists the entire policy as a versioned, serializer-agnostic snapshot to anyPhalcon\Storagebackend (Redis, Apcu, Stream, Memcached) and reloads it on construction, defined by the newPhalcon\Contracts\Acl\Adapter\Persistablecontract (save()/load()). Callable (closure) rules are not serializable, so they are persisted asDENY(fail closed) and must be re-registered afterload();load()returnsfalsefor a snapshot without a version marker and throwsPhalcon\Acl\Exceptions\InvalidSnapshoton an incompatible version or a malformed structure. Single-writer contract:save()writes the whole snapshot (last-write-wins; use external locking for concurrent writers). #17220 [doc] - Added
Phalcon\Acl\Exceptions\InvalidSnapshot, thrown byPhalcon\Acl\Adapter\Storage::load()on an incompatible or malformed policy snapshot. #17220 [doc] - Added
Phalcon\Encryption\Security\JWT\Validator::validateSubject(), which compares the token'ssubclaim against the expected subject and reportsValidation: incorrect subjecton a mismatch. Anullsubject expresses no expectation and is skipped. #17361 [doc] - Added
Phalcon\Filter\Validation::setDefaultMessages()andPhalcon\Filter\Validation::getDefaultMessage()for registering global default validator failure messages keyed by validator class name (e.g.Validation::setDefaultMessages([PresenceOf::class => 'Default message :field is required'])). A registered default overrides a validator's built-in class default message, while a message set on the validator instance (the constructormessage/templateoption orsetTemplate()) still takes precedence; it applies to validators whose message is produced throughgetTemplate()/messageFactory(). #17257 [doc] - Added
Phalcon\Filter\Validation\Validator\Files, a validator for an array of uploaded files (<input name="files[]" type="file" multiple>). It accepts the same options asPhalcon\Filter\Validation\Validator\File, normalizes a single-file or transposed multi-file$_FILESnode into individual files, and validates each by delegating toFile, failing on the first file that violates a rule. #17259 [doc] - Added
Phalcon\Mvc\Model\Query::setResultsetRowClass()andPhalcon\Mvc\Model\Query::getResultsetRowClass()to control the class used to hydrate rows that are not mapped to a model (custom-columnSELECTs and joins). When set, those result rows are built as the given subclass ofPhalcon\Mvc\Model\Rowinstead ofRowitself - on both the simple (custom columns) and complex (join) resultset paths - so they can carry reusable helper methods. The class is validated when set (it must exist and be a subclass ofPhalcon\Mvc\Model\Row), throwing the newPhalcon\Mvc\Model\Query\Exceptions\ResultsetRowClassNotFoundorPhalcon\Mvc\Model\Query\Exceptions\InvalidResultsetRowClass. #17337 [doc] - Added
Phalcon\Mvc\Model\Query\Builder::setResultsetRowClass()andPhalcon\Mvc\Model\Query\Builder::getResultsetRowClass()so the custom resultset row class can be set on a query builder, which forwards it to thePhalcon\Mvc\Model\Queryit produces ingetQuery(). BecausePhalcon\Paginator\Adapter\QueryBuilderbuilds its query through the builder,paginate()now returns the givenPhalcon\Mvc\Model\Rowsubclass for its non-model result rows. #17337 [doc] - Added a set of reusable traits under the
Phalcon\Traitsnamespace that framework classes compose instead of carrying duplicated logic. The public traits:Phalcon\Traits\Support\Helper\Arr\GetTrait- read an array element by key with a default value and an optional cast.Phalcon\Traits\Support\Helper\Arr\FilterTrait- filter a collection witharray_filter()and an optional callable.Phalcon\Traits\Support\Helper\Str\DirFromFileTrait- build a nested directory path from a file name, with an optional path-safety flag.Phalcon\Traits\Support\Helper\Str\DirSeparatorTrait- ensure a directory string ends with exactly oneDIRECTORY_SEPARATOR.Phalcon\Traits\Support\Helper\Str\EndsWithTrait/StartsWithTrait- case-insensitive, multibyte-aware suffix/prefix checks.Phalcon\Traits\Support\Helper\Str\InterpolateTrait- PSR-3%placeholder%message interpolation.Phalcon\Traits\Support\Helper\Str\CamelizeTrait/UncamelizeTrait- convert betweenCamelCaseand delimited strings.Phalcon\Traits\Support\Helper\Str\LowerTrait/UpperTrait- multibyte-safe lower/upper casing.Phalcon\Traits\Support\Helper\Json\EncodeTrait/DecodeTrait-json_encode()/json_decode()wrappers that throw the native\JsonException.Phalcon\Traits\Php\ApcuTrait- wrappers forapcu_*methods.Phalcon\Traits\Php\Base64Trait- wrappers forbase64_*methods.Phalcon\Traits\Php\FileTrait- wrappers forfile_*methods.Phalcon\Traits\Php\HashTrait- wrappers forhash_*methods.Phalcon\Traits\Php\HeaderTrait- wrapper forheaders_sent.Phalcon\Traits\Php\IgbinaryTrait- wrappers forigbinary_*methods.Phalcon\Traits\Php\InfoTrait- wrappers forextension_loadedand `function_exists.Phalcon\Traits\Php\IniTrait- wrappers forini_*methods.Phalcon\Traits\Php\MbCaseTrait- wrappers formb_case_*methods.Phalcon\Traits\Php\MsgpackTrait- wrappers formsgpack_*methods.Phalcon\Traits\Php\OpensslTrait- wrappers foropenssl_*methods.Phalcon\Traits\Php\SerializeTrait- wrappers forserializeandunserialize.Phalcon\Traits\Php\UrlTrait- wrappers forparse_url,rawurldecodeandrawurlencode.Phalcon\Traits\Php\YamlTrait- - wrappers foryaml_*methods. These calls can be substituted in tests. #17273 [doc]
- Added an opt-in "sticky" read/write connection mode to
Phalcon\Mvc\Model\Manager. AfterPhalcon\Mvc\Model\Manager::setSticky(true), once a model has written to its write connection during the current request cycle, any further reads for that write service are served from the write connection, so data written earlier in the request can be read back immediately. Writes are recorded via the newPhalcon\Mvc\Model\Manager::registerWrite()(called internally on a successful insert/update/delete), andPhalcon\Mvc\Model\Manager::resetConnectionState()clears the per-request tracking for long-running runtimes (e.g. Swoole, RoadRunner) that reuse the manager across requests. Sticky is off by default, preserving the existing read/write split; the transaction connection still takes precedence. The three methods are added toPhalcon\Mvc\Model\ManagerInterface. #17256 [doc] - Added an opt-in
allowWildcardsoption toPhalcon\Filter\Validation\Validator\File\MimeType(and passed through fromPhalcon\Filter\Validation\Validator\Filevia the same key) that, whentrue, matches each configured type against the detected MIME type as an anchored regular expression (e.g.image/.*,video/.*) in addition to an exact string match; it defaults tofalse, preserving the existing exact-match behavior. #17254 [doc] - Added closure binding to
Phalcon\Filter\Validation\Validator\Callback: aClosurepassed as thecallbackoption is now bound to the validator instance ($this) before it is invoked, so the callback can call the validator's own public methods - for example$this->setTemplate('...')to set a per-failure message from inside the callback. Non-closure callables (function-name strings,[object, method]arrays) are left unbound, and the callback's return contract is unchanged (bool, or aPhalcon\Filter\Validation\ValidatorInterfaceto delegate). #17255 [doc] - Added support for absolute and current-template-relative paths in the Volt
{% extends %}and{% include %}tags (Phalcon\Mvc\View\Engine\Volt\Compiler): an absolute path (Unix/..., or a Windows drive/UNC path) is now used as-is, and a path beginning with./or../is resolved against the directory of the template currently being compiled instead of the views directory; every other path keeps the existing views-directory-relative resolution. #17269 [doc] - Added the
Phalcon\Contracts\Aclcontracts -Phalcon\Contracts\Acl\Adapter\Adapter,Phalcon\Contracts\Acl\Adapter\Persistable,Phalcon\Contracts\Acl\Role,Phalcon\Contracts\Acl\Component,Phalcon\Contracts\Acl\RoleAwareandPhalcon\Contracts\Acl\ComponentAware- as the canonical homes for the ACL interfaces; the legacyPhalcon\Acl\...\*Interfacetypes remain as deprecated bridges that extend them. #17220 [doc] - Added the
Phalcon\Contracts\Queue\Inspectablecontract (getStats(Queue $queue): array), implemented byPhalcon\Queue\Adapter\Beanstalk\BeanstalkContext, exposing the Beanstalkdstats-tubefields (current-jobs-ready,current-jobs-reserved,current-jobs-delayed,current-jobs-buried,current-jobs-urgent,total-jobs, thecmd-*counters, ...) for queue backlog/depth monitoring. The returned array is adapter-native; thecurrent-jobs-*keys are always present (zero for an unknown tube) and the read runs on its own short-lived connection. Backed by a newPhalcon\Queue\Adapter\Beanstalk\BeanstalkConnection::statsTube()wire command. #17209 [doc] - Added the granular
Phalcon\Auth\Exceptions\*exceptionsAccessNotRegistered,ActiveAccessRequired,DefaultGuardNotRegistered,GuardNotDefined,MissingHandlerContext,OptionRequiresArray,OptionRequiresString,SessionNamesMustDiffer,UnknownAdapterandUnknownGuard. #17220 [doc]
Fixed
- Fixed
Phalcon\Authlogin timing leaking account existence: the credential adapters now perform a throwaway password hash on the user-not-found path, so an attempt for an unknown identifier costs the same as one for a real account with a wrong password (mitigates login-timing user enumeration). #17220 [doc] - Fixed
Phalcon\Db\Dialect::getSqlExpression()throwingThe argument is not initialized or iterable()while resolving acaseexpression when the expression array is held as a PHP reference, by fetching thewhen-clauseslist througharray_values()before iterating it. #17225 [doc] - Fixed
Phalcon\Encryption\Crypt::encrypt()to properly capture exceptions thrown byopenssl_random_pseudo_bytes. #17326 [doc] - Fixed
Phalcon\Encryption\Security\JWT\Token\Token::validate()throwingPhalcon\Encryption\Security\JWT\Exceptions\InvalidAudienceTypewhen handed a freshly constructedPhalcon\Encryption\Security\JWT\Validator, which made a defaultValidatorimpossible to pass to it. #17361 [doc] - Fixed
Phalcon\Encryption\Security\JWT\Validator::validateIssuedAt()andPhalcon\Encryption\Security\JWT\Validator::validateNotBefore()rejecting a token whoseiat/nbfclaim falls on exactly the validated timestamp. #17361 [doc] - Fixed
Phalcon\Forms\Element\AbstractElement::render()to cast a non-nullelement value tostringbefore passing it to the input helper, so a numeric default set viasetDefault()(e.g.setDefault(10)orsetDefault(10.5)on aPhalcon\Forms\Element\Numeric) renders asvalue="10"instead of raising aTypeErrorfor passing anint/floatto the helper'sstring$valueparameter. #17232 [doc] - Fixed
Phalcon\Http\Response::getStatusCode()andPhalcon\Http\Response::getReasonPhrase()raising aTypeError(substr(): Argument #1 ($string) must be of type string, bool given) when noStatusheader had been set (e.g. a response built with onlysetContent()), becausePhalcon\Http\Response\Headers::get('Status')returnsfalsefor an absent header; the header value is now cast to string beforesubstr(), so both methods returnnullas documented. #17248 [doc] - Fixed
Phalcon\Image\Adapter\AbstractAdapter::resize()truncating the scaled master-mode (Enum::WIDTH,Enum::HEIGHT,Enum::PRECISE) dimension to anintbefore rounding, so a value whose fractional part was>= 0.5came out one pixel short (e.g. a1820x694source resized to height80produced width209instead of210); the scaled width/height are now rounded before the integer cast. #17225 [doc] - Fixed
Phalcon\Mvc\Model::cloneResult()andPhalcon\Mvc\Model::cloneResultMap()calling a model setter (or throwingPhalcon\Mvc\Model\Exceptions\PropertyNotAccessiblewhen no setter exists) while hydrating a declaredprivateproperty, because the property write fromModelscope fell back to__set()and itspossibleSetter()routing regardless of theorm.call_setters_on_hydrationsetting. #16454 [doc] - Fixed
Phalcon\Mvc\Model::create()andPhalcon\Mvc\Model::update()passingnullto thefieldargument ofPhalcon\Messages\Message(typedstringsince v5.14), which raised aPassing null to parameter #2 ($field) of type string is deprecatedwarning when callingcreate()on an existing record orupdate()on a non-existent one; they now pass an empty string. #17224 [doc] - Fixed
Phalcon\Mvc\Model\Query::executeUpdate()raising a PDOInvalid parameter number: mixed named and positional parameterserror for a PHQLUPDATEwhoseSETclause is an expression carrying a bound placeholder (e.g.SET col = col + :inc:): the named placeholder is now resolved from the bind parameters and inlined into the expression before thePhalcon\Db\RawValueis built, so it no longer collides with the positional?marker of the primary-keyWHEREclause, and the placeholder is removed from the bind parameters forwarded to the pre-updateSELECT. #16976 [doc] - Fixed a segmentation fault when rendering a Volt template that
extendsa parent chain but defines no blocks of its own, where a block declared higher in the chain calls{{ partial() }}:Phalcon\Mvc\View\Engine\Volt\Compiler::compileSource()passed anullblocksvalue toarray_key_exists(), which read it as an array from an uninitialized pointer;blocksis now coerced to an empty array so any inheritance depth is handled. #17294 [doc] - Fixed the PHP 8.4/8.5 deprecation notices raised by the extension: removed the
imagedestroy()calls inPhalcon\Image\Adapter\Gd(a no-op since PHP 8.0), thefinfo_close()calls inPhalcon\Http\Request\FileandPhalcon\Filter\Validation\Validator\File\MimeTypeand theReflectionProperty::setAccessible()call inPhalcon\Support\Debug\Dump(no-ops since PHP 8.1), clamped the random pad byte inPhalcon\Encryption\Crypt\Padding\Iso10126tochr(rand() % 256)to avoid the out-of-rangechr()deprecation on PHP 8.5, and guardedPhalcon\Messages\Messages::offsetSet()against an implicitnullarray offset. #17253 [doc]