Loading core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItemBase.php +24 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ namespace Drupal\Core\Field\Plugin\Field\FieldType; use Drupal\Core\Field\FieldItemBase; use Drupal\Core\Utility\FiberResumeType; /** * Base class for field items referencing other entities. Loading @@ -13,4 +14,27 @@ */ abstract class EntityReferenceItemBase extends FieldItemBase implements EntityReferenceItemInterface { /** * {@inheritdoc} */ public function __get($property_name) { // This is a workaround for a PHP bug where a fiber suspend from within a // __get() call can incorrectly trigger PHP's recursion guarding. // See https://github.com/php/php-src/issues/14983 if ($property_name === 'entity' && \Fiber::getCurrent()) { $fiber = new \Fiber(fn() => parent::__get($property_name)); $fiber->start(); while (!$fiber->isTerminated()) { if ($fiber->isSuspended()) { $resume_type = $fiber->resume(); if (!$fiber->isTerminated() && $resume_type !== FiberResumeType::Immediate) { usleep(500); } } } return $fiber->getReturn(); } return parent::__get($property_name); } } core/tests/Drupal/KernelTests/Core/Entity/EntityReferenceFieldTest.php +50 −0 Original line number Diff line number Diff line Loading @@ -87,6 +87,56 @@ protected function setUp(): void { } /** * Tests fiber suspension within EntityReferenceFieldItemList::__get(). * * @see https://github.com/php/php-src/issues/14983 */ public function testEntityReferenceListFiberSuspension(): void { $referenced_entity = $this->container->get('entity_type.manager') ->getStorage($this->referencedEntityType) ->create(['type' => $this->bundle]); $referenced_entity->save(); $storage = $this->container->get('entity_type.manager')->getStorage($this->entityType); $entity = $storage->create(['type' => $this->bundle]); $entity->{$this->fieldName}->target_id = $referenced_entity->id(); $entity->save(); $entity = $storage->load($entity->id()); $fiber = new \Fiber(fn() => $entity->{$this->fieldName}->entity); $fiber->start(); $referenced_entity = $entity->{$this->fieldName}->entity; $this->assertIsObject($referenced_entity); } /** * Tests fiber suspension within EntityReferenceItemBase::__get(). */ public function testEntityReferenceItemFiberSuspension(): void { $referenced_entity = $this->container->get('entity_type.manager') ->getStorage($this->referencedEntityType) ->create(['type' => $this->bundle]); $referenced_entity->save(); $storage = $this->container->get('entity_type.manager')->getStorage($this->entityType); $entity = $storage->create(['type' => $this->bundle]); $entity->{$this->fieldName}->target_id = $referenced_entity->id(); $entity->save(); /** @var \Drupal\Core\Entity\FieldableEntityInterface $entity */ $entity = $storage->load($entity->id()); $field_item = $entity->get($this->fieldName)->first(); $fiber = new \Fiber(fn() => $field_item->entity); $fiber->start(); $referenced_entity = $field_item->entity; $this->assertIsObject($referenced_entity); } /** * Tests reference field validation. */ Loading Loading
core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItemBase.php +24 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ namespace Drupal\Core\Field\Plugin\Field\FieldType; use Drupal\Core\Field\FieldItemBase; use Drupal\Core\Utility\FiberResumeType; /** * Base class for field items referencing other entities. Loading @@ -13,4 +14,27 @@ */ abstract class EntityReferenceItemBase extends FieldItemBase implements EntityReferenceItemInterface { /** * {@inheritdoc} */ public function __get($property_name) { // This is a workaround for a PHP bug where a fiber suspend from within a // __get() call can incorrectly trigger PHP's recursion guarding. // See https://github.com/php/php-src/issues/14983 if ($property_name === 'entity' && \Fiber::getCurrent()) { $fiber = new \Fiber(fn() => parent::__get($property_name)); $fiber->start(); while (!$fiber->isTerminated()) { if ($fiber->isSuspended()) { $resume_type = $fiber->resume(); if (!$fiber->isTerminated() && $resume_type !== FiberResumeType::Immediate) { usleep(500); } } } return $fiber->getReturn(); } return parent::__get($property_name); } }
core/tests/Drupal/KernelTests/Core/Entity/EntityReferenceFieldTest.php +50 −0 Original line number Diff line number Diff line Loading @@ -87,6 +87,56 @@ protected function setUp(): void { } /** * Tests fiber suspension within EntityReferenceFieldItemList::__get(). * * @see https://github.com/php/php-src/issues/14983 */ public function testEntityReferenceListFiberSuspension(): void { $referenced_entity = $this->container->get('entity_type.manager') ->getStorage($this->referencedEntityType) ->create(['type' => $this->bundle]); $referenced_entity->save(); $storage = $this->container->get('entity_type.manager')->getStorage($this->entityType); $entity = $storage->create(['type' => $this->bundle]); $entity->{$this->fieldName}->target_id = $referenced_entity->id(); $entity->save(); $entity = $storage->load($entity->id()); $fiber = new \Fiber(fn() => $entity->{$this->fieldName}->entity); $fiber->start(); $referenced_entity = $entity->{$this->fieldName}->entity; $this->assertIsObject($referenced_entity); } /** * Tests fiber suspension within EntityReferenceItemBase::__get(). */ public function testEntityReferenceItemFiberSuspension(): void { $referenced_entity = $this->container->get('entity_type.manager') ->getStorage($this->referencedEntityType) ->create(['type' => $this->bundle]); $referenced_entity->save(); $storage = $this->container->get('entity_type.manager')->getStorage($this->entityType); $entity = $storage->create(['type' => $this->bundle]); $entity->{$this->fieldName}->target_id = $referenced_entity->id(); $entity->save(); /** @var \Drupal\Core\Entity\FieldableEntityInterface $entity */ $entity = $storage->load($entity->id()); $field_item = $entity->get($this->fieldName)->first(); $fiber = new \Fiber(fn() => $field_item->entity); $fiber->start(); $referenced_entity = $field_item->entity; $this->assertIsObject($referenced_entity); } /** * Tests reference field validation. */ Loading