NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
Packagist · #1790 most downloaded on Packagist
A collection of reusable Drupal drivers
Last release 2 months 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
Removed the dead drush stub from 'TestDrushDriver' and documented the legacy Drush removal. @AlexSkrypnyk
Full Changelog: v3.3.0...v3.3.1
v3.3.1 Latest
Latest
Compare
One column per quarter.
Added a dedicated field value-shape classifier gating the default handler, and deprecated pass-through handlers. @AlexSkrypnyk ( #386 ) When a field t…
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.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
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
AlexSkrypnyk and ptmkenny
Assets 2 Loading
There was an error while loading. Please reload this page .
All reactions
Fixed CI failures from Composer advisory blocking, 'phpunit-bridge' deprecations, and a stale path-repo version. @AlexSkrypnyk
Full Changelog: v3.1.0...v3.2.0
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
AlexSkrypnyk
Assets 2 Loading
There was an error while loading. Please reload this page .
All reactions
[ #374 ] Added 'ColorFieldTypeHandler' for the 'color_field' contrib module. @AlexSkrypnyk
Full Changelog: v3.0.0...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
@AlexSkrypnyk
AlexSkrypnyk
Assets 2 Loading
There was an error while loading. Please reload this page .
All reactions
Fixed documentation inaccuracies surfaced during v3.0.0 release review. @AlexSkrypnyk
Full Changelog: v3.0.0-rc2...v3.0.0
See UPGRADING.md for the full v2 to v3 migration guide.
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 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.
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.$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.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.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.\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.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.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.\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.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.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.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.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.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.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.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.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, Drupal\Driver\Fields\Drupal8\*, or the old Cores/ and Fields/ namespaces must update to Drupal\Driver\Core\Core and Drupal\Driver\Core\Field\*. A future Drupal version that diverges can ship a Drupal\Driver\Core{N}\Core subclass and the setCoreFromVersion() chain will pick it up automatically.TypeError at the boundary; audit any consumer code that calls into Core, DrupalDriver, DrushDriver, or any handler.DrupalDriver::detectMajorVersion() throws a BootstrapException when 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 the 2.x branch).\stdClass now declares EntityStubInterface, the field-handler boundary accepts the typed stub directly, and the previous (object) [...] constructs are replaced with new EntityStub($entity_type, $bundle, [...]). Callers can read the saved id and entity through typed accessors instead of guessing property names.drupalextension test suite against this branch so regressions that only surface in the primary downstream consumer are caught before release.TextHandler and TextLongHandler, both multi-column types that immediately tripped the new loud-failure policy in DefaultHandler. This adds both handlers, restores the 2.x reuse-by-URI / reuse-by-basename behaviour for FileHandler (and ImageHandler, which now extends FileHandler), and adds FieldTypeCoverageKernelTest that walks every registered field type and fails if a new core type is missing a handler entry.FieldClassifier exposes 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, and DefaultHandler throws for any multi-column type without a registered handler. EntityReferenceRevisionsHandler is added for paragraphs and other revision-tracked references; FieldCapabilityInterface and the fieldExists / fieldIsBase predicates are removed.getCore(), setCore(), and getDrupalVersion() are now declared on DrupalDriverInterface, and $core / $version on DrupalDriver were narrowed from public to protected. Consumers that hand-rolled a DrupalDriverInterface implementation must add the three accessors, and any callers reading the properties directly must switch to the public methods.blockPlace(), blockDelete(), blockContentCreate(), and blockContentDelete() are now first-class capability methods on DrupalDriver and Core. Placement covers the block config entity (id auto-generated when omitted, plugin/theme/region wiring), and the content-block methods cover the block_content content entity via the generic field-expansion pipeline. Block-type creation is intentionally out of scope.$core->registerFieldHandler($field_type, $class) (last registration wins), and $driver->setCore(CoreInterface $core) lets them inject any CoreInterface implementation. Internally Core::__construct() calls registerDefaultFieldHandlers() which scans the sibling Field/ directory and registers every concrete *Handler class, so future Drupal versions add handlers additively by dropping a file into a Core{N}\Field\ directory.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 references go through EntityReferenceHandler (already covered by the existing kernel test), so the orphaned class and its two tests are gone.entityCreate() and entityDelete() now throw InvalidArgumentException('Unknown entity type "X".') when given a bad type instead of leaking PluginNotFoundException. termCreate() validates the vocabulary_machine_name property, the existence of the named vocabulary, and the existence of any named parent term up front, with messages that quote the offending value.on_label / off_label from the field config and matches them case-insensitively, then falls back to filter_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 a RuntimeException listing all accepted forms.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 $id is given, it doubles as the human-readable label.drupal/core-dev requirement was replaced with the three transitive deps the test suite actually needs (vfsstream, prophecy-phpunit, phpstan/extension-installer), and a project-owned tests/bootstrap.php replaces 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.entity_reference_revisions use - ['target_id' => 'alice', 'display' => 1]) previously crashed with Calling 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.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 like commerce_product.variations resolve their labels instead of reaching storage raw. entityCreate() and entityDelete() now resolve the id key per entity type (uid, nid, tid, etc.) instead of hardcoding id.src/Drupal/Driver/: guard clauses replace nested conditionals, blank lines bracket if / foreach / for blocks, generic $return variables are renamed to semantic names, and step-by-step comments that just restated the code are removed. One incidental correctness fix in DrushDriver::drush() replaces an !== NULL check on a typed non-nullable property with isset() so root-mode no longer trips on uninitialised access.DrupalDriver and DrushDriver are now grouped alphabetically by capability (Authentication, Cache, Config, Content, ...) so navigating between the two drivers is consistent. $bootstrapped, $drupalRoot, $uri, $random, and $arguments were widened from private to protected so subclasses can access them. No logic, signatures, or behaviour changed.DrushDriverInterface no longer extends ContentCapabilityInterface or FieldCapabilityInterface, and the eight methods that required the companion endpoint module (entityCreate, entityDelete, nodeCreate, nodeDelete, termCreate, termDelete, fieldExists, fieldIsBase) are removed from DrushDriver. Consumers that need those operations should use DrupalDriver; see UPGRADING.md.Capability\*CapabilityInterface interfaces replace the monolithic DriverInterface / BaseDriver model; 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 *Mail family flipped to mail*); BaseDriver, AbstractCore, AuthenticationDriverInterface, CoreAuthenticationInterface, and DrupalCoreInterface are removed.FieldHandlerKernelTestBase with attachField() and assertFieldRoundTripViaDriver() 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, and Core::userCreate() re-validated the Drupal site even when bootstrap had already done so.shivammathur/setup-php@v2, with actions/cache for the Composer cache. The docker-compose.yml and the wodby/drupal-php image dependency are gone, removing the PHP_VERSION / DOCKER_USER_ID env scaffolding the container needed.conf.py, eight .rst files, snippets) and the stale ReadTheDocs link are removed; the architecture/extending guide that lived in doc/extending.rst is condensed into a new "Extending for a new Drupal version" section in CONTRIBUTING.md.src/Drupal/Driver/Cores/Drupal8.php becomes src/Drupal/Driver/Core/Core.php and the seventeen handlers under Fields/Drupal8/ move to Core/Field/. DrupalDriver::setCoreFromVersion() and Core::getFieldHandler() walk a Core{N}\ chain so a future Drupal version that diverges can ship an override file alongside the default without copy-pasting the whole implementation. No Core{N}/ directory exists today; everything resolves to the default Core\Core and Core\Field\* classes.palantirnet/drupal-rector was dropped in favour of rector/rector ^2.0 directly). Constructor property promotion was reverted in three classes where it mangled existing docblocks, and that rule is now skipped going forward.^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-lowest and normal dependency resolution. master is the active 3.x branch; the maintenance line lives on the new 2.x branch.draft-release-notes.yml workflow that already publishes notes via GitHub Releases. The "Release notes" link in README.md now points at the Releases page.composer normalize --dry-run were moved into a dedicated job that runs once on PHP 8.4 / Drupal 11 instead of repeating across every matrix combination. The tests job retains its full PHP/Drupal matrix and coverage logic.Full Changelog: v2.5.1...v3.0.0-alpha1
Full Changelog: v3.0.0-alpha1...v3.0.0-rc1
smart_date contrib module. @AlexSkrypnyk (#371)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.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
Full Changelog : v3.0.0...v3.1.0
@AlexSkrypnyk
AlexSkrypnyk
Assets 2 Loading
There was an error while loading. Please reload this page .
All reactions
Added 'SmartdateHandler' for the smart_date contrib module. @AlexSkrypnyk ( #371 ) Closes a v3.0 regression: writing to a smartdate field (six storage
smart_date contrib module. @AlexSkrypnyk (#371)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.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
Full Changelog : v3.0.0-rc2...v3.0.0
@AlexSkrypnyk
[ #366 ] Made 'NameHandler' honour the field's 'components' setting. @AlexSkrypnyk
Full Changelog: v3.0.0-alpha1...v3.0.0-rc1
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
AlexSkrypnyk
Assets 2 Loading
There was an error while loading. Please reload this page .
All reactions
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 tig
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.
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.$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.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.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.\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.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.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.\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.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.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.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.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.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.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.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.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, Drupal\Driver\Fields\Drupal8\*, or the old Cores/ and Fields/ namespaces must update to Drupal\Driver\Core\Core and Drupal\Driver\Core\Field\*. A future Drupal version that diverges can ship a Drupal\Driver\Core{N}\Core subclass and the setCoreFromVersion() chain will pick it up automatically.TypeError at the boundary; audit any consumer code that calls into Core, DrupalDriver, DrushDriver, or any handler.DrupalDriver::detectMajorVersion() throws a BootstrapException when 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 the 2.x branch).\stdClass now declares EntityStubInterface, the field-handler boundary accepts the typed stub directly, and the previous (object) [...] constructs are replaced with new EntityStub($entity_type, $bundle, [...]). Callers can read the saved id and entity through typed accessors instead of guessing property names.drupalextension test suite against this branch so regressions that only surface in the primary downstream consumer are caught before release.TextHandler and TextLongHandler, both multi-column types that immediately tripped the new loud-failure policy in DefaultHandler. This adds both handlers, restores the 2.x reuse-by-URI / reuse-by-basename behaviour for FileHandler (and ImageHandler, which now extends FileHandler), and adds FieldTypeCoverageKernelTest that walks every registered field type and fails if a new core type is missing a handler entry.FieldClassifier exposes 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, and DefaultHandler throws for any multi-column type without a registered handler. EntityReferenceRevisionsHandler is added for paragraphs and other revision-tracked references; FieldCapabilityInterface and the fieldExists / fieldIsBase predicates are removed.getCore(), setCore(), and getDrupalVersion() are now declared on DrupalDriverInterface, and $core / $version on DrupalDriver were narrowed from public to protected. Consumers that hand-rolled a DrupalDriverInterface implementation must add the three accessors, and any callers reading the properties directly must switch to the public methods.blockPlace(), blockDelete(), blockContentCreate(), and blockContentDelete() are now first-class capability methods on DrupalDriver and Core. Placement covers the block config entity (id auto-generated when omitted, plugin/theme/region wiring), and the content-block methods cover the block_content content entity via the generic field-expansion pipeline. Block-type creation is intentionally out of scope.$core->registerFieldHandler($field_type, $class) (last registration wins), and $driver->setCore(CoreInterface $core) lets them inject any CoreInterface implementation. Internally Core::__construct() calls registerDefaultFieldHandlers() which scans the sibling Field/ directory and registers every concrete *Handler class, so future Drupal versions add handlers additively by dropping a file into a Core{N}\Field\ directory.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 references go through EntityReferenceHandler (already covered by the existing kernel test), so the orphaned class and its two tests are gone.entityCreate() and entityDelete() now throw InvalidArgumentException('Unknown entity type "X".') when given a bad type instead of leaking PluginNotFoundException. termCreate() validates the vocabulary_machine_name property, the existence of the named vocabulary, and the existence of any named parent term up front, with messages that quote the offending value.on_label / off_label from the field config and matches them case-insensitively, then falls back to filter_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 a RuntimeException listing all accepted forms.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 $id is given, it doubles as the human-readable label.drupal/core-dev requirement was replaced with the three transitive deps the test suite actually needs (vfsstream, prophecy-phpunit, phpstan/extension-installer), and a project-owned tests/bootstrap.php replaces 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.entity_reference_revisions use - ['target_id' => 'alice', 'display' => 1]) previously crashed with Calling 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.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 like commerce_product.variations resolve their labels instead of reaching storage raw. entityCreate() and entityDelete() now resolve the id key per entity type (uid, nid, tid, etc.) instead of hardcoding id.src/Drupal/Driver/: guard clauses replace nested conditionals, blank lines bracket if / foreach / for blocks, generic $return variables are renamed to semantic names, and step-by-step comments that just restated the code are removed. One incidental correctness fix in DrushDriver::drush() replaces an !== NULL check on a typed non-nullable property with isset() so root-mode no longer trips on uninitialised access.DrupalDriver and DrushDriver are now grouped alphabetically by capability (Authentication, Cache, Config, Content, ...) so navigating between the two drivers is consistent. $bootstrapped, $drupalRoot, $uri, $random, and $arguments were widened from private to protected so subclasses can access them. No logic, signatures, or behaviour changed.DrushDriverInterface no longer extends ContentCapabilityInterface or FieldCapabilityInterface, and the eight methods that required the companion endpoint module (entityCreate, entityDelete, nodeCreate, nodeDelete, termCreate, termDelete, fieldExists, fieldIsBase) are removed from DrushDriver. Consumers that need those operations should use DrupalDriver; see UPGRADING.md.Capability\*CapabilityInterface interfaces replace the monolithic DriverInterface / BaseDriver model; 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 *Mail family flipped to mail*); BaseDriver, AbstractCore, AuthenticationDriverInterface, CoreAuthenticationInterface, and DrupalCoreInterface are removed.FieldHandlerKernelTestBase with attachField() and assertFieldRoundTripViaDriver() 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, and Core::userCreate() re-validated the Drupal site even when bootstrap had already done so.shivammathur/setup-php@v2, with actions/cache for the Composer cache. The docker-compose.yml and the wodby/drupal-php image dependency are gone, removing the PHP_VERSION / DOCKER_USER_ID env scaffolding the container needed.conf.py, eight .rst files, snippets) and the stale ReadTheDocs link are removed; the architecture/extending guide that lived in doc/extending.rst is condensed into a new "Extending for a new Drupal version" section in CONTRIBUTING.md.src/Drupal/Driver/Cores/Drupal8.php becomes src/Drupal/Driver/Core/Core.php and the seventeen handlers under Fields/Drupal8/ move to Core/Field/. DrupalDriver::setCoreFromVersion() and Core::getFieldHandler() walk a Core{N}\ chain so a future Drupal version that diverges can ship an override file alongside the default without copy-pasting the whole implementation. No Core{N}/ directory exists today; everything resolves to the default Core\Core and Core\Field\* classes.palantirnet/drupal-rector was dropped in favour of rector/rector ^2.0 directly). Constructor property promotion was reverted in three classes where it mangled existing docblocks, and that rule is now skipped going forward.^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-lowest and normal dependency resolution. master is the active 3.x branch; the maintenance line lives on the new 2.x branch.draft-release-notes.yml workflow that already publishes notes via GitHub Releases. The "Release notes" link in README.md now points at the Releases page.composer normalize --dry-run were moved into a dedicated job that runs once on PHP 8.4 / Drupal 11 instead of repeating across every matrix combination. The tests job retains its full PHP/Drupal matrix and coverage logic.Full Changelog: v2.5.1...3.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
@AlexSkrypnyk
AlexSkrypnyk
Assets 2 Loading
There was an error while loading. Please reload this page .
All reactions
What's new since v2.5.0 #357 Fixed FileHandler regression introduced in 2.5.0 where referencing an existing managed file by filename failed with Faile
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
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.
[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.
[ #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
There was an error while loading. Please reload this page .
All reactions
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 worki
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.
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.
[#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.
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 instead of a single-item array. Hidden-field filtering now works (the previous associative-vs-sequential key bug is fixed), country_code is recognised explicitly, and unknown named keys raise \RuntimeException instead of being silently dropped.
[#210] Skipped exception when deleting non-existent role. @bkosborne, @AlexSkrypnyk (#319)Drupal8::roleDelete() no longer throws a RuntimeException when 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 calls resetCache() on every entity type's access control handler in addition to the existing drupal_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+ daterange field type, aligned with DatetimeHandler's timezone handling: uses DrupalDateTime, 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 Alex Skrypnyk as maintainer. @AlexSkrypnyk (#316)
Adds alex@drevops.com as a maintainer in composer.json, reflecting active stewardship of the library.
Added 'moduleInstall()' and 'moduleUninstall()' to all drivers. @phenaproxima, @AlexSkrypnyk (#315)
Introduces moduleInstall($module_name) and moduleUninstall($module_name) on DriverInterface and CoreInterface, wired up across DrupalDriver (delegates to the core), DrushDriver (uses pm-enable / pm-uninstall --yes), and BaseDriver (throws UnsupportedDriverActionException). All three cores - Drupal6, Drupal7, and Drupal8 - implement the methods using their native module APIs.
Implemented proper file handling in 'FileHandler'. @azurams, @AlexSkrypnyk (#314)FileHandler previously extended EntityReferenceHandler and tried a name-based entity lookup on raw file paths, which was incorrect for real file fields. It now extends AbstractHandler and implements expand() by reading the file from disk and creates a managed file entity via \Drupal::service('file.repository')->writeData(), writing to the public:// 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; null titles 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 keys title, uri, and options in 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 of batch_get() before calling batch_process(), preventing a fatal error when tests trigger processBatch() without any queued batch operations.
[#200] Fixed date-only field handling and simplified relative date logic. @ericgsmith, @AlexSkrypnyk (#309)DatetimeHandler now inspects the field's datetime_type setting and stores date-only fields using DATE_STORAGE_FORMAT in the site timezone, rather than always applying a UTC-converted datetime format. The relative and explicit branches are unified: the relative: prefix is stripped first, then a single path picks the right storage format by field type, and both cases use DrupalDateTime for proper timezone handling.
[#291] Fixed 'EntityReferenceHandler' crash when entity label key is FALSE. @khiminrm, @AlexSkrypnyk (#308)
Entity types such as commerce_license do not define a label key, which caused EntityReferenceHandler::expand() to build an orConditionGroup with a FALSE field name and crash the entity query. When getKey('label') returns FALSE, the handler now falls back to an ID-only condition.
[#293] Added 'SupportedImageHandler' for supported_image module. @chrisolof, @AlexSkrypnyk (#307)
New handler for the supported_image contrib module's field type that mirrors ImageHandler and adds the module's extra sub-fields: caption_value, caption_format, attribution_value, and attribution_format.
Added 'TimeHandler' for the 'time_field' contrib module. @ericgsmith, @AlexSkrypnyk (#306)
New handler for the time_field contrib module. Numeric values pass through as seconds past midnight; string values like "9:30 AM", "14:15:30", or "midnight" are parsed via strtotime() 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 in EntityReferenceHandler when a non-numeric (string or title) value is used to look up an entity whose ID column is an integer - PostgreSQL rejects the mixed-type OR condition 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
Full Changelog : v2.5.0...v2.5.1
@AlexSkrypnyk
AlexSkrypnyk
Assets 2 Loading
There was an error while loading. Please reload this page .
All reactions
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
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.
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.
[ #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.
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
sensespidey, AlexSkrypnyk, and 16 other contributors
Assets 2 Loading
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.
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →