drupal/drupal-driver
A collection of reusable Drupal drivers
v3.3.1
17M downloads/mo
#1591 most downloaded on Packagist
jhedstrom/DrupalDriver
What this package is like to depend on
Last release 26 days ago
28 Jul 2026
Ships unpredictably
gaps range from 9 days to 1.8 years
Rarely documented
notes for 8 of 51 stable releases
Nothing withdrawn
no release was ever pulled
12 years old
63 releases · first in 2014
14 releases in the last 12 months
see the full history below
Release timeline
63 releases · Oct 2014 to Jul 2026Releases
latest 60 of 63-
v3.3.128 Jul 2026Release notes
Open source →What's new since v3.3.0
- Removed the dead drush stub from 'TestDrushDriver' and documented the legacy Drush removal. @AlexSkrypnyk (#390)
- remove legacyDrush check @ptmkenny (#387)
- Fixed 'DrupalExtension smoke' checkout failing for pull requests from forks. @AlexSkrypnyk (#389)
- Removed the redundant default value from the 'mainProperty' field handler property. @AlexSkrypnyk (#388)
Full Changelog: v3.3.0...v3.3.1
-
v3.3.007 Jul 2026Release notes
Open source →What's new since v3.2.0
All changes
- Added a dedicated field value-shape classifier gating the default handler, and deprecated pass-through handlers. @AlexSkrypnyk (#386)
When a field type has no dedicated handler,Corenow consults a value-shape classifier before falling back toDefaultHandler: entity-reference and complex/nested shapes cannot be authored as a plain scalar, so they are refused up front with an error naming the field, type, entity type, and bundle instead of silently persisting corrupt data, while plain scalars (strings, integers, datetime strings, booleans, list keys) relay through the default verbatim - so multi-column scalar fields that previously tripped the old single-column guard now work without a hand-rolled handler. The four now-redundant pass-through handlers -TextHandler,TextLongHandler,TextWithSummaryHandler, andColorFieldTypeHandler- are marked@deprecatedfor removal indrupal-driver:4.0.0but remain registered and functional, so consumers who extend or reference them have a full release to migrate before anything changes. - [#384] Added 'DateRecurHandler' for the 'date_recur' contrib field type. @AlexSkrypnyk (#385)
Adds a built-in handler for the contribdate_recurfield type, whose five columns (value,end_value,rrule,timezone,infinite) previously had no dedicated handler - so the driver loud-failed on the field and every consuming test suite had to hand-roll and register its own.DateRecurHandlerauto-registers throughCore's field-type discovery and relays the multi-column records unchanged (the field type derivesinfinitefrom therrulein its ownpreSave()), removing that per-project boilerplate for a common contrib module.
Full Changelog: v3.2.0...v3.3.0
Release notes
Open source →-
Removed the dead drush stub from 'TestDrushDriver' and documented the legacy Drush removal. @AlexSkrypnyk ( #390 )
-
remove legacyDrush check @ptmkenny ( #387 )
-
Fixed 'DrupalExtension smoke' checkout failing for pull requests from forks. @AlexSkrypnyk ( #389 )
-
Removed the redundant default value from the 'mainProperty' field handler property. @AlexSkrypnyk ( #388 )
Full Changelog : v3.3.0...v3.3.1
@AlexSkrypnyk and @ptmkenny
Contributors
AlexSkrypnyk and ptmkenny
Assets 2 Loading
Uh oh!
There was an error while loading. Please reload this page .
All reactions
- Added a dedicated field value-shape classifier gating the default handler, and deprecated pass-through handlers. @AlexSkrypnyk (#386)
-
v3.2.022 Jun 2026Release notes
Open source →What's new since v3.1.0
- Fixed formatting in credits in README.md @AlexSkrypnyk (#382)
- Fixed 'DrupalExtension smoke' CI job to resolve against both dev and stable DrupalDriver constraints. @AlexSkrypnyk (#381)
- [#379] Added optional 'attachments' parameter to 'MailCapabilityInterface::mailSend()'. @AlexSkrypnyk (#380)
- [#376] Added 'drushResult()' non-throwing executor returning a 'DrushResult'. @AlexSkrypnyk (#377)
- Fixed CI failures from Composer advisory blocking, 'phpunit-bridge' deprecations, and a stale path-repo version. @AlexSkrypnyk (#378)
Full Changelog: v3.1.0...v3.2.0
Release notes
Open source →All changes
-
Added a dedicated field value-shape classifier gating the default handler, and deprecated pass-through handlers. @AlexSkrypnyk ( #386 ) When a field type has no dedicated handler, Core now consults a value-shape classifier before falling back to DefaultHandler : entity-reference and complex/nested shapes cannot be authored as a plain scalar, so they are refused up front with an error naming the field, type, entity type, and bundle instead of silently persisting corrupt data, while plain scalars (strings, integers, datetime strings, booleans, list keys) relay through the default verbatim - so multi-column scalar fields that previously tripped the old single-column guard now work without a hand-rolled handler. The four now-redundant pass-through handlers - TextHandler , TextLongHandler , TextWithSummaryHandler , and ColorFieldTypeHandler - are marked @deprecated for removal in drupal-driver:4.0.0 but remain registered and functional, so consumers who extend or reference them have a full release to migrate before anything changes.
-
[ #384 ] Added 'DateRecurHandler' for the 'date_recur' contrib field type. @AlexSkrypnyk ( #385 ) Adds a built-in handler for the contrib date_recur field type, whose five columns ( value , end_value , rrule , timezone , infinite ) previously had no dedicated handler - so the driver loud-failed on the field and every consuming test suite had to hand-roll and register its own. DateRecurHandler auto-registers through Core 's field-type discovery and relays the multi-column records unchanged (the field type derives infinite from the rrule in its own preSave() ), removing that per-project boilerplate for a common contrib module.
Full Changelog : v3.2.0...v3.3.0
@AlexSkrypnyk
Contributors
AlexSkrypnyk
Assets 2 Loading
Uh oh!
There was an error while loading. Please reload this page .
All reactions
-
v3.1.018 Jun 2026Release notes
Open source →What's new since v3.0.0
- [#374] Added 'ColorFieldTypeHandler' for the 'color_field' contrib module. @AlexSkrypnyk (#375)
Full Changelog: v3.0.0...v3.1.0
Release notes
Open source →-
Fixed formatting in credits in README.md @AlexSkrypnyk ( #382 )
-
Fixed 'DrupalExtension smoke' CI job to resolve against both dev and stable DrupalDriver constraints. @AlexSkrypnyk ( #381 )
-
[ #379 ] Added optional 'attachments' parameter to 'MailCapabilityInterface::mailSend()'. @AlexSkrypnyk ( #380 )
-
[ #376 ] Added 'drushResult()' non-throwing executor returning a 'DrushResult'. @AlexSkrypnyk ( #377 )
-
Fixed CI failures from Composer advisory blocking, 'phpunit-bridge' deprecations, and a stale path-repo version. @AlexSkrypnyk ( #378 )
Full Changelog : v3.1.0...v3.2.0
@AlexSkrypnyk
Contributors
AlexSkrypnyk
Assets 2 Loading
Uh oh!
There was an error while loading. Please reload this page .
All reactions
-
v3.0.029 May 2026Release notes
Open source →What's new since v3.0.0-rc2
- Fixed documentation inaccuracies surfaced during v3.0.0 release review. @AlexSkrypnyk (#373)
Full Changelog: v3.0.0-rc2...v3.0.0
Upgrading from v2
See UPGRADING.md for the full v2 to v3 migration guide.
Implemented across pre-stable v3 releases
This release rolls up everything shipped in the v3.0.0 prerelease cycle (
v3.0.0-alpha1→v3.0.0-rc1→v3.0.0-rc2). Each section below preserves the original release notes for that prerelease.
v3.0.0-alpha1 (May 3, 2026)
v3 reworks the driver contract into a set of composable capability interfaces, narrows entity stubs to a typed value object, drops Drupal 6/7, and tightens the supported PHP/Drupal range. Most changes are mechanical; a small amount of consumer-side code (typically in DrupalExtension integrations) may need updating.
Highlights
- [v3] Restructured driver and core contracts into composable capability interfaces. (#342)
The driver surface is now a set of twelve small, named capability interfaces (AuthenticationCapabilityInterface,CacheCapabilityInterface,ContentCapabilityInterface, ...) composed into per-driver contracts. Consumers caninstanceof-check exactly the capability they need (if ($driver instanceof MailCapabilityInterface) { ... }) instead of catchingUnsupportedDriverActionExceptionfrom a giant base class, and downstream projects can build new drivers by implementing only the capabilities they actually support. - [#155] Added field handler registry with directory-based discovery and CoreInterface injection. (#353)
Consumer projects now have two real extension seams that don't require squatting on this project's namespace:$core->registerFieldHandler('my_type', MyHandler::class)overrides one handler from anywhere, and$driver->setCore($custom_core)swaps the entireCorefor a consumer-owned subclass. Internally the project dogfoods the same mechanism via a glob-based scan of the siblingField/directory, so future Drupal versions add handlers by file-drop instead of editing a hardcoded list. - [#111] Added 'BlockCapabilityInterface' with block placement and content-block methods. (#354)
Closes a 2016 gap (#111):blockPlace(),blockDelete(),blockContentCreate(), andblockContentDelete()are now first-class driver operations on D10/D11, supporting both theblockconfig entity (placement in a region) and theblock_contentcontent entity (custom block bodies). Block-type creation is intentionally out of scope - like node types and vocabularies, that is schema-level setup that belongs in the test site's configuration, not in a scenario step. - Added 'FieldClassifier' with nine F-row predicates and rewrote field-expansion pipeline. (#359)
The newFieldClassifierprovides nine origin/storage predicates (F1-F9) that classify every Drupal field, andCore::expandEntityFields()now routes through them so only writable, handler-eligible fields enter the pipeline. Previously, computed and non-writable base fields slipped through the configured-field filter and produced silent garbage; nowDefaultHandlerthrows loudly for any multi-column type without a registered handler so gaps surface immediately. A newEntityReferenceRevisionsHandlercovers paragraph and other revision-tracked references. - [#362] Replaced 'stdClass' entity stubs with typed 'EntityStub' class. (#363)
The untyped\stdClassshape that flowed between callers and every driver create/delete method was replaced with a typedEntityStubcarrying entity type, bundle, a mutable values bag, and a saved-entity slot. Callers can now use typed accessors ($stub->getId(),$stub->isSaved(),$stub->getSavedEntity()) instead of guessing which property holds the saved id, and the field-handler boundary is also typed so the impedance mismatch between the driver and the expand pipeline is gone. - [#124] Added 'BooleanHandler' accepting field labels and canonical yes/no/true/false/on/off forms. (#350)
Scenarios stubbingbooleanfields no longer have to use raw1/0. The handler matches the field's ownon_label/off_labelfirst (case-insensitive, so a German site withJa/Neinworks verbatim), then falls back to the canonical English allow-list viafilter_var(FILTER_VALIDATE_BOOLEAN). Anything outside both sets throws aRuntimeExceptionlisting the accepted values, replacing silent coercion toFALSE. - [#118] Improved error messages for unknown entity types, bundles, vocabularies, and parent terms. (#351)
Stubs referencing a missing entity type, bundle, vocabulary, or parent term now fail with anInvalidArgumentExceptionthat quotes the offending value (Unknown entity type "fake_type".,Cannot create term because parent term 'Missing' does not exist in vocabulary 'tags'.) instead of leaking low-level plugin-system errors or undefined-property warnings. Scenario authors get an actionable message at the failure boundary instead of a stack trace into Drupal internals.
Breaking changes
- [#362] Replaced 'stdClass' entity stubs with typed 'EntityStub' class. (#363)
Every capability interface that previously accepted or returned\stdClass(AuthenticationCapabilityInterface::login(), everyBlock*,Content*,Language*, andUser*create/delete method) now declaresEntityStubInterface. Migration: replace(object) ['type' => 'page', ...]constructs withnew \Drupal\Driver\Entity\EntityStub('node', 'page', [...]), and read the saved id via$stub->getId()/ saved entity via$stub->getSavedEntity()instead of guessing property names. There is no\stdClassshim - this is a clean cut. - Added 'FieldClassifier' with nine F-row predicates and rewrote field-expansion pipeline. (#359)
FieldCapabilityInterfaceis removed along withfieldExists()andfieldIsBase()onCoreandDrupalDriverInterface; both predicates now live onFieldClassifierInterface.Core::expandEntityFields()no longer accepts a$base_fieldsargument andCore::getEntityFieldTypes()now takes?string $bundleinstead ofarray $base_fields.Core::expandEntityBaseFields()andCore::detectBaseFieldsOnEntity()were removed.DefaultHandler::expand()now throws\RuntimeExceptionfor any field whose storage is not a singlevaluecolumn instead of silently emitting garbage. Migration is documented inUPGRADING.md. - Tightened 'DrupalDriverInterface' contract and 'DrupalDriver' visibility. (#358)
getCore(),setCore(), andgetDrupalVersion()are now part of theDrupalDriverInterfacecontract, so any consumer that implements the interface directly must add these three methods.DrupalDriver::$coreandDrupalDriver::$versionwere narrowed frompublictoprotected; replace direct property access ($driver->core,$driver->version) with the new public accessors. - [#155] Added field handler registry with directory-based discovery and CoreInterface injection. (#353)
DrupalDriver::setCore()signature changed fromsetCore(array $available_cores)(keyed by Drupal version) tosetCore(CoreInterface $core). Migration: callers that previously passed a version-keyed array of core classes must now construct or select a singleCoreInterfaceinstance themselves and inject it. The convention-basedsetCoreFromVersion()lookup that walksDrupal\Driver\Core{N}\Coreclasses is unchanged. - [#154] Removed unused 'TaxonomyTermReferenceHandler' and its tests. (#352)
The handler matched the legacytaxonomy_term_referencefield type that Drupal core removed in 8.0.0-beta10 (2015) and was unreachable on every supported Drupal version. Modern taxonomy-linking fields areentity_referencewithtarget_type = taxonomy_termand route throughEntityReferenceHandler; consumers that subclassedTaxonomyTermReferenceHandlershould subclassEntityReferenceHandlerinstead. - [#270] Fixed 'entityCreate()' base field expansion and dynamic id key resolution. (#346)
entityCreate()now populates the entity-type-specific id property (e.g.$stub->uidfor users,$stub->nidfor nodes) instead of always assigning to$stub->id, andentityDelete()reads from the same key. Consumers reading$stub->idafter a genericentityCreate('user', $stub)must switch to$stub->$id_key(resolved via$entity_type_definition->getKey('id')) or read from the returned entity. Base fields set on the stub (e.g.commerce_product.variations) are now auto-routed through the handler pipeline rather than reaching storage in raw form. - [v3] Removed 'drush-ops/behat-drush-endpoint' and Drush-backed entity and field methods. (#343)
DrushDriverInterfaceno longer extendsContentCapabilityInterfaceorFieldCapabilityInterface.entityCreate,entityDelete,nodeCreate,nodeDelete,termCreate,termDelete,fieldExists, andfieldIsBaseare gone fromDrushDriver, and thedrush-ops/behat-drush-endpointcompanion module is no longer required. Consumers that need entity CRUD or field introspection should switch toDrupalDriver; seeUPGRADING.mdfor the per-method migration table. - [v3] Restructured driver and core contracts into composable capability interfaces. (#342)
The monolithicDriverInterface/BaseDrivermodel was replaced with twelve fine-grainedCapability\*CapabilityInterfaceinterfaces and per-driver composite interfaces (DrupalDriverInterface,DrushDriverInterface,BlackboxDriverInterface). Many methods were renamed to lead with their capability prefix (createNode→nodeCreate,createTerm→termCreate,createEntity→entityCreate,isField→fieldExists,isBaseField→fieldIsBase,clearCache→cacheClear,clearStaticCaches→cacheClearStatic,runCron→cronRun,fetchWatchdog→watchdogFetch, all*Mailmethods →mail*).BaseDriver,AbstractCore,AuthenticationDriverInterface,CoreAuthenticationInterface, andDrupalCoreInterfacewere removed.UPGRADING.mddocuments every rename and theinstanceofcapability-check pattern. - Restructured cores into 'Core/' with version-override lookup chain. (#338)
src/Drupal/Driver/Cores/Drupal8.phpwas renamed tosrc/Drupal/Driver/Core/Core.php(classDrupal8→Core) andsrc/Drupal/Driver/Fields/Drupal8/*moved tosrc/Drupal/Driver/Core/Field/*. Any code referencingDrupal\Driver\Cores\Drupal8,Drupal\Driver\Fields\Drupal8\*, or the oldCores/andFields/namespaces must update toDrupal\Driver\Core\CoreandDrupal\Driver\Core\Field\*. A future Drupal version that diverges can ship aDrupal\Driver\Core{N}\Coresubclass and thesetCoreFromVersion()chain will pick it up automatically. - Added 'declare(strict_types=1)' and PHP 8.2 type declarations via Rector. (#337)
Every source and test file now declares strict types and parameters/returns/properties carry PHP 8.2 type declarations. Callers that previously relied on PHP's loose-mode coercion (passing strings where ints are expected, etc.) will now hitTypeErrorat the boundary; audit any consumer code that calls intoCore,DrupalDriver,DrushDriver, or any handler. - Removed Drupal 6 and 7 support and bumped composer constraints for 3.x. (#336)
Minimum PHP is now 8.2 and supported Drupal versions are 10 and 11 only; D6 and D7 core classes, field handlers, and tests were deleted.DrupalDriver::detectMajorVersion()throws aBootstrapExceptionwhen it sees Drupal < 10. Symfony deps were lifted to^6.4 || ^7. Sites still on PHP 8.1 or Drupal 9 must stay on the 2.x line (now maintained on the2.xbranch).
All changes
- [#362] Replaced 'stdClass' entity stubs with typed 'EntityStub' class. (#363)
Every capability interface that touched\stdClassnow declaresEntityStubInterface, the field-handler boundary accepts the typed stub directly, and the previous(object) [...]constructs are replaced withnew EntityStub($entity_type, $bundle, [...]). Callers can read the saved id and entity through typed accessors instead of guessing property names. - Added 'DrupalExtension' downstream smoke check job to CI.
A new CI job runs thedrupalextensiontest suite against this branch so regressions that only surface in the primary downstream consumer are caught before release. - Restored 'text_long', 'text', 'file', 'image' field-handler semantics and added type-coverage safety net. (#360)
The v3 pipeline rewrite shipped withoutTextHandlerandTextLongHandler, both multi-column types that immediately tripped the new loud-failure policy inDefaultHandler. This adds both handlers, restores the 2.x reuse-by-URI / reuse-by-basename behaviour forFileHandler(andImageHandler, which now extendsFileHandler), and addsFieldTypeCoverageKernelTestthat walks every registered field type and fails if a new core type is missing a handler entry. - Added 'FieldClassifier' with nine F-row predicates and rewrote field-expansion pipeline. (#359)
FieldClassifierexposes nine predicates that classify each field by origin (base / configurable / bundle-storage-backed) and storage profile (standard / computed / custom).Core::expandEntityFields()now routes through the classifier so only F1, F5, and F9 rows enter the handler pipeline, computed and non-writable fields no longer reach storage, andDefaultHandlerthrows for any multi-column type without a registered handler.EntityReferenceRevisionsHandleris added for paragraphs and other revision-tracked references;FieldCapabilityInterfaceand thefieldExists/fieldIsBasepredicates are removed. - Tightened 'DrupalDriverInterface' contract and 'DrupalDriver' visibility. (#358)
getCore(),setCore(), andgetDrupalVersion()are now declared onDrupalDriverInterface, and$core/$versiononDrupalDriverwere narrowed frompublictoprotected. Consumers that hand-rolled aDrupalDriverInterfaceimplementation must add the three accessors, and any callers reading the properties directly must switch to the public methods. - [#111] Added 'BlockCapabilityInterface' with block placement and content-block methods. (#354)
blockPlace(),blockDelete(),blockContentCreate(), andblockContentDelete()are now first-class capability methods onDrupalDriverandCore. Placement covers theblockconfig entity (id auto-generated when omitted, plugin/theme/region wiring), and the content-block methods cover theblock_contentcontent entity via the generic field-expansion pipeline. Block-type creation is intentionally out of scope. - [#155] Added field handler registry with directory-based discovery and CoreInterface injection. (#353)
Consumers register custom handlers with$core->registerFieldHandler($field_type, $class)(last registration wins), and$driver->setCore(CoreInterface $core)lets them inject anyCoreInterfaceimplementation. InternallyCore::__construct()callsregisterDefaultFieldHandlers()which scans the siblingField/directory and registers every concrete*Handlerclass, so future Drupal versions add handlers additively by dropping a file into aCore{N}\Field\directory. - [#154] Removed unused 'TaxonomyTermReferenceHandler' and its tests. (#352)
The handler matched the legacytaxonomy_term_referencefield type that Drupal core removed in 8.0.0-beta10 (2015) and was unreachable on every supported Drupal version. Modern taxonomy references go throughEntityReferenceHandler(already covered by the existing kernel test), so the orphaned class and its two tests are gone. - [#118] Improved error messages for unknown entity types, bundles, vocabularies, and parent terms. (#351)
entityCreate()andentityDelete()now throwInvalidArgumentException('Unknown entity type "X".')when given a bad type instead of leakingPluginNotFoundException.termCreate()validates thevocabulary_machine_nameproperty, the existence of the named vocabulary, and the existence of any named parent term up front, with messages that quote the offending value. - [#124] Added 'BooleanHandler' accepting field labels and canonical yes/no/true/false/on/off forms. (#350)
The new handler readson_label/off_labelfrom the field config and matches them case-insensitively, then falls back tofilter_var(FILTER_VALIDATE_BOOLEAN)for the canonical English forms. Localised label sets (e.g.Ja/Nein) work for free; values that match neither set throw aRuntimeExceptionlisting all accepted forms. - [#202] Allowed explicit id and label on 'roleCreate()'. (#349)
roleCreate(array $permissions, ?string $id = NULL, ?string $label = NULL)lets callers name roles explicitly while preserving the existing random-id behaviour when both arguments are omitted. Closes a 2018 issue: scenarios that assert against a role by name no longer have to scrape the generated id out of the return value. When only$idis given, it doubles as the human-readable label. - Decoupled dev dependencies from 'drupal/core-dev' metapackage. (#348)
Thedrupal/core-devrequirement was replaced with the three transitive deps the test suite actually needs (vfsstream,prophecy-phpunit,phpstan/extension-installer), and a project-ownedtests/bootstrap.phpreplaces the upstream Drupal bootstrap so the Mink class-alias line no longer fatals when Behat/Mink is absent. The local install footprint drops by roughly 50 transitive packages. - Fixed 'EntityReferenceHandler::expand()' to preserve extra item properties on associative-array deltas. (#347)
Associative-array deltas (the shape file fields, image fields, andentity_reference_revisionsuse -['target_id' => 'alice', 'display' => 1]) previously crashed withCalling Drupal\Core\Database\Query\Condition::condition() without an array compatible operator is not supported. The handler now extracts the field's main property as the lookup label, resolves it to an id, writes the id back into the array, and round-trips the remaining columns intact. - [#270] Fixed 'entityCreate()' base field expansion and dynamic id key resolution. (#346)
expandEntityFields()now auto-detects base fields set as properties on the stub (excluding the id and bundle keys) and routes them through the handler pipeline, so base entity-reference fields likecommerce_product.variationsresolve their labels instead of reaching storage raw.entityCreate()andentityDelete()now resolve the id key per entity type (uid,nid,tid, etc.) instead of hardcodingid. - Refactored source files for readability. (#345)
Vertical-rhythm pass over twelve source files insrc/Drupal/Driver/: guard clauses replace nested conditionals, blank lines bracketif/foreach/forblocks, generic$returnvariables are renamed to semantic names, and step-by-step comments that just restated the code are removed. One incidental correctness fix inDrushDriver::drush()replaces an!== NULLcheck on a typed non-nullable property withisset()so root-mode no longer trips on uninitialised access. - [v3] Reordered driver methods into canonical capability-based layout and widened property visibility to 'protected'. (#344)
Public methods onDrupalDriverandDrushDriverare now grouped alphabetically by capability (Authentication, Cache, Config, Content, ...) so navigating between the two drivers is consistent.$bootstrapped,$drupalRoot,$uri,$random, and$argumentswere widened fromprivatetoprotectedso subclasses can access them. No logic, signatures, or behaviour changed. - [v3] Removed 'drush-ops/behat-drush-endpoint' and Drush-backed entity and field methods. (#343)
DrushDriverInterfaceno longer extendsContentCapabilityInterfaceorFieldCapabilityInterface, and the eight methods that required the companion endpoint module (entityCreate,entityDelete,nodeCreate,nodeDelete,termCreate,termDelete,fieldExists,fieldIsBase) are removed fromDrushDriver. Consumers that need those operations should useDrupalDriver; seeUPGRADING.md. - [v3] Restructured driver and core contracts into composable capability interfaces. (#342)
Twelve fine-grainedCapability\*CapabilityInterfaceinterfaces replace the monolithicDriverInterface/BaseDrivermodel; each driver opts into the capabilities it actually services. Many methods were renamed to lead with their capability (createNode→nodeCreate,clearCache→cacheClear,runCron→cronRun,isField→fieldExists, the*Mailfamily flipped tomail*);BaseDriver,AbstractCore,AuthenticationDriverInterface,CoreAuthenticationInterface, andDrupalCoreInterfaceare removed. - Added kernel test infrastructure for field handler round-trips. (#341)
A sharedFieldHandlerKernelTestBasewithattachField()andassertFieldRoundTripViaDriver()helpers backs new kernel tests for fifteen field handlers (Datetime, Link, ListString, ListInteger, ListFloat, Daterange, TextWithSummary, Default, EntityReference, File, Image, Address, Name, SupportedImage, Time). Writing the tests exposed and fixed two latent bugs:Core::entityCreate()returned the wrong type, andCore::userCreate()re-validated the Drupal site even when bootstrap had already done so. - Removed Docker from CI in favour of 'shivammathur/setup-php' action. (#340)
The CI workflow now runs PHP and Composer directly on the GitHub-hosted runner viashivammathur/setup-php@v2, withactions/cachefor the Composer cache. Thedocker-compose.ymland thewodby/drupal-phpimage dependency are gone, removing thePHP_VERSION/DOCKER_USER_IDenv scaffolding the container needed. - Removed 'doc/' in favour of using markdown in repo. (#339)
The Sphinx tree (Makefile,conf.py, eight.rstfiles, snippets) and the stale ReadTheDocs link are removed; the architecture/extending guide that lived indoc/extending.rstis condensed into a new "Extending for a new Drupal version" section inCONTRIBUTING.md. - Restructured cores into 'Core/' with version-override lookup chain. (#338)
src/Drupal/Driver/Cores/Drupal8.phpbecomessrc/Drupal/Driver/Core/Core.phpand the seventeen handlers underFields/Drupal8/move toCore/Field/.DrupalDriver::setCoreFromVersion()andCore::getFieldHandler()walk aCore{N}\chain so a future Drupal version that diverges can ship an override file alongside the default without copy-pasting the whole implementation. NoCore{N}/directory exists today; everything resolves to the defaultCore\CoreandCore\Field\*classes. - Added 'declare(strict_types=1)' and PHP 8.2 type declarations via Rector. (#337)
Strict types and PHP 8.2 parameter / return / property declarations were applied across all 56 source and test files via a Rector config that mirrors the Drupal Extension setup (palantirnet/drupal-rectorwas dropped in favour ofrector/rector ^2.0directly). Constructor property promotion was reverted in three classes where it mangled existing docblocks, and that rule is now skipped going forward. - Removed Drupal 6 and 7 support and bumped composer constraints for 3.x. (#336)
D6 and D7 cores, field handlers, and tests are deleted; PHP is now^8.2, Drupal is^10 || ^11, Symfony is^6.4 || ^7. The CI matrix doubles to ten entries because each PHP/Drupal combination now runs under both--prefer-lowestand normal dependency resolution.masteris the active 3.x branch; the maintenance line lives on the new2.xbranch. - Removed 'CHANGELOG.md' in favour of GitHub Releases. (#335)
The 199-line in-repo changelog was redundant with thedraft-release-notes.ymlworkflow that already publishes notes via GitHub Releases. The "Release notes" link inREADME.mdnow points at the Releases page. - Split 'lint' job out of 'tests' job in CI workflow. (#334)
Lint andcomposer normalize --dry-runwere moved into a dedicated job that runs once on PHP 8.4 / Drupal 11 instead of repeating across every matrix combination. Thetestsjob retains its full PHP/Drupal matrix and coverage logic.
Full Changelog: v2.5.1...v3.0.0-alpha1
v3.0.0-rc1 (May 18, 2026)
- [#366] Made 'NameHandler' honour the field's 'components' setting. @AlexSkrypnyk (#368)
- [#365] Added creation alias registry for entity stub properties. @AlexSkrypnyk (#369)
Full Changelog: v3.0.0-alpha1...v3.0.0-rc1
v3.0.0-rc2 (May 19, 2026)
All changes
- Added 'SmartdateHandler' for the
smart_datecontrib module. @AlexSkrypnyk (#371)
Closes a v3.0 regression: writing to asmartdatefield (six storage columns from thesmart_datecontrib module) threwRuntimeExceptionbecause v3.0 hardenedDefaultHandlerto reject unknown multi-column fields. The new handler accepts a positional[start, end]pair, a keyed record, or a list of either form for multi-delta fields; parses numeric Unix timestamps andstrtotime()-compatible strings; and auto-derivesdurationas(end - start) / 60clamped to zero when both endpoints are supplied without an explicit duration. - Fixed 'ImageHandler' and 'DatetimeHandler' compound parser shape. @AlexSkrypnyk (#370)
Fixes aTypeErrorinImageHandler::expand()when callers handed it a list-of-records shape like[['target_id' => 'foo.jpg', ...]](which the v6 DrupalExtension field parser naturally produces) instead of a bare path. Adds a sharednormalise()helper onAbstractHandlerthat folds any loose caller shape - bare scalar, list of scalars, single record, list of records, or mixed list - into a canonical list of records, and migratesImageHandler,DatetimeHandler, and the three text handlers onto it. The ambiguous "positional value + named extras" shape (e.g.['/path', 'alt' => 'A']) is now rejected withInvalidArgumentExceptioninstead of being silently disambiguated.
Full Changelog: v3.0.0-rc1...v3.0.0-rc2
Release notes
Open source →- [ #374 ] Added 'ColorFieldTypeHandler' for the 'color_field' contrib module. @AlexSkrypnyk ( #375 )
Full Changelog : v3.0.0...v3.1.0
@AlexSkrypnyk
Contributors
AlexSkrypnyk
Assets 2 Loading
Uh oh!
There was an error while loading. Please reload this page .
All reactions
-
v3.0.0-rc219 May 2026 pre-releaseRelease notes
Open source →What's new since v3.0.0-rc1
All changes
- Added 'SmartdateHandler' for the
smart_datecontrib module. @AlexSkrypnyk (#371)
Closes a v3.0 regression: writing to asmartdatefield (six storage columns from thesmart_datecontrib module) threwRuntimeExceptionbecause v3.0 hardenedDefaultHandlerto reject unknown multi-column fields. The new handler accepts a positional[start, end]pair, a keyed record, or a list of either form for multi-delta fields; parses numeric Unix timestamps andstrtotime()-compatible strings; and auto-derivesdurationas(end - start) / 60clamped to zero when both endpoints are supplied without an explicit duration. - Fixed 'ImageHandler' and 'DatetimeHandler' compound parser shape. @AlexSkrypnyk (#370)
Fixes aTypeErrorinImageHandler::expand()when callers handed it a list-of-records shape like[['target_id' => 'foo.jpg', ...]](which the v6 DrupalExtension field parser naturally produces) instead of a bare path. Adds a sharednormalise()helper onAbstractHandlerthat folds any loose caller shape - bare scalar, list of scalars, single record, list of records, or mixed list - into a canonical list of records, and migratesImageHandler,DatetimeHandler, and the three text handlers onto it. The ambiguous "positional value + named extras" shape (e.g.['/path', 'alt' => 'A']) is now rejected withInvalidArgumentExceptioninstead of being silently disambiguated.
Full Changelog: v3.0.0-rc1...v3.0.0-rc2
Release notes
Open source →- Fixed documentation inaccuracies surfaced during v3.0.0 release review. @AlexSkrypnyk ( #373 )
Full Changelog : v3.0.0-rc2...v3.0.0
@AlexSkrypnyk
- Added 'SmartdateHandler' for the
-
v3.0.0-rc117 May 2026 pre-releaseRelease notes
Open source →What's new since v3.0.0-alpha1
- [#366] Made 'NameHandler' honour the field's 'components' setting. @AlexSkrypnyk (#368)
- [#365] Added creation alias registry for entity stub properties. @AlexSkrypnyk (#369)
Full Changelog: v3.0.0-alpha1...v3.0.0-rc1
Release notes
Open source →All changes
-
Added 'SmartdateHandler' for the smart_date contrib module. @AlexSkrypnyk ( #371 ) Closes a v3.0 regression: writing to a smartdate field (six storage columns from the smart_date contrib module) threw RuntimeException because v3.0 hardened DefaultHandler to reject unknown multi-column fields. The new handler accepts a positional [start, end] pair, a keyed record, or a list of either form for multi-delta fields; parses numeric Unix timestamps and strtotime() -compatible strings; and auto-derives duration as (end - start) / 60 clamped to zero when both endpoints are supplied without an explicit duration.
-
Fixed 'ImageHandler' and 'DatetimeHandler' compound parser shape. @AlexSkrypnyk ( #370 ) Fixes a TypeError in ImageHandler::expand() when callers handed it a list-of-records shape like [['target_id' => 'foo.jpg', ...]] (which the v6 DrupalExtension field parser naturally produces) instead of a bare path. Adds a shared normalise() helper on AbstractHandler that folds any loose caller shape - bare scalar, list of scalars, single record, list of records, or mixed list - into a canonical list of records, and migrates ImageHandler , DatetimeHandler , and the three text handlers onto it. The ambiguous "positional value + named extras" shape (e.g. ['/path', 'alt' => 'A'] ) is now rejected with InvalidArgumentException instead of being silently disambiguated.
Full Changelog : v3.0.0-rc1...v3.0.0-rc2
Contributors
AlexSkrypnyk
Assets 2 Loading
Uh oh!
There was an error while loading. Please reload this page .
All reactions
-
v3.0.0-alpha103 May 2026 pre-releaseRelease notes
Open source →What's new since v2.5.1
v3 reworks the driver contract into a set of composable capability interfaces, narrows entity stubs to a typed value object, drops Drupal 6/7, and tightens the supported PHP/Drupal range. Most changes are mechanical; a small amount of consumer-side code (typically in DrupalExtension integrations) may need updating.
Highlights
- [v3] Restructured driver and core contracts into composable capability interfaces. (#342)
The driver surface is now a set of twelve small, named capability interfaces (AuthenticationCapabilityInterface,CacheCapabilityInterface,ContentCapabilityInterface, ...) composed into per-driver contracts. Consumers caninstanceof-check exactly the capability they need (if ($driver instanceof MailCapabilityInterface) { ... }) instead of catchingUnsupportedDriverActionExceptionfrom a giant base class, and downstream projects can build new drivers by implementing only the capabilities they actually support. - [#155] Added field handler registry with directory-based discovery and CoreInterface injection. (#353)
Consumer projects now have two real extension seams that don't require squatting on this project's namespace:$core->registerFieldHandler('my_type', MyHandler::class)overrides one handler from anywhere, and$driver->setCore($custom_core)swaps the entireCorefor a consumer-owned subclass. Internally the project dogfoods the same mechanism via a glob-based scan of the siblingField/directory, so future Drupal versions add handlers by file-drop instead of editing a hardcoded list. - [#111] Added 'BlockCapabilityInterface' with block placement and content-block methods. (#354)
Closes a 2016 gap (#111):blockPlace(),blockDelete(),blockContentCreate(), andblockContentDelete()are now first-class driver operations on D10/D11, supporting both theblockconfig entity (placement in a region) and theblock_contentcontent entity (custom block bodies). Block-type creation is intentionally out of scope - like node types and vocabularies, that is schema-level setup that belongs in the test site's configuration, not in a scenario step. - Added 'FieldClassifier' with nine F-row predicates and rewrote field-expansion pipeline. (#359)
The newFieldClassifierprovides nine origin/storage predicates (F1-F9) that classify every Drupal field, andCore::expandEntityFields()now routes through them so only writable, handler-eligible fields enter the pipeline. Previously, computed and non-writable base fields slipped through the configured-field filter and produced silent garbage; nowDefaultHandlerthrows loudly for any multi-column type without a registered handler so gaps surface immediately. A newEntityReferenceRevisionsHandlercovers paragraph and other revision-tracked references. - [#362] Replaced 'stdClass' entity stubs with typed 'EntityStub' class. (#363)
The untyped\stdClassshape that flowed between callers and every driver create/delete method was replaced with a typedEntityStubcarrying entity type, bundle, a mutable values bag, and a saved-entity slot. Callers can now use typed accessors ($stub->getId(),$stub->isSaved(),$stub->getSavedEntity()) instead of guessing which property holds the saved id, and the field-handler boundary is also typed so the impedance mismatch between the driver and the expand pipeline is gone. - [#124] Added 'BooleanHandler' accepting field labels and canonical yes/no/true/false/on/off forms. (#350)
Scenarios stubbingbooleanfields no longer have to use raw1/0. The handler matches the field's ownon_label/off_labelfirst (case-insensitive, so a German site withJa/Neinworks verbatim), then falls back to the canonical English allow-list viafilter_var(FILTER_VALIDATE_BOOLEAN). Anything outside both sets throws aRuntimeExceptionlisting the accepted values, replacing silent coercion toFALSE. - [#118] Improved error messages for unknown entity types, bundles, vocabularies, and parent terms. (#351)
Stubs referencing a missing entity type, bundle, vocabulary, or parent term now fail with anInvalidArgumentExceptionthat quotes the offending value (Unknown entity type "fake_type".,Cannot create term because parent term 'Missing' does not exist in vocabulary 'tags'.) instead of leaking low-level plugin-system errors or undefined-property warnings. Scenario authors get an actionable message at the failure boundary instead of a stack trace into Drupal internals.
Breaking changes
- [#362] Replaced 'stdClass' entity stubs with typed 'EntityStub' class. (#363)
Every capability interface that previously accepted or returned\stdClass(AuthenticationCapabilityInterface::login(), everyBlock*,Content*,Language*, andUser*create/delete method) now declaresEntityStubInterface. Migration: replace(object) ['type' => 'page', ...]constructs withnew \Drupal\Driver\Entity\EntityStub('node', 'page', [...]), and read the saved id via$stub->getId()/ saved entity via$stub->getSavedEntity()instead of guessing property names. There is no\stdClassshim - this is a clean cut. - Added 'FieldClassifier' with nine F-row predicates and rewrote field-expansion pipeline. (#359)
FieldCapabilityInterfaceis removed along withfieldExists()andfieldIsBase()onCoreandDrupalDriverInterface; both predicates now live onFieldClassifierInterface.Core::expandEntityFields()no longer accepts a$base_fieldsargument andCore::getEntityFieldTypes()now takes?string $bundleinstead ofarray $base_fields.Core::expandEntityBaseFields()andCore::detectBaseFieldsOnEntity()were removed.DefaultHandler::expand()now throws\RuntimeExceptionfor any field whose storage is not a singlevaluecolumn instead of silently emitting garbage. Migration is documented inUPGRADING.md. - Tightened 'DrupalDriverInterface' contract and 'DrupalDriver' visibility. (#358)
getCore(),setCore(), andgetDrupalVersion()are now part of theDrupalDriverInterfacecontract, so any consumer that implements the interface directly must add these three methods.DrupalDriver::$coreandDrupalDriver::$versionwere narrowed frompublictoprotected; replace direct property access ($driver->core,$driver->version) with the new public accessors. - [#155] Added field handler registry with directory-based discovery and CoreInterface injection. (#353)
DrupalDriver::setCore()signature changed fromsetCore(array $available_cores)(keyed by Drupal version) tosetCore(CoreInterface $core). Migration: callers that previously passed a version-keyed array of core classes must now construct or select a singleCoreInterfaceinstance themselves and inject it. The convention-basedsetCoreFromVersion()lookup that walksDrupal\Driver\Core{N}\Coreclasses is unchanged. - [#154] Removed unused 'TaxonomyTermReferenceHandler' and its tests. (#352)
The handler matched the legacytaxonomy_term_referencefield type that Drupal core removed in 8.0.0-beta10 (2015) and was unreachable on every supported Drupal version. Modern taxonomy-linking fields areentity_referencewithtarget_type = taxonomy_termand route throughEntityReferenceHandler; consumers that subclassedTaxonomyTermReferenceHandlershould subclassEntityReferenceHandlerinstead. - [#270] Fixed 'entityCreate()' base field expansion and dynamic id key resolution. (#346)
entityCreate()now populates the entity-type-specific id property (e.g.$stub->uidfor users,$stub->nidfor nodes) instead of always assigning to$stub->id, andentityDelete()reads from the same key. Consumers reading$stub->idafter a genericentityCreate('user', $stub)must switch to$stub->$id_key(resolved via$entity_type_definition->getKey('id')) or read from the returned entity. Base fields set on the stub (e.g.commerce_product.variations) are now auto-routed through the handler pipeline rather than reaching storage in raw form. - [v3] Removed 'drush-ops/behat-drush-endpoint' and Drush-backed entity and field methods. (#343)
DrushDriverInterfaceno longer extendsContentCapabilityInterfaceorFieldCapabilityInterface.entityCreate,entityDelete,nodeCreate,nodeDelete,termCreate,termDelete,fieldExists, andfieldIsBaseare gone fromDrushDriver, and thedrush-ops/behat-drush-endpointcompanion module is no longer required. Consumers that need entity CRUD or field introspection should switch toDrupalDriver; seeUPGRADING.mdfor the per-method migration table. - [v3] Restructured driver and core contracts into composable capability interfaces. (#342)
The monolithicDriverInterface/BaseDrivermodel was replaced with twelve fine-grainedCapability\*CapabilityInterfaceinterfaces and per-driver composite interfaces (DrupalDriverInterface,DrushDriverInterface,BlackboxDriverInterface). Many methods were renamed to lead with their capability prefix (createNode→nodeCreate,createTerm→termCreate,createEntity→entityCreate,isField→fieldExists,isBaseField→fieldIsBase,clearCache→cacheClear,clearStaticCaches→cacheClearStatic,runCron→cronRun,fetchWatchdog→watchdogFetch, all*Mailmethods →mail*).BaseDriver,AbstractCore,AuthenticationDriverInterface,CoreAuthenticationInterface, andDrupalCoreInterfacewere removed.UPGRADING.mddocuments every rename and theinstanceofcapability-check pattern. - Restructured cores into 'Core/' with version-override lookup chain. (#338)
src/Drupal/Driver/Cores/Drupal8.phpwas renamed tosrc/Drupal/Driver/Core/Core.php(classDrupal8→Core) andsrc/Drupal/Driver/Fields/Drupal8/*moved tosrc/Drupal/Driver/Core/Field/*. Any code referencingDrupal\Driver\Cores\Drupal8,Drupal\Driver\Fields\Drupal8\*, or the oldCores/andFields/namespaces must update toDrupal\Driver\Core\CoreandDrupal\Driver\Core\Field\*. A future Drupal version that diverges can ship aDrupal\Driver\Core{N}\Coresubclass and thesetCoreFromVersion()chain will pick it up automatically. - Added 'declare(strict_types=1)' and PHP 8.2 type declarations via Rector. (#337)
Every source and test file now declares strict types and parameters/returns/properties carry PHP 8.2 type declarations. Callers that previously relied on PHP's loose-mode coercion (passing strings where ints are expected, etc.) will now hitTypeErrorat the boundary; audit any consumer code that calls intoCore,DrupalDriver,DrushDriver, or any handler. - Removed Drupal 6 and 7 support and bumped composer constraints for 3.x. (#336)
Minimum PHP is now 8.2 and supported Drupal versions are 10 and 11 only; D6 and D7 core classes, field handlers, and tests were deleted.DrupalDriver::detectMajorVersion()throws aBootstrapExceptionwhen it sees Drupal < 10. Symfony deps were lifted to^6.4 || ^7. Sites still on PHP 8.1 or Drupal 9 must stay on the 2.x line (now maintained on the2.xbranch).
All changes
- [#362] Replaced 'stdClass' entity stubs with typed 'EntityStub' class. (#363)
Every capability interface that touched\stdClassnow declaresEntityStubInterface, the field-handler boundary accepts the typed stub directly, and the previous(object) [...]constructs are replaced withnew EntityStub($entity_type, $bundle, [...]). Callers can read the saved id and entity through typed accessors instead of guessing property names. - Added 'DrupalExtension' downstream smoke check job to CI.
A new CI job runs thedrupalextensiontest suite against this branch so regressions that only surface in the primary downstream consumer are caught before release. - Restored 'text_long', 'text', 'file', 'image' field-handler semantics and added type-coverage safety net. (#360)
The v3 pipeline rewrite shipped withoutTextHandlerandTextLongHandler, both multi-column types that immediately tripped the new loud-failure policy inDefaultHandler. This adds both handlers, restores the 2.x reuse-by-URI / reuse-by-basename behaviour forFileHandler(andImageHandler, which now extendsFileHandler), and addsFieldTypeCoverageKernelTestthat walks every registered field type and fails if a new core type is missing a handler entry. - Added 'FieldClassifier' with nine F-row predicates and rewrote field-expansion pipeline. (#359)
FieldClassifierexposes nine predicates that classify each field by origin (base / configurable / bundle-storage-backed) and storage profile (standard / computed / custom).Core::expandEntityFields()now routes through the classifier so only F1, F5, and F9 rows enter the handler pipeline, computed and non-writable fields no longer reach storage, andDefaultHandlerthrows for any multi-column type without a registered handler.EntityReferenceRevisionsHandleris added for paragraphs and other revision-tracked references;FieldCapabilityInterfaceand thefieldExists/fieldIsBasepredicates are removed. - Tightened 'DrupalDriverInterface' contract and 'DrupalDriver' visibility. (#358)
getCore(),setCore(), andgetDrupalVersion()are now declared onDrupalDriverInterface, and$core/$versiononDrupalDriverwere narrowed frompublictoprotected. Consumers that hand-rolled aDrupalDriverInterfaceimplementation must add the three accessors, and any callers reading the properties directly must switch to the public methods. - [#111] Added 'BlockCapabilityInterface' with block placement and content-block methods. (#354)
blockPlace(),blockDelete(),blockContentCreate(), andblockContentDelete()are now first-class capability methods onDrupalDriverandCore. Placement covers theblockconfig entity (id auto-generated when omitted, plugin/theme/region wiring), and the content-block methods cover theblock_contentcontent entity via the generic field-expansion pipeline. Block-type creation is intentionally out of scope. - [#155] Added field handler registry with directory-based discovery and CoreInterface injection. (#353)
Consumers register custom handlers with$core->registerFieldHandler($field_type, $class)(last registration wins), and$driver->setCore(CoreInterface $core)lets them inject anyCoreInterfaceimplementation. InternallyCore::__construct()callsregisterDefaultFieldHandlers()which scans the siblingField/directory and registers every concrete*Handlerclass, so future Drupal versions add handlers additively by dropping a file into aCore{N}\Field\directory. - [#154] Removed unused 'TaxonomyTermReferenceHandler' and its tests. (#352)
The handler matched the legacytaxonomy_term_referencefield type that Drupal core removed in 8.0.0-beta10 (2015) and was unreachable on every supported Drupal version. Modern taxonomy references go throughEntityReferenceHandler(already covered by the existing kernel test), so the orphaned class and its two tests are gone. - [#118] Improved error messages for unknown entity types, bundles, vocabularies, and parent terms. (#351)
entityCreate()andentityDelete()now throwInvalidArgumentException('Unknown entity type "X".')when given a bad type instead of leakingPluginNotFoundException.termCreate()validates thevocabulary_machine_nameproperty, the existence of the named vocabulary, and the existence of any named parent term up front, with messages that quote the offending value. - [#124] Added 'BooleanHandler' accepting field labels and canonical yes/no/true/false/on/off forms. (#350)
The new handler readson_label/off_labelfrom the field config and matches them case-insensitively, then falls back tofilter_var(FILTER_VALIDATE_BOOLEAN)for the canonical English forms. Localised label sets (e.g.Ja/Nein) work for free; values that match neither set throw aRuntimeExceptionlisting all accepted forms. - [#202] Allowed explicit id and label on 'roleCreate()'. (#349)
roleCreate(array $permissions, ?string $id = NULL, ?string $label = NULL)lets callers name roles explicitly while preserving the existing random-id behaviour when both arguments are omitted. Closes a 2018 issue: scenarios that assert against a role by name no longer have to scrape the generated id out of the return value. When only$idis given, it doubles as the human-readable label. - Decoupled dev dependencies from 'drupal/core-dev' metapackage. (#348)
Thedrupal/core-devrequirement was replaced with the three transitive deps the test suite actually needs (vfsstream,prophecy-phpunit,phpstan/extension-installer), and a project-ownedtests/bootstrap.phpreplaces the upstream Drupal bootstrap so the Mink class-alias line no longer fatals when Behat/Mink is absent. The local install footprint drops by roughly 50 transitive packages. - Fixed 'EntityReferenceHandler::expand()' to preserve extra item properties on associative-array deltas. (#347)
Associative-array deltas (the shape file fields, image fields, andentity_reference_revisionsuse -['target_id' => 'alice', 'display' => 1]) previously crashed withCalling Drupal\Core\Database\Query\Condition::condition() without an array compatible operator is not supported. The handler now extracts the field's main property as the lookup label, resolves it to an id, writes the id back into the array, and round-trips the remaining columns intact. - [#270] Fixed 'entityCreate()' base field expansion and dynamic id key resolution. (#346)
expandEntityFields()now auto-detects base fields set as properties on the stub (excluding the id and bundle keys) and routes them through the handler pipeline, so base entity-reference fields likecommerce_product.variationsresolve their labels instead of reaching storage raw.entityCreate()andentityDelete()now resolve the id key per entity type (uid,nid,tid, etc.) instead of hardcodingid. - Refactored source files for readability. (#345)
Vertical-rhythm pass over twelve source files insrc/Drupal/Driver/: guard clauses replace nested conditionals, blank lines bracketif/foreach/forblocks, generic$returnvariables are renamed to semantic names, and step-by-step comments that just restated the code are removed. One incidental correctness fix inDrushDriver::drush()replaces an!== NULLcheck on a typed non-nullable property withisset()so root-mode no longer trips on uninitialised access. - [v3] Reordered driver methods into canonical capability-based layout and widened property visibility to 'protected'. (#344)
Public methods onDrupalDriverandDrushDriverare now grouped alphabetically by capability (Authentication, Cache, Config, Content, ...) so navigating between the two drivers is consistent.$bootstrapped,$drupalRoot,$uri,$random, and$argumentswere widened fromprivatetoprotectedso subclasses can access them. No logic, signatures, or behaviour changed. - [v3] Removed 'drush-ops/behat-drush-endpoint' and Drush-backed entity and field methods. (#343)
DrushDriverInterfaceno longer extendsContentCapabilityInterfaceorFieldCapabilityInterface, and the eight methods that required the companion endpoint module (entityCreate,entityDelete,nodeCreate,nodeDelete,termCreate,termDelete,fieldExists,fieldIsBase) are removed fromDrushDriver. Consumers that need those operations should useDrupalDriver; seeUPGRADING.md. - [v3] Restructured driver and core contracts into composable capability interfaces. (#342)
Twelve fine-grainedCapability\*CapabilityInterfaceinterfaces replace the monolithicDriverInterface/BaseDrivermodel; each driver opts into the capabilities it actually services. Many methods were renamed to lead with their capability (createNode→nodeCreate,clearCache→cacheClear,runCron→cronRun,isField→fieldExists, the*Mailfamily flipped tomail*);BaseDriver,AbstractCore,AuthenticationDriverInterface,CoreAuthenticationInterface, andDrupalCoreInterfaceare removed. - Added kernel test infrastructure for field handler round-trips. (#341)
A sharedFieldHandlerKernelTestBasewithattachField()andassertFieldRoundTripViaDriver()helpers backs new kernel tests for fifteen field handlers (Datetime, Link, ListString, ListInteger, ListFloat, Daterange, TextWithSummary, Default, EntityReference, File, Image, Address, Name, SupportedImage, Time). Writing the tests exposed and fixed two latent bugs:Core::entityCreate()returned the wrong type, andCore::userCreate()re-validated the Drupal site even when bootstrap had already done so. - Removed Docker from CI in favour of 'shivammathur/setup-php' action. (#340)
The CI workflow now runs PHP and Composer directly on the GitHub-hosted runner viashivammathur/setup-php@v2, withactions/cachefor the Composer cache. Thedocker-compose.ymland thewodby/drupal-phpimage dependency are gone, removing thePHP_VERSION/DOCKER_USER_IDenv scaffolding the container needed. - Removed 'doc/' in favour of using markdown in repo. (#339)
The Sphinx tree (Makefile,conf.py, eight.rstfiles, snippets) and the stale ReadTheDocs link are removed; the architecture/extending guide that lived indoc/extending.rstis condensed into a new "Extending for a new Drupal version" section inCONTRIBUTING.md. - Restructured cores into 'Core/' with version-override lookup chain. (#338)
src/Drupal/Driver/Cores/Drupal8.phpbecomessrc/Drupal/Driver/Core/Core.phpand the seventeen handlers underFields/Drupal8/move toCore/Field/.DrupalDriver::setCoreFromVersion()andCore::getFieldHandler()walk aCore{N}\chain so a future Drupal version that diverges can ship an override file alongside the default without copy-pasting the whole implementation. NoCore{N}/directory exists today; everything resolves to the defaultCore\CoreandCore\Field\*classes. - Added 'declare(strict_types=1)' and PHP 8.2 type declarations via Rector. (#337)
Strict types and PHP 8.2 parameter / return / property declarations were applied across all 56 source and test files via a Rector config that mirrors the Drupal Extension setup (palantirnet/drupal-rectorwas dropped in favour ofrector/rector ^2.0directly). Constructor property promotion was reverted in three classes where it mangled existing docblocks, and that rule is now skipped going forward. - Removed Drupal 6 and 7 support and bumped composer constraints for 3.x. (#336)
D6 and D7 cores, field handlers, and tests are deleted; PHP is now^8.2, Drupal is^10 || ^11, Symfony is^6.4 || ^7. The CI matrix doubles to ten entries because each PHP/Drupal combination now runs under both--prefer-lowestand normal dependency resolution.masteris the active 3.x branch; the maintenance line lives on the new2.xbranch. - Removed 'CHANGELOG.md' in favour of GitHub Releases. (#335)
The 199-line in-repo changelog was redundant with thedraft-release-notes.ymlworkflow that already publishes notes via GitHub Releases. The "Release notes" link inREADME.mdnow points at the Releases page. - Split 'lint' job out of 'tests' job in CI workflow. (#334)
Lint andcomposer normalize --dry-runwere moved into a dedicated job that runs once on PHP 8.4 / Drupal 11 instead of repeating across every matrix combination. Thetestsjob retains its full PHP/Drupal matrix and coverage logic.
Full Changelog: v2.5.1...3.0.0-alpha1
Release notes
Open source →-
[ #366 ] Made 'NameHandler' honour the field's 'components' setting. @AlexSkrypnyk ( #368 )
-
[ #365 ] Added creation alias registry for entity stub properties. @AlexSkrypnyk ( #369 )
Full Changelog : v3.0.0-alpha1...v3.0.0-rc1
@AlexSkrypnyk
Contributors
AlexSkrypnyk
Assets 2 Loading
Uh oh!
There was an error while loading. Please reload this page .
All reactions
- [v3] Restructured driver and core contracts into composable capability interfaces. (#342)
-
v2.5.127 Apr 2026Release notes
Open source →What's new since v2.5.0
- #357 Fixed
FileHandlerregression introduced in 2.5.0 where referencing an existing managed file by filename failed withFailed to open stream: No such file or directory.
Full Changelog: v2.5.0...v2.5.1
Release notes
Open source →v3 reworks the driver contract into a set of composable capability interfaces, narrows entity stubs to a typed value object, drops Drupal 6/7, and tightens the supported PHP/Drupal range. Most changes are mechanical; a small amount of consumer-side code (typically in DrupalExtension integrations) may need updating.
Highlights
-
[v3] Restructured driver and core contracts into composable capability interfaces. ( #342 ) The driver surface is now a set of twelve small, named capability interfaces ( AuthenticationCapabilityInterface , CacheCapabilityInterface , ContentCapabilityInterface , ...) composed into per-driver contracts. Consumers can instanceof -check exactly the capability they need ( if ($driver instanceof MailCapabilityInterface) { ... } ) instead of catching UnsupportedDriverActionException from a giant base class, and downstream projects can build new drivers by implementing only the capabilities they actually support.
-
[ #155 ] Added field handler registry with directory-based discovery and CoreInterface injection. ( #353 ) Consumer projects now have two real extension seams that don't require squatting on this project's namespace: $core->registerFieldHandler('my_type', MyHandler::class) overrides one handler from anywhere, and $driver->setCore($custom_core) swaps the entire Core for a consumer-owned subclass. Internally the project dogfoods the same mechanism via a glob-based scan of the sibling Field/ directory, so future Drupal versions add handlers by file-drop instead of editing a hardcoded list.
-
[ #111 ] Added 'BlockCapabilityInterface' with block placement and content-block methods. ( #354 ) Closes a 2016 gap ( #111 ): blockPlace() , blockDelete() , blockContentCreate() , and blockContentDelete() are now first-class driver operations on D10/D11, supporting both the block config entity (placement in a region) and the block_content content entity (custom block bodies). Block-type creation is intentionally out of scope - like node types and vocabularies, that is schema-level setup that belongs in the test site's configuration, not in a scenario step.
-
Added 'FieldClassifier' with nine F-row predicates and rewrote field-expansion pipeline. ( #359 ) The new FieldClassifier provides nine origin/storage predicates (F1-F9) that classify every Drupal field, and Core::expandEntityFields() now routes through them so only writable, handler-eligible fields enter the pipeline. Previously, computed and non-writable base fields slipped through the configured-field filter and produced silent garbage; now DefaultHandler throws loudly for any multi-column type without a registered handler so gaps surface immediately. A new EntityReferenceRevisionsHandler covers paragraph and other revision-tracked references.
-
[ #362 ] Replaced 'stdClass' entity stubs with typed 'EntityStub' class. ( #363 ) The untyped \stdClass shape that flowed between callers and every driver create/delete method was replaced with a typed EntityStub carrying entity type, bundle, a mutable values bag, and a saved-entity slot. Callers can now use typed accessors ( $stub->getId() , $stub->isSaved() , $stub->getSavedEntity() ) instead of guessing which property holds the saved id, and the field-handler boundary is also typed so the impedance mismatch between the driver and the expand pipeline is gone.
-
[ #124 ] Added 'BooleanHandler' accepting field labels and canonical yes/no/true/false/on/off forms. ( #350 ) Scenarios stubbing boolean fields no longer have to use raw 1 / 0 . The handler matches the field's own on_label / off_label first (case-insensitive, so a German site with Ja / Nein works verbatim), then falls back to the canonical English allow-list via filter_var(FILTER_VALIDATE_BOOLEAN) . Anything outside both sets throws a RuntimeException listing the accepted values, replacing silent coercion to FALSE .
-
[ #118 ] Improved error messages for unknown entity types, bundles, vocabularies, and parent terms. ( #351 ) Stubs referencing a missing entity type, bundle, vocabulary, or parent term now fail with an InvalidArgumentException that quotes the offending value ( Unknown entity type "fake_type". , Cannot create term because parent term 'Missing' does not exist in vocabulary 'tags'. ) instead of leaking low-level plugin-system errors or undefined-property warnings. Scenario authors get an actionable message at the failure boundary instead of a stack trace into Drupal internals.
Breaking changes
-
[ #362 ] Replaced 'stdClass' entity stubs with typed 'EntityStub' class. ( #363 ) Every capability interface that previously accepted or returned \stdClass ( AuthenticationCapabilityInterface::login() , every Block* , Content* , Language* , and User* create/delete method) now declares EntityStubInterface . Migration: replace (object) ['type' => 'page', ...] constructs with new \Drupal\Driver\Entity\EntityStub('node', 'page', [...]) , and read the saved id via $stub->getId() / saved entity via $stub->getSavedEntity() instead of guessing property names. There is no \stdClass shim - this is a clean cut.
-
Added 'FieldClassifier' with nine F-row predicates and rewrote field-expansion pipeline. ( #359 ) FieldCapabilityInterface is removed along with fieldExists() and fieldIsBase() on Core and DrupalDriverInterface ; both predicates now live on FieldClassifierInterface . Core::expandEntityFields() no longer accepts a $base_fields argument and Core::getEntityFieldTypes() now takes ?string $bundle instead of array $base_fields . Core::expandEntityBaseFields() and Core::detectBaseFieldsOnEntity() were removed. DefaultHandler::expand() now throws \RuntimeException for any field whose storage is not a single value column instead of silently emitting garbage. Migration is documented in UPGRADING.md .
-
Tightened 'DrupalDriverInterface' contract and 'DrupalDriver' visibility. ( #358 ) getCore() , setCore() , and getDrupalVersion() are now part of the DrupalDriverInterface contract, so any consumer that implements the interface directly must add these three methods. DrupalDriver::$core and DrupalDriver::$version were narrowed from public to protected ; replace direct property access ( $driver->core , $driver->version ) with the new public accessors.
-
[ #155 ] Added field handler registry with directory-based discovery and CoreInterface injection. ( #353 ) DrupalDriver::setCore() signature changed from setCore(array $available_cores) (keyed by Drupal version) to setCore(CoreInterface $core) . Migration: callers that previously passed a version-keyed array of core classes must now construct or select a single CoreInterface instance themselves and inject it. The convention-based setCoreFromVersion() lookup that walks Drupal\Driver\Core{N}\Core classes is unchanged.
-
[ #154 ] Removed unused 'TaxonomyTermReferenceHandler' and its tests. ( #352 ) The handler matched the legacy taxonomy_term_reference field type that Drupal core removed in 8.0.0-beta10 (2015) and was unreachable on every supported Drupal version. Modern taxonomy-linking fields are entity_reference with target_type = taxonomy_term and route through EntityReferenceHandler ; consumers that subclassed TaxonomyTermReferenceHandler should subclass EntityReferenceHandler instead.
-
[ #270 ] Fixed 'entityCreate()' base field expansion and dynamic id key resolution. ( #346 ) entityCreate() now populates the entity-type-specific id property (e.g. $stub->uid for users, $stub->nid for nodes) instead of always assigning to $stub->id , and entityDelete() reads from the same key. Consumers reading $stub->id after a generic entityCreate('user', $stub) must switch to $stub->$id_key (resolved via $entity_type_definition->getKey('id') ) or read from the returned entity. Base fields set on the stub (e.g. commerce_product.variations ) are now auto-routed through the handler pipeline rather than reaching storage in raw form.
-
[v3] Removed 'drush-ops/behat-drush-endpoint' and Drush-backed entity and field methods. ( #343 ) DrushDriverInterface no longer extends ContentCapabilityInterface or FieldCapabilityInterface . entityCreate , entityDelete , nodeCreate , nodeDelete , termCreate , termDelete , fieldExists , and fieldIsBase are gone from DrushDriver , and the drush-ops/behat-drush-endpoint companion module is no longer required. Consumers that need entity CRUD or field introspection should switch to DrupalDriver ; see UPGRADING.md for the per-method migration table.
-
[v3] Restructured driver and core contracts into composable capability interfaces. ( #342 ) The monolithic DriverInterface / BaseDriver model was replaced with twelve fine-grained Capability*CapabilityInterface interfaces and per-driver composite interfaces ( DrupalDriverInterface , DrushDriverInterface , BlackboxDriverInterface ). Many methods were renamed to lead with their capability prefix ( createNode → nodeCreate , createTerm → termCreate , createEntity → entityCreate , isField → fieldExists , isBaseField → fieldIsBase , clearCache → cacheClear , clearStaticCaches → cacheClearStatic , runCron → cronRun , fetchWatchdog → watchdogFetch , all Mail methods → mail ). BaseDriver , AbstractCore , AuthenticationDriverInterface , CoreAuthenticationInterface , and DrupalCoreInterface were removed. UPGRADING.md documents every rename and the instanceof capability-check pattern.
-
Restructured cores into 'Core/' with version-override lookup chain. ( #338 ) src/Drupal/Driver/Cores/Drupal8.php was renamed to src/Drupal/Driver/Core/Core.php (class Drupal8 → Core ) and src/Drupal/Driver/Fields/Drupal8/* moved to src/Drupal/Driver/Core/Field/* . Any code referencing Drupal\Driver\Cores\Drupal8 , `Drupa...
Read more
Assets 2 Loading
Uh oh!
There was an error while loading. Please reload this page .
All reactions
- #357 Fixed
-
v2.5.017 Apr 2026Release notes
Open source →What's new since v2.4.3
Important
This is the last
2.xrelease with Drupal 7 support. Only critical bug fixes will be worked on as patches for2.5, if required.
We are working on3.x, which will remove Drupal 6/7 support and potentially update some APIs.Breaking changes
-
Added 'moduleInstall()' and 'moduleUninstall()' to all drivers. @phenaproxima, @AlexSkrypnyk (#315)
New methodsmoduleInstall($module_name)andmoduleUninstall($module_name)have been added toDriverInterfaceandCoreInterface. Consumers that implement either interface directly must add both methods to their own classes - the simplest migration is to throwUnsupportedDriverActionException, matchingBaseDriver's default. Consumers that extendBaseDriver,DrupalDriver, orDrushDriverneed no code changes; the parent classes already provide working implementations. -
Rewrote 'AddressHandler' with named key support and multi-value handling. @BramDriesen, @daggerhart, @AlexSkrypnyk (#320)
AddressHandler::expand()now returns one entry per delta for multi-value address fields (previously always a single-item array) and throws\RuntimeExceptionwhen it receives an unrecognised named key (previously silently discarded). To migrate, audit any test data that passes associative address values and ensure every key is a valid address sub-field (given_name,family_name,organization,address_line1,address_line2,locality,administrative_area,postal_code,country_code), and update assertions that assumed a single delta for a multi-value address field.
Highlights
-
[#281] Fixed 'parseUserId()' to handle Drush 12+ table output format. @AlexSkrypnyk (#324)
Drush 12+ switcheduser:informationoutput from"User ID : 123"key-value pairs to a bordered table, which previously causedDrushDriver::parseUserId()to always returnnulland broke every user-related operation on newer Drush. The method now detects the table format and extracts the UID from the first data row while preserving backward compatibility with older Drush. -
Added 'moduleInstall()' and 'moduleUninstall()' to all drivers. @phenaproxima, @AlexSkrypnyk (#315)
Tests can now install and uninstall Drupal modules through the driver without dropping to Drush shell commands or raw PHP. The same API works acrossDrupalDriver(direct Drupal API),DrushDriver(Drush CLI), and all supported cores (Drupal 6, 7, and 8+). -
Implemented proper file handling in 'FileHandler'. @azurams, @AlexSkrypnyk (#314)
The Drupal 8+FileHandlerpreviously extendedEntityReferenceHandlerand tried a name-based entity lookup on raw file paths, which never produced a usable file entity. It now reads the file from disk and creates a managed file via thefile.repositoryservice, so tests can populate afilefield by passing a local path. -
Rewrote 'AddressHandler' with named key support and multi-value handling. @BramDriesen, @daggerhart, @AlexSkrypnyk (#320)
Address fields can now be populated using descriptive named keys (given_name,address_line1,country_code, etc.) alongside positional numeric indices, and multi-value address fields correctly produce one delta per value instead of collapsing to a single item. Unknown keys fail loudly with aRuntimeExceptioninstead of being silently dropped. -
Added 'DaterangeHandler' with timezone-aware date formatting. @shrimala, @AlexSkrypnyk (#317)
The Drupal 8+daterangefield type is now a first-class citizen: the handler reads the site timezone fromsystem.date, converts datetime values to UTC storage, and picks the correct storage format based on the field'sdatetime_typesetting. Supports therelative:prefix, namedvalue/end_valuekeys, and positional indices. -
Added 'TimeHandler' for the 'time_field' contrib module. @ericgsmith, @AlexSkrypnyk (#306)
First-class support for thetime_fieldcontrib module. Numeric values are stored as seconds past midnight; string values like"9:30 AM","14:15:30", or"midnight"are parsed viastrtotime()and converted to the storage format automatically. -
Added 'NameHandler' for the Name module with tests. @sensespidey, @AlexSkrypnyk (#322)
First-class support for the Name contrib module covering all six name components (title,given,middle,family,generational,credentials). Accepts the"Family, Given"shorthand string, named array keys, and positional indices, with multi-value field support.
All changes
-
Added PHPUnit tests for D8+ field handlers and exceptions. @AlexSkrypnyk (#331)
Adds targeted PHPUnit coverage for the D8+ field handlers (Address,Daterange,Datetime,Default,EntityReference,File,Image,List*,SupportedImage,TaxonomyTermReference,TextWithSummary) plusBlackboxDriverandUnsupportedDriverActionException. Legacy D6/D7 sources are excluded from coverage measurement, and the CI coverage floor is raised from 10% to 35% to lock in the new D8+ baseline (39% lines). -
Added PHPUnit code coverage reporting to CI with threshold enforcement. @AlexSkrypnyk (#330)
Adds pcov-based coverage reporting on the PHP 8.4 / Drupal 11 matrix entry, uploads HTML, Cobertura, and text artifacts, posts a sticky per-PR coverage comment with a collapsible per-class breakdown, and fails the build when line coverage drops below a configurable threshold. Coverage regressions are now visible before merge. -
Refactored codebase for readability: extracted methods, removed dead code, clarified naming. @AlexSkrypnyk (#329)
Removes dead loops and unreachable branches acrossAbstractHandler,EntityReferenceHandler, andDrupal8; extracts long methods inAddressHandler::expand(),DrupalDriver::getDrupalVersion(), andDrushDriver; and reshapesDaterangeHandlerto extendDatetimeHandlerso date-format logic lives in one place. No behaviour changes. -
Added project-level rector configuration and applied fixes. @AlexSkrypnyk (#328)
Replaces the fragilecp+cdworkaround that copiedpalantirnet/drupal-rector's config into thedrupal/directory with a project-levelrector.phptargeting PHP 7.4.composer lintandcomposer lint-fixnow invoke Rector directly, and the dead-code, code-quality, early-return, andinstanceofrule sets have been applied across 18 source and test files. -
Updated PHPCS configuration and fixed coding standards across the codebase. @AlexSkrypnyk (#327)
Replacesphpcs-ruleset.xmlwithphpcs.xmlthat applies both theDrupalandDrevOpsstandards via a newdrevops/phpcs-standarddev dependency. The DrevOps standard enforces snake_case locals and parameters anddataProvider*naming for test data providers; 358 general plus 311 snake_case fixes were applied, and CI now runslintandtestas separate steps. Strict-type declarations remain deferred to v3.x. -
Added .gitattributes for lean distribution archives. @AlexSkrypnyk (#326)
Adds a project.gitattributeswithexport-ignorerules socomposer installandgit archivepayloads contain onlycomposer.json,LICENSE,README.md,CHANGELOG.md, andsrc/, dropping CI config, tests, Docker, specs, and docs tooling. Drupal scaffold is told not to overwrite the file. -
Updated README and CONTRIBUTING to match DrupalExtension structure. @AlexSkrypnyk (#325)
Modernises theREADME.md(centred heading, grouped badges, driver comparison table, singlecomposer requireinstall step, cleaner usage example) and expandsCONTRIBUTING.mdwith a "How this project works" section, a field-handler reference table, a test-command table, and a v3.x roadmap pointer. Documentation-only change. -
[#281] Fixed 'parseUserId()' to handle Drush 12+ table output format. @AlexSkrypnyk (#324)
Drush 12+ switcheduser:informationoutput from"User ID : 123"key-value pairs to a bordered table, which previously causedDrushDriver::parseUserId()to always returnnulland broke every user-related operation on newer Drush. The method now tries the legacy regex first and, on miss, walks the table rows to extract the UID from the first data row, preserving compatibility with older Drush. -
Resolved project-level Drush binary in DrushDriver. @mxr576, @AlexSkrypnyk (#323)
When the defaultdrushbinary is configured,DrushDrivernow triesCOMPOSER_BIN_DIR, thenvendor/bin/drushrelative to the current working directory, before falling back to the globaldrush. This avoids mismatches between the globally installed Drush and the Drush pinned by a project'scomposer.json. Resolution is extracted into a protectedresolveProjectDrush()method that subclasses can override. -
Added 'NameHandler' for the Name module with tests. @sensespidey, @AlexSkrypnyk (#322)
New handler for the Name contrib module covering all six name components (title,given,middle,family,generational,credentials). Accepts the"Family, Given"shorthand string, named array keys, and positional numeric indices, with multi-value field support. -
Added 'OgStandardReferenceHandler' for Organic Groups. @cheppers, @AlexSkrypnyk (#321)
New handler for the Organic Groupsog_standard_referencefield type. The entity-lookup logic is identical to a plain entity reference, so the handler is a thin subclass ofEntityReferenceHandler. -
Rewrote 'AddressHandler' with named key support and multi-value handling. @BramDriesen, @daggerhart, @AlexSkrypnyk (#320)
Address fields can now be populated using descriptive named keys (given_name,address_line1,country_code, etc.) alongside positional numeric arrays, and multi-value fields correctly produce one entry per delta instead of a single-item array. Hidden-field filtering now works (the previous associative-vs-sequential key bug is fixed),country_codeis recognised explicitly, and unknown named keys raise\RuntimeExceptioninstead of being silently dropped. -
[#210] Skipped exception when deleting non-existent role. @bkosborne, @AlexSkrypnyk (#319)
Drupal8::roleDelete()no longer throws aRuntimeExceptionwhen the target role does not exist, preventing spurious failures in test teardown when a role was never created in the first place. -
Cleared EntityAccessControlHandler static cache in 'clearStaticCaches()'. @pfrenssen, @AlexSkrypnyk (#318)
clearStaticCaches()now callsresetCache()on every entity type's access control handler in addition to the existingdrupal_static_reset()and cache-tag resets, so stale access decisions from one test step cannot persist into a later one. -
Added 'DaterangeHandler' with timezone-aware date formatting. @shrimala, @AlexSkrypnyk (#317)
New handler for the Drupal 8+daterangefield type, aligned withDatetimeHandler's timezone handling: usesDrupalDateTime, reads the site timezone fromsystem.date, converts datetime values to UTC storage, and picks the correct storage format based on the field'sdatetime_typesetting. Supports therelative:prefix, namedvalue/end_valuekeys, and positional indices. -
Added Alex Skrypnyk as maintainer. @AlexSkrypnyk (#316)
Adds[email protected]as a maintainer incomposer.json, reflecting active stewardship of the library. -
Added 'moduleInstall()' and 'moduleUninstall()' to all drivers. @phenaproxima, @AlexSkrypnyk (#315)
IntroducesmoduleInstall($module_name)andmoduleUninstall($module_name)onDriverInterfaceandCoreInterface, wired up acrossDrupalDriver(delegates to the core),DrushDriver(usespm-enable/pm-uninstall --yes), andBaseDriver(throwsUnsupportedDriverActionException). All three cores -Drupal6,Drupal7, andDrupal8- implement the methods using their native module APIs. -
Implemented proper file handling in 'FileHandler'. @azurams, @AlexSkrypnyk (#314)
FileHandlerpreviously extendedEntityReferenceHandlerand tried a name-based entity lookup on raw file paths, which was incorrect for real file fields. It now extendsAbstractHandlerand implementsexpand()by reading the file from disk and creates a managed file entity via\Drupal::service('file.repository')->writeData(), writing to thepublic://stream wrapper with a unique filename. -
Added URI-only string support to 'LinkHandler'. @zolhorvath, @ohthehugemanatee, @AlexSkrypnyk (#313)
LinkHandler::expand()now accepts a plain string as a URI-only link value and normalises it to['uri' => $value]before further processing;nulltitles are stripped from the returned array. Mixed input (plain strings alongside full arrays) is handled correctly in a single call. -
[#192] Allowed 'LinkHandler' to accept named field properties. @chrisolof, @AlexSkrypnyk (#311)
LinkHandler::expand()now accepts named keystitle,uri, andoptionsin addition to the existing numeric-index format. Named keys take precedence via null-coalescing, so both input shapes remain supported without regressing existing tests. -
Skipped processing empty batches in 'processBatch()'. @idimopoulos, @AlexSkrypnyk (#310)
Drupal8::processBatch()now checks the return value ofbatch_get()before callingbatch_process(), preventing a fatal error when tests triggerprocessBatch()without any queued batch operations. -
[#200] Fixed date-only field handling and simplified relative date logic. @ericgsmith, @AlexSkrypnyk (#309)
DatetimeHandlernow inspects the field'sdatetime_typesetting and stores date-only fields usingDATE_STORAGE_FORMATin the site timezone, rather than always applying a UTC-converted datetime format. The relative and explicit branches are unified: therelative:prefix is stripped first, then a single path picks the right storage format by field type, and both cases useDrupalDateTimefor proper timezone handling. -
[#291] Fixed 'EntityReferenceHandler' crash when entity label key is FALSE. @khiminrm, @AlexSkrypnyk (#308)
Entity types such ascommerce_licensedo not define a label key, which causedEntityReferenceHandler::expand()to build anorConditionGroupwith aFALSEfield name and crash the entity query. WhengetKey('label')returnsFALSE, the handler now falls back to an ID-only condition. -
[#293] Added 'SupportedImageHandler' for supported_image module. @chrisolof, @AlexSkrypnyk (#307)
New handler for thesupported_imagecontrib module's field type that mirrorsImageHandlerand adds the module's extra sub-fields:caption_value,caption_format,attribution_value, andattribution_format. -
Added 'TimeHandler' for the 'time_field' contrib module. @ericgsmith, @AlexSkrypnyk (#306)
New handler for thetime_fieldcontrib module. Numeric values pass through as seconds past midnight; string values like"9:30 AM","14:15:30", or"midnight"are parsed viastrtotime()and converted to seconds past midnight automatically. -
PostgreSQL crashes when trying to look up an entity reference field t… @svenbergryen (#305)
Fixes a PostgreSQL-specific crash inEntityReferenceHandlerwhen a non-numeric (string or title) value is used to look up an entity whose ID column is an integer - PostgreSQL rejects the mixed-typeORcondition that MySQL silently accepts. The handler now skips the ID-based match for non-numeric inputs and matches by label only.
Full Changelog: v2.4.3...v2.5.0
@AlexSkrypnyk @azurams @bkosborne @BramDriesen @cheppers @chrisolof @daggerhart @ericgsmith @idimopoulos @khiminrm @mxr576 @ohthehugemanatee @pfrenssen @phenaproxima @sensespidey @shrimala @svenbergryen @zolhorvath
Release notes
Open source →- #357 Fixed FileHandler regression introduced in 2.5.0 where referencing an existing managed file by filename failed with Failed to open stream: No such file or directory .
Full Changelog : v2.5.0...v2.5.1
@AlexSkrypnyk
Contributors
AlexSkrypnyk
Assets 2 Loading
Uh oh!
There was an error while loading. Please reload this page .
All reactions
-
-
v2.4.317 Mar 2026Release notes
Open source →Important
This is the last 2.x release with Drupal 7 support. Only critical bug fixes will be worked on as patches for 2.5 , if required. We are working on 3.x , which will remove Drupal 6/7 support and potentially update some APIs.
Breaking changes
Added 'moduleInstall()' and 'moduleUninstall()' to all drivers. @phenaproxima , @AlexSkrypnyk ( #315 ) New methods moduleInstall($module_name) and moduleUninstall($module_name) have been added to DriverInterface and CoreInterface . Consumers that implement either interface directly must add both methods to their own classes - the simplest migration is to throw UnsupportedDriverActionException , matching BaseDriver 's default. Consumers that extend BaseDriver , DrupalDriver , or DrushDriver need no code changes; the parent classes already provide working implementations.
Rewrote 'AddressHandler' with named key support and multi-value handling. @BramDriesen , @daggerhart , @AlexSkrypnyk ( #320 ) AddressHandler::expand() now returns one entry per delta for multi-value address fields (previously always a single-item array) and throws \RuntimeException when it receives an unrecognised named key (previously silently discarded). To migrate, audit any test data that passes associative address values and ensure every key is a valid address sub-field ( given_name , family_name , organization , address_line1 , address_line2 , locality , administrative_area , postal_code , country_code ), and update assertions that assumed a single delta for a multi-value address field.
Highlights
[ #281 ] Fixed 'parseUserId()' to handle Drush 12+ table output format. @AlexSkrypnyk ( #324 ) Drush 12+ switched user:information output from "User ID : 123" key-value pairs to a bordered table, which previously caused DrushDriver::parseUserId() to always return null and broke every user-related operation on newer Drush. The method now detects the table format and extracts the UID from the first data row while preserving backward compatibility with older Drush.
Added 'moduleInstall()' and 'moduleUninstall()' to all drivers. @phenaproxima , @AlexSkrypnyk ( #315 ) Tests can now install and uninstall Drupal modules through the driver without dropping to Drush shell commands or raw PHP. The same API works across DrupalDriver (direct Drupal API), DrushDriver (Drush CLI), and all supported cores (Drupal 6, 7, and 8+).
Implemented proper file handling in 'FileHandler'. @azurams , @AlexSkrypnyk ( #314 ) The Drupal 8+ FileHandler previously extended EntityReferenceHandler and tried a name-based entity lookup on raw file paths, which never produced a usable file entity. It now reads the file from disk and creates a managed file via the file.repository service, so tests can populate a file field by passing a local path.
Rewrote 'AddressHandler' with named key support and multi-value handling. @BramDriesen , @daggerhart , @AlexSkrypnyk ( #320 ) Address fields can now be populated using descriptive named keys ( given_name , address_line1 , country_code , etc.) alongside positional numeric indices, and multi-value address fields correctly produce one delta per value instead of collapsing to a single item. Unknown keys fail loudly with a RuntimeException instead of being silently dropped.
Added 'DaterangeHandler' with timezone-aware date formatting. @shrimala , @AlexSkrypnyk ( #317 ) The Drupal 8+ daterange field type is now a first-class citizen: the handler reads the site timezone from system.date , converts datetime values to UTC storage, and picks the correct storage format based on the field's datetime_type setting. Supports the relative: prefix, named value / end_value keys, and positional indices.
Added 'TimeHandler' for the 'time_field' contrib module. @ericgsmith , @AlexSkrypnyk ( #306 ) First-class support for the time_field contrib module. Numeric values are stored as seconds past midnight; string values like "9:30 AM" , "14:15:30" , or "midnight" are parsed via strtotime() and converted to the storage format automatically.
Added 'NameHandler' for the Name module with tests. @sensespidey , @AlexSkrypnyk ( #322 ) First-class support for the Name contrib module covering all six name components ( title , given , middle , family , generational , credentials ). Accepts the "Family, Given" shorthand string, named array keys, and positional indices, with multi-value field support.
All changes
Added PHPUnit tests for D8+ field handlers and exceptions. @AlexSkrypnyk ( #331 ) Adds targeted PHPUnit coverage for the D8+ field handlers ( Address , Daterange , Datetime , Default , EntityReference , File , Image , List* , SupportedImage , TaxonomyTermReference , TextWithSummary ) plus BlackboxDriver and UnsupportedDriverActionException . Legacy D6/D7 sources are excluded from coverage measurement, and the CI coverage floor is raised from 10% to 35% to lock in the new D8+ baseline (39% lines).
Added PHPUnit code coverage reporting to CI with threshold enforcement. @AlexSkrypnyk ( #330 ) Adds pcov-based coverage reporting on the PHP 8.4 / Drupal 11 matrix entry, uploads HTML, Cobertura, and text artifacts, posts a sticky per-PR coverage comment with a collapsible per-class breakdown, and fails the build when line coverage drops below a configurable threshold. Coverage regressions are now visible before merge.
Refactored codebase for readability: extracted methods, removed dead code, clarified naming. @AlexSkrypnyk ( #329 ) Removes dead loops and unreachable branches across AbstractHandler , EntityReferenceHandler , and Drupal8 ; extracts long methods in AddressHandler::expand() , DrupalDriver::getDrupalVersion() , and DrushDriver ; and reshapes DaterangeHandler to extend DatetimeHandler so date-format logic lives in one place. No behaviour changes.
Added project-level rector configuration and applied fixes. @AlexSkrypnyk ( #328 ) Replaces the fragile cp + cd workaround that copied palantirnet/drupal-rector 's config into the drupal/ directory with a project-level rector.php targeting PHP 7.4. composer lint and composer lint-fix now invoke Rector directly, and the dead-code, code-quality, early-return, and instanceof rule sets have been applied across 18 source and test files.
Updated PHPCS configuration and fixed coding standards across the codebase. @AlexSkrypnyk ( #327 ) Replaces phpcs-ruleset.xml with phpcs.xml that applies both the Drupal and DrevOps standards via a new drevops/phpcs-standard dev dependency. The DrevOps standard enforces snake_case locals and parameters and dataProvider* naming for test data providers; 358 general plus 311 snake_case fixes were applied, and CI now runs lint and test as separate steps. Strict-type declarations remain deferred to v3.x.
Added .gitattributes for lean distribution archives. @AlexSkrypnyk ( #326 ) Adds a project .gitattributes with export-ignore rules so composer install and git archive payloads contain only composer.json , LICENSE , README.md , CHANGELOG.md , and src/ , dropping CI config, tests, Docker, specs, and docs tooling. Drupal scaffold is told not to overwrite the file.
Updated README and CONTRIBUTING to match DrupalExtension structure. @AlexSkrypnyk ( #325 ) Modernises the README.md (centred heading, grouped badges, driver comparison table, single composer require install step, cleaner usage example) and expands CONTRIBUTING.md with a "How this project works" section, a field-handler reference table, a test-command table, and a v3.x roadmap pointer. Documentation-only change.
[ #281 ] Fixed 'parseUserId()' to handle Drush 12+ table output format. @AlexSkrypnyk ( #324 ) Drush 12+ switched user:information output from "User ID : 123" key-value pairs to a bordered table, which previously caused DrushDriver::parseUserId() to always return null and broke every user-related operation on newer Drush. The method now tries the legacy regex first and, on miss, walks the table rows to extract the UID from the first data row, preserving compatibility with older Drush.
Resolved project-level Drush binary in DrushDriver. @mxr576 , @AlexSkrypnyk ( #323 ) When the default drush binary is configured, DrushDriver now tries COMPOSER_BIN_DIR , then vendor/bin/drush relative to the current working directory, before falling back to the global drush . This avoids mismatches between the globally installed Drush and the Drush pinned by a project's composer.json . Resolution is extracted into a protected resolveProjectDrush() method that subclasses can override.
Added 'NameHandler' for the Name module with tests. @sensespidey , @AlexSkrypnyk ( #322 ) New handler for the Name contrib module covering all six name components ( title , given , middle , family , generational , credentials ). Accepts the "Family, Given" shorthand string, named array keys, and positional numeric indices, with multi-value field support.
Added 'OgStandardReferenceHandler' for Organic Groups. @cheppers , @AlexSkrypnyk ( #321 ) New handler for the Organic Groups og_standard_reference field type. The entity-lookup logic is identical to a plain entity reference, so the handler is a thin subclass of EntityReferenceHandler .
Rewrote 'AddressHandler' with named key support and multi-value handling. @BramDriesen , @daggerhart , @AlexSkrypnyk ( #320 ) Address fields can now be populated using descriptive named keys ( given_name , address_line1 , country_code , etc.) alongside positional numeric arrays, and multi-value fields correctly produce one entry per delta...
Read more
Contributors
sensespidey, AlexSkrypnyk, and 16 other contributors
Assets 2 Loading
Uh oh!
There was an error while loading. Please reload this page .
All reactions
Previous 1 2 3 4 5 6 Next
Previous Next
You can’t perform that action at this time.
-
v2.4.216 Mar 2026Nothing published for this version
-
v2.4.115 Mar 2026Nothing published for this version
-
v2.4.002 Mar 2026Nothing published for this version
-
v2.3.021 Oct 2024Nothing published for this version
-
v2.2.206 Jan 2023Nothing published for this version
-
v2.2.123 Nov 2022Nothing published for this version
-
v2.2.016 May 2022Nothing published for this version
-
v2.1.225 Apr 2022Nothing published for this version
-
v2.1.107 May 2021Nothing published for this version
-
v2.1.001 Jun 2020Nothing published for this version
-
v2.0.027 Sep 2019Nothing published for this version
-
v2.0.0-rc125 Jul 2019 pre-releaseNothing published for this version
-
v2.0.0-alpha618 Dec 2018 pre-releaseNothing published for this version
-
v2.0.0-alpha521 Sep 2018 pre-releaseNothing published for this version
-
v2.0.0-alpha421 Sep 2018 pre-releaseNothing published for this version
-
v2.0.0-alpha321 Jun 2018 pre-releaseNothing published for this version
-
v2.0.0-alpha221 Mar 2018 pre-releaseNothing published for this version
-
v2.0.0-alpha119 Mar 2018 pre-releaseNothing published for this version
-
v1.4.009 Feb 2018Nothing published for this version
-
v1.3.228 Nov 2017Nothing published for this version
-
v1.3.120 Nov 2017Nothing published for this version
-
v1.3.017 Nov 2017Nothing published for this version
-
v1.2.210 Nov 2017Nothing published for this version
-
v1.2.120 Jun 2016Nothing published for this version
-
v1.2.016 Jun 2016Nothing published for this version
-
v1.1.608 Apr 2016Nothing published for this version
-
v1.1.514 Dec 2015Nothing published for this version
-
v1.1.430 Nov 2015Nothing published for this version
-
v1.1.327 Oct 2015Nothing published for this version
-
v1.1.206 Oct 2015Nothing published for this version
-
v1.1.102 Sep 2015Nothing published for this version
-
v1.1.026 Aug 2015Nothing published for this version
-
v1.0.1706 Oct 2015Nothing published for this version
-
v1.0.1625 Jun 2015Nothing published for this version
-
v1.0.1501 Jun 2015Nothing published for this version
-
v1.0.1422 May 2015Nothing published for this version
-
v1.0.1316 May 2015Nothing published for this version
-
v1.0.1231 Mar 2015Nothing published for this version
-
v1.0.1125 Mar 2015Nothing published for this version
-
v1.0.1019 Mar 2015Nothing published for this version
-
v1.0.918 Mar 2015Nothing published for this version
-
v1.0.805 Mar 2015Nothing published for this version
-
v1.0.725 Feb 2015Nothing published for this version
-
v1.0.625 Feb 2015Nothing published for this version
-
v1.0.520 Feb 2015Nothing published for this version
-
v1.0.413 Feb 2015Nothing published for this version
-
v1.0.323 Dec 2014Nothing published for this version
-
v1.0.207 Nov 2014Nothing published for this version
-
v1.0.107 Nov 2014Nothing published for this version