Problem/Motivation

While create a new enhancer, if I use attributes to define it i got an error on both backend panel and json response. Defining it with annotations give no problems.

Steps to reproduce

Create a new enhancer

#[ResourceFieldEnhancer(
  id: 'custom_enhancer',
  label: new TranslatableMarkup('Custom enhancer'),
  description: new TranslatableMarkup('Dummy for test only.'),
)]
class CustomEnhancer extends ResourceFieldEnhancerBase implements ContainerFactoryPluginInterface {}

then go to /admin/config/services/jsonapi/resource_types/node--page/edit
got this error: Uncaught PHP Exception TypeError: "Cannot access offset of type array in isset or empty" at /var/www/html/web/core/lib/Drupal/Core/Extension/ModuleHandler.php line 267

If defining like

/**
 * @ResourceFieldEnhancer(
 *   id = "custom_enhancer",
 *   label = @Translation("Custom enhancer"),
 *   description = @Translation("Dummy for test only."),
 * )
 */
class CustomEnhancer extends ResourceFieldEnhancerBase implements ContainerFactoryPluginInterface {}

then no error happen

Proposed resolution

Change Drupal\jsonapi_extras\Plugin\ResourceFieldEnhancerManager.php
adding a check in the alterDefinitions() foreach

foreach ($definition_dependencies as $dependency) {
  if (!is_string($dependency)) {
    continue;
  }
  // If dependency is not enabled removed from list of definitions.
  if (!$this->moduleHandler->moduleExists($dependency)) {
    unset($definitions[$definition_key]);
    continue;
  }
}
CommentFileSizeAuthor
#3 jsonapi_extras-3584099-3.patch1.4 KBbala_28
Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

g.rocchini created an issue. See original summary.

bala_28’s picture

StatusFileSize
new1.4 KB

Root cause
In ResourceFieldEnhancerManager::alterDefinitions(), the code iterates over $definition_info['dependencies'] and passes each value directly to $this->moduleHandler->moduleExists(). However, plugin definitions using the #[ResourceFieldEnhancer] attribute declare dependencies as a flat array (e.g., dependencies: ['datetime']), but during plugin discovery the dependencies value can be structured as a keyed array (e.g., ['module' => ['datetime']]). When the value is a nested array, it gets passed as-is to moduleExists(), which calls isset($this->moduleList[$module]) — triggering a TypeError in PHP 8.1+ because an array cannot be used as an array offset in isset().

Fix
Flatten the dependencies array before iterating, so both flat (['datetime']) and nested (['module' => ['datetime']]) formats are handled correctly.

PS: The existing partial fix (the is_string check) was incorrect — it silently skipped array dependencies without validating them, so plugins with unmet nested dependencies would still be loaded.

nvandijk’s picture

Status: Active » Reviewed & tested by the community

I have tested https://www.drupal.org/files/issues/2026-04-22/jsonapi_extras-3584099-3.... against one of my projects. The routes no longer give a 500 error, but instead give the desired output.

bbrala’s picture

I think i need to check if this change will break ealier versions.

spfaffly’s picture

Ran into this issue this morning - loaded up the patch from #3 and the page loads as intended.

Thanks!

timfletcher’s picture

I tried Patch #3 and the error still appears. I verified the patch definitely applied and the changes appeared. I'm running jsonapi_extras 8.x-3.28.

I'm trying to access the 'Override' page for several entities, but found the error also fires when attempting to edit existing overrides. I'm also not defining any enhancers or plugins.

The full error is:

TypeError: Cannot access offset of type array in isset or empty in Drupal\Core\Extension\ModuleHandler->moduleExists() (line 267 of core/lib/Drupal/Core/Extension/ModuleHandler.php).
Drupal\jsonapi_extras\Plugin\ResourceFieldEnhancerManager->alterDefinitions() (Line: 344)
Drupal\Core\Plugin\DefaultPluginManager->findDefinitions() (Line: 216)
Drupal\Core\Plugin\DefaultPluginManager->getDefinitions() (Line: 412)
Drupal\jsonapi_extras\Form\JsonapiResourceConfigForm->buildOverridesField() (Line: 309)
Drupal\jsonapi_extras\Form\JsonapiResourceConfigForm->buildOverridesForm() (Line: 138)
Drupal\jsonapi_extras\Form\JsonapiResourceConfigForm->form() (Line: 108)
Drupal\Core\Entity\EntityForm->buildForm()
call_user_func_array() (Line: 559)
Drupal\Core\Form\FormBuilder->retrieveForm() (Line: 299)
Drupal\Core\Form\FormBuilder->buildForm() (Line: 73)
Drupal\Core\Controller\FormController->getContentResult()
call_user_func_array() (Line: 123)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->{closure:Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber::wrapControllerExecutionInRenderContext():121}() (Line: 638)
Drupal\Core\Render\Renderer::{closure:Drupal\Core\Render\Renderer::executeInRenderContext():638}()
Fiber->resume() (Line: 653)
Drupal\Core\Render\Renderer->executeInRenderContext() (Line: 121)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext() (Line: 97)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->{closure:Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber::onController():96}() (Line: 183)
Symfony\Component\HttpKernel\HttpKernel->handleRaw() (Line: 76)
Symfony\Component\HttpKernel\HttpKernel->handle() (Line: 53)
Drupal\Core\StackMiddleware\Session->handle() (Line: 30)
Drupal\Core\StackMiddleware\KernelPreHandle->handle() (Line: 28)
Drupal\Core\StackMiddleware\ContentLength->handle() (Line: 118)
Drupal\page_cache\StackMiddleware\PageCache->pass() (Line: 92)
Drupal\page_cache\StackMiddleware\PageCache->handle() (Line: 263)
Drupal\shield\ShieldMiddleware->bypass() (Line: 154)
Drupal\shield\ShieldMiddleware->handle() (Line: 53)
Asm89\Stack\Cors->handle() (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle() (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle() (Line: 61)
Drupal\Core\StackMiddleware\AjaxPageState->handle() (Line: 54)
Drupal\Core\StackMiddleware\StackedHttpKernel->handle() (Line: 753)
Drupal\Core\DrupalKernel->handle() (Line: 34)
Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner->run() (Line: 32)
require('/var/www/html/vendor/autoload_runtime.php') (Line: 22)
require_once('/var/www/html/web/autoload_runtime.php') (Line: 13)
bbrala’s picture

Did some testing and this fix will drop dependencies, which is not nice. The fix will be to actually move things around. I will keep a BC path for the enhancers but will emit a deprecation.

bbrala’s picture

Version: 8.x-3.28 » 8.x-3.x-dev
Status: Reviewed & tested by the community » Fixed

Fixed, thanks all

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

  • bbrala committed b84acb29 on 8.x-3.x authored by g.rocchini
    fix: #3584099 Error with custom enhancer defined with attributes
    
    By: g....

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.