diff --git a/core/modules/serialization/src/Normalizer/NormalizerBase.php b/core/modules/serialization/src/Normalizer/NormalizerBase.php index 46d286d4aaa..88c19698a31 100644 --- a/core/modules/serialization/src/Normalizer/NormalizerBase.php +++ b/core/modules/serialization/src/Normalizer/NormalizerBase.php @@ -38,6 +38,7 @@ public function supportsNormalization($data, ?string $format = NULL, array $cont /** * Implements \Symfony\Component\Serializer\Normalizer\DenormalizerInterface::supportsDenormalization() + * Checks whether the given data type can be denormalized. * * This class doesn't implement DenormalizerInterface, but most of its child * classes do. Therefore, this method is implemented at this level to reduce @@ -51,11 +52,21 @@ public function supportsDenormalization($data, string $type, ?string $format = N $supported = array_keys($this->getSupportedTypes($format)); - $subclass_check = function ($name) use ($type) { - return (class_exists($name) || interface_exists($name)) && is_subclass_of($type, $name, TRUE); - }; + if ( + // Object types should be checked. + is_object($type) || + // There's also possible to pass a fully qualified class name (FQCN) + // as an argument. We'll perceive a string as an object type if it + // contains backslashes, which are indicators of the PHP namespace, + // or if the "class_exists()" explicitly returns "TRUE". + (is_string($type) && (strpos($type, '\\') !== FALSE || class_exists($type))) + ) { + return (bool) array_filter($supported, function ($name) use ($type) { + return (class_exists($name) || interface_exists($name)) && is_a($type, $name, TRUE); + }); + } - return in_array($type, $supported) || array_filter($supported, $subclass_check); + return in_array($type, $supported, TRUE); } /** diff --git a/core/modules/serialization/tests/src/Unit/Normalizer/NormalizerBaseTest.php b/core/modules/serialization/tests/src/Unit/Normalizer/NormalizerBaseTest.php index 683c9a5f19c..e8fa84803ad 100644 --- a/core/modules/serialization/tests/src/Unit/Normalizer/NormalizerBaseTest.php +++ b/core/modules/serialization/tests/src/Unit/Normalizer/NormalizerBaseTest.php @@ -58,6 +58,63 @@ public static function providerTestSupportsNormalization() { ]; } + /** + * Tests the supportsDenormalization method. + * + * @param bool $expected_return + * The expected boolean return value from supportDenormalization. + * @param string|int|float $type + * An interface, class or value to check for denormalization support. + * @param string|string[] $format + * (optional) An interface, class or value the denormalization is supported + * for. + * + * @dataProvider providerTestSupportsDenormalization + */ + public function testSupportsDenormalization($expected_return, $type, $format = NULL) { + /** @var \Drupal\Tests\serialization\Unit\Normalizer\TestNormalizerBase $normalizer_base */ + $normalizer_base = $this->getMockForAbstractClass(TestNormalizerBase::class); + + if (isset($format)) { + $normalizer_base->setSupportedInterfaceOrClass($format); + } + + $this->assertSame($expected_return, $normalizer_base->supportsDenormalization(NULL, $type)); + } + + /** + * Data provider for testSupportsDenormalization. + * + * @return array[] + * An array of provider data for testSupportsDenormalization. + */ + public function providerTestSupportsDenormalization() { + $formats = [ + 1.10, + '123', + '1.13', + 'Ns\Of\MissingClass', + 'stdClass', + NormalizerBase::class, + ]; + + return [ + [FALSE, '1.10', $formats], + [TRUE, 1.10, $formats], + [FALSE, 123, $formats], + [TRUE, '123', $formats], + [FALSE, 1.13, $formats], + [TRUE, '1.13', $formats], + // A non-existent class must fail. + [FALSE, 'Ns\Of\MissingClass', $formats], + // Check the same class. + [TRUE, \stdClass::class, $formats], + // Check the instance. + [TRUE, new \stdClass(), $formats], + // Check the subclass. + [TRUE, TestNormalizerBase::class, $formats], + ]; + } } /**