diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index b6d5c63dd9..7dba368299 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -2323,7 +2323,11 @@ function install_config_import_batch() {
   // Match up the site UUIDs, the install_base_system install task will have
   // installed the system module and created a new UUID.
   $system_site = $sync->read('system.site');
-  \Drupal::configFactory()->getEditable('system.site')->set('uuid', $system_site['uuid'])->save();
+  // @todo the conditional below is required to get tests pass on PHP 7.4;
+  // find a better solution.
+  if ($system_site !== FALSE) {
+    \Drupal::configFactory()->getEditable('system.site')->set('uuid', $system_site['uuid'])->save();
+  }
 
   // Create the storage comparer and the config importer.
   $storage_comparer = new StorageComparer($sync, \Drupal::service('config.storage'));
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index d2d54979b7..58474d1a39 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -815,11 +815,26 @@ function template_preprocess_image(&$variables) {
     $variables['attributes']['srcset'] = implode(', ', $srcset);
   }
 
+  // Ensure that attributes can be accessed as an array, for PHP 7.4
+  // compatibility.
+  // @todo look for a permanent fix in #3090145, "Ensure that mixing array and
+  // Attribute objects in theme rendering is managed properly".
+  // @see https://www.drupal.org/project/drupal/issues/3090145
+  if ($variables['attributes'] instanceof Attribute) {
+    $attributes_array = $variables['attributes']->toArray();
+  }
+  elseif (is_array($variables['attributes'])) {
+    $attributes_array = $variables['attributes'];
+  }
+  else {
+    $attributes_array = [];
+  }
+
   foreach (['width', 'height', 'alt', 'title', 'sizes'] as $key) {
     if (isset($variables[$key])) {
-      // If the property has already been defined in the attributes,
-      // do not override, including NULL.
-      if (array_key_exists($key, $variables['attributes'])) {
+      // If the property has already been defined in the attributes, do not
+      // override, including NULL.
+      if (array_key_exists($key, $attributes_array)) {
         continue;
       }
       $variables['attributes'][$key] = $variables[$key];
@@ -1128,7 +1143,7 @@ function template_preprocess_item_list(&$variables) {
             // \Drupal\Core\Render\Element::children(), which cannot be used
             // here, since it triggers an error on string values.
             foreach ($child as $child_key => $child_value) {
-              if ($child_key[0] !== '#') {
+              if (is_int($child_key) || $child_key[0] !== '#') {
                 $child['#items'][$child_key] = $child_value;
                 unset($child[$child_key]);
               }
diff --git a/core/lib/Drupal/Component/Annotation/Doctrine/DocParser.php b/core/lib/Drupal/Component/Annotation/Doctrine/DocParser.php
index 1d8cf2fbc3..5083ed7568 100644
--- a/core/lib/Drupal/Component/Annotation/Doctrine/DocParser.php
+++ b/core/lib/Drupal/Component/Annotation/Doctrine/DocParser.php
@@ -966,13 +966,16 @@ private function Identifier()
 
         $className = $this->lexer->token['value'];
 
-        while ($this->lexer->lookahead['position'] === ($this->lexer->token['position'] + strlen($this->lexer->token['value']))
+        $position = $this->lexer->lookahead['position'] ?? NULL;
+        while ($position === ($this->lexer->token['position'] + strlen($this->lexer->token['value']))
                 && $this->lexer->isNextToken(DocLexer::T_NAMESPACE_SEPARATOR)) {
 
             $this->match(DocLexer::T_NAMESPACE_SEPARATOR);
             $this->matchAny(self::$classIdentifiers);
 
             $className .= '\\' . $this->lexer->token['value'];
+
+            $position = $this->lexer->lookahead['position'] ?? NULL;
         }
 
         return $className;
@@ -987,7 +990,7 @@ private function Value()
     {
         $peek = $this->lexer->glimpse();
 
-        if (DocLexer::T_EQUALS === $peek['type']) {
+        if ($peek !== NULL && DocLexer::T_EQUALS === $peek['type']) {
             return $this->FieldAssignment();
         }
 
diff --git a/core/lib/Drupal/Core/Asset/CssCollectionRenderer.php b/core/lib/Drupal/Core/Asset/CssCollectionRenderer.php
index 24ea64ea7a..5a0b844c61 100644
--- a/core/lib/Drupal/Core/Asset/CssCollectionRenderer.php
+++ b/core/lib/Drupal/Core/Asset/CssCollectionRenderer.php
@@ -49,6 +49,11 @@ public function render(array $css_assets) {
 
     foreach ($css_assets as $css_asset) {
       $element = $link_element_defaults;
+      // @todo the emptiness check below is needed to get tests pass on
+      // PHP 7.4. Needs better solution.
+      if (empty($css_asset)) {
+        throw new \Exception('Invalid CSS asset type.');
+      }
       $element['#attributes']['media'] = $css_asset['media'];
       $element['#browsers'] = $css_asset['browsers'];
 
diff --git a/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php b/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php
index 9c99bdc53d..86c22c1837 100644
--- a/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php
+++ b/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php
@@ -117,7 +117,7 @@ public function buildByExtension($extension) {
           $library['version'] = \Drupal::VERSION;
         }
         // Remove 'v' prefix from external library versions.
-        elseif ($library['version'][0] === 'v') {
+        elseif (is_string($library['version']) && $library['version'][0] === 'v') {
           $library['version'] = substr($library['version'], 1);
         }
       }
diff --git a/core/lib/Drupal/Core/Config/StorageComparer.php b/core/lib/Drupal/Core/Config/StorageComparer.php
index e362493ac3..03a904bea7 100644
--- a/core/lib/Drupal/Core/Config/StorageComparer.php
+++ b/core/lib/Drupal/Core/Config/StorageComparer.php
@@ -388,7 +388,7 @@ public function hasChanges() {
   public function validateSiteUuid() {
     $source = $this->sourceStorage->read('system.site');
     $target = $this->targetStorage->read('system.site');
-    return $source['uuid'] === $target['uuid'];
+    return $source && $target && $source['uuid'] === $target['uuid'];
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Database/Install/Tasks.php b/core/lib/Drupal/Core/Database/Install/Tasks.php
index 7dfe5d0288..d63a05e8da 100644
--- a/core/lib/Drupal/Core/Database/Install/Tasks.php
+++ b/core/lib/Drupal/Core/Database/Install/Tasks.php
@@ -243,7 +243,9 @@ public function getFormOptions(array $database) {
     ];
 
     global $install_state;
-    $profile = $install_state['parameters']['profile'];
+    // @todo the use of the coalesce operator below is needed to get
+    // migrate_drupal_ui tests pass on PHP 7.4.
+    $profile = $install_state['parameters']['profile'] ?? NULL;
     $db_prefix = ($profile == 'standard') ? 'drupal_' : $profile . '_';
     $form['advanced_options']['prefix'] = [
       '#type' => 'textfield',
diff --git a/core/lib/Drupal/Core/Field/FieldTypePluginManager.php b/core/lib/Drupal/Core/Field/FieldTypePluginManager.php
index 565fa4e6f7..841b42da7b 100644
--- a/core/lib/Drupal/Core/Field/FieldTypePluginManager.php
+++ b/core/lib/Drupal/Core/Field/FieldTypePluginManager.php
@@ -168,7 +168,10 @@ public function getPreconfiguredOptions($field_type) {
    */
   public function getPluginClass($type) {
     $plugin_definition = $this->getDefinition($type, FALSE);
-    return $plugin_definition['class'];
+    // @todo below is needed to get FieldMissingTypeTest tests pass on PHP
+    // 7.4. Better solution needed.
+    $plugin_class = ($plugin_definition && isset($plugin_definition['class'])) ? $plugin_definition['class'] : NULL;
+    return $plugin_class;
   }
 
 }
diff --git a/core/lib/Drupal/Core/FileTransfer/FileTransfer.php b/core/lib/Drupal/Core/FileTransfer/FileTransfer.php
index 9e4148824e..c1b3055b44 100644
--- a/core/lib/Drupal/Core/FileTransfer/FileTransfer.php
+++ b/core/lib/Drupal/Core/FileTransfer/FileTransfer.php
@@ -363,7 +363,7 @@ public function findChroot() {
     $parts = explode('/', $path);
     $chroot = '';
     while (count($parts)) {
-      $check = implode($parts, '/');
+      $check = implode('/', $parts);
       if ($this->isFile($check . '/' . \Drupal::service('file_system')->basename(__FILE__))) {
         // Remove the trailing slash.
         return substr($chroot, 0, -1);
diff --git a/core/lib/Drupal/Core/Form/FormSubmitter.php b/core/lib/Drupal/Core/Form/FormSubmitter.php
index 8591edaa2b..4a6a34c807 100644
--- a/core/lib/Drupal/Core/Form/FormSubmitter.php
+++ b/core/lib/Drupal/Core/Form/FormSubmitter.php
@@ -61,7 +61,9 @@ public function doSubmitForm(&$form, FormStateInterface &$form_state) {
 
       $batch['progressive'] = !$form_state->isProgrammed();
       $response = batch_process();
-      if ($batch['progressive']) {
+      // If the batch has been completed and _batch_finished() called then
+      // $batch will be NULL.
+      if ($batch && $batch['progressive']) {
         return $response;
       }
 
diff --git a/core/lib/Drupal/Core/Render/Element.php b/core/lib/Drupal/Core/Render/Element.php
index 682f236163..f27304cde8 100644
--- a/core/lib/Drupal/Core/Render/Element.php
+++ b/core/lib/Drupal/Core/Render/Element.php
@@ -78,7 +78,7 @@ public static function children(array &$elements, $sort = FALSE) {
     $i = 0;
     $sortable = FALSE;
     foreach ($elements as $key => $value) {
-      if ($key === '' || $key[0] !== '#') {
+      if (is_int($key) || $key === '' || $key[0] !== '#') {
         if (is_array($value)) {
           if (isset($value['#weight'])) {
             $weight = $value['#weight'];
diff --git a/core/lib/Drupal/Core/Security/RequestSanitizer.php b/core/lib/Drupal/Core/Security/RequestSanitizer.php
index e1626ed383..a778e4be4e 100644
--- a/core/lib/Drupal/Core/Security/RequestSanitizer.php
+++ b/core/lib/Drupal/Core/Security/RequestSanitizer.php
@@ -153,7 +153,7 @@ protected static function checkDestination($destination, array $whitelist) {
   protected static function stripDangerousValues($input, array $whitelist, array &$sanitized_keys) {
     if (is_array($input)) {
       foreach ($input as $key => $value) {
-        if ($key !== '' && $key[0] === '#' && !in_array($key, $whitelist, TRUE)) {
+        if ($key !== '' && ((string) $key)[0] === '#' && !in_array($key, $whitelist, TRUE)) {
           unset($input[$key]);
           $sanitized_keys[] = $key;
         }
diff --git a/core/modules/color/color.module b/core/modules/color/color.module
index 6b70035e74..3ae1030203 100644
--- a/core/modules/color/color.module
+++ b/core/modules/color/color.module
@@ -764,8 +764,9 @@ function _color_blend($img, $hex1, $hex2, $alpha) {
  * Converts a hex color into an RGB triplet.
  */
 function _color_unpack($hex, $normalize = FALSE) {
-  if (strlen($hex) == 4) {
-    $hex = $hex[1] . $hex[1] . $hex[2] . $hex[2] . $hex[3] . $hex[3];
+  $hex = substr($hex, 1);
+  if (strlen($hex) == 3) {
+    $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
   }
   $c = hexdec($hex);
   for ($i = 16; $i >= 0; $i -= 8) {
diff --git a/core/modules/field/src/Plugin/migrate/process/d7/FieldInstanceSettings.php b/core/modules/field/src/Plugin/migrate/process/d7/FieldInstanceSettings.php
index a695760dfc..ed7a026b79 100644
--- a/core/modules/field/src/Plugin/migrate/process/d7/FieldInstanceSettings.php
+++ b/core/modules/field/src/Plugin/migrate/process/d7/FieldInstanceSettings.php
@@ -21,7 +21,6 @@ public function transform($value, MigrateExecutableInterface $migrate_executable
     $widget_type = $widget_settings['type'];
 
     $field_data = unserialize($field_definition['data']);
-    $field_settings = $field_data['settings'];
 
     // Get taxonomy term reference handler settings from allowed values.
     if ($row->getSourceProperty('type') == 'taxonomy_term_reference') {
@@ -38,6 +37,7 @@ public function transform($value, MigrateExecutableInterface $migrate_executable
 
     // Get entityreference handler settings from source field configuration.
     if ($row->getSourceProperty('type') == "entityreference") {
+      $field_settings = $field_data['settings'];
       $instance_settings['handler'] = 'default:' . $field_settings['target_type'];
       // Transform the sort settings to D8 structure.
       $sort = [
diff --git a/core/modules/field/src/Plugin/migrate/source/d7/FieldInstance.php b/core/modules/field/src/Plugin/migrate/source/d7/FieldInstance.php
index 74340884d2..5417dabc4a 100644
--- a/core/modules/field/src/Plugin/migrate/source/d7/FieldInstance.php
+++ b/core/modules/field/src/Plugin/migrate/source/d7/FieldInstance.php
@@ -181,7 +181,9 @@ public function prepareRow(Row $row) {
     }
 
     $field_data = unserialize($row->getSourceProperty('field_data'));
-    $row->setSourceProperty('field_settings', $field_data['settings']);
+    // @todo the use of the coalesce operator below is needed to get
+    // migrate tests pass on PHP 7.4.
+    $row->setSourceProperty('field_settings', $field_data['settings'] ?? NULL);
 
     return parent::prepareRow($row);
   }
diff --git a/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php b/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php
index 48dbb81b47..b4d51cf617 100644
--- a/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php
+++ b/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php
@@ -82,7 +82,10 @@ public function normalize($field_item, $format = NULL, array $context = []) {
 
     // Normalize the target entity.
     $embedded = $this->serializer->normalize($target_entity, $format, $context);
-    $link = $embedded['_links']['self'];
+    // @todo $embedded can be NULL - this breaks PHP 7.4 testing. Using the null
+    //   coalescence operator fixes
+    //   Drupal\Tests\node\Functional\Hal\NodeHalJsonAnonTest for example.
+    $link = $embedded['_links']['self'] ?? NULL;
     // If the field is translatable, add the langcode to the link relation
     // object. This does not indicate the language of the target entity.
     if ($langcode) {
diff --git a/core/modules/inline_form_errors/tests/src/Unit/FormErrorHandlerTest.php b/core/modules/inline_form_errors/tests/src/Unit/FormErrorHandlerTest.php
index d0937aed51..5aff4094ed 100644
--- a/core/modules/inline_form_errors/tests/src/Unit/FormErrorHandlerTest.php
+++ b/core/modules/inline_form_errors/tests/src/Unit/FormErrorHandlerTest.php
@@ -138,7 +138,7 @@ public function testErrorMessagesInline() {
         foreach ($render_array[1]['#items'] as $item) {
           $links[] = htmlspecialchars($item['#title']);
         }
-        return $render_array[0]['#markup'] . '<ul-comma-list-mock><li-mock>' . implode($links, '</li-mock><li-mock>') . '</li-mock></ul-comma-list-mock>';
+        return $render_array[0]['#markup'] . '<ul-comma-list-mock><li-mock>' . implode('</li-mock><li-mock>', $links) . '</li-mock></ul-comma-list-mock>';
       }));
 
     $form_state = new FormState();
diff --git a/core/modules/language/src/LanguageServiceProvider.php b/core/modules/language/src/LanguageServiceProvider.php
index a7e934aab6..b463bb6ef6 100644
--- a/core/modules/language/src/LanguageServiceProvider.php
+++ b/core/modules/language/src/LanguageServiceProvider.php
@@ -87,9 +87,11 @@ protected function isMultilingual() {
   protected function getDefaultLanguageValues() {
     $config_storage = BootstrapConfigStorageFactory::get();
     $system = $config_storage->read('system.site');
-    $default_language = $config_storage->read(static::CONFIG_PREFIX . $system['default_langcode']);
-    if (is_array($default_language)) {
-      return $default_language;
+    if ($system) {
+      $default_language = $config_storage->read(static::CONFIG_PREFIX . $system['default_langcode']);
+      if (is_array($default_language)) {
+        return $default_language;
+      }
     }
     return FALSE;
   }
diff --git a/core/modules/layout_builder/tests/src/Kernel/FieldBlockTest.php b/core/modules/layout_builder/tests/src/Kernel/FieldBlockTest.php
index ebf19c53a4..d041e39689 100644
--- a/core/modules/layout_builder/tests/src/Kernel/FieldBlockTest.php
+++ b/core/modules/layout_builder/tests/src/Kernel/FieldBlockTest.php
@@ -281,13 +281,22 @@ public function providerTestBuild() {
       new ReturnPromise([[]]),
       '',
     ];
-    $data['exception'] = [
-      new ThrowPromise(new \Exception('The exception message')),
+    return $data;
+  }
+
+  /**
+   * @covers ::build
+   */
+  public function testBuildException() {
+    // In PHP 7.4 ReflectionClass cannot be serialized so this cannot be part of
+    // providerTestBuild().
+    $promise = new ThrowPromise(new \Exception('The exception message'));
+    $this->testBuild(
+      $promise,
       '',
       'The field "%field" failed to render with the error of "%error".',
-      ['%field' => 'the_field_name', '%error' => 'The exception message'],
-    ];
-    return $data;
+      ['%field' => 'the_field_name', '%error' => 'The exception message']
+    );
   }
 
   /**
diff --git a/core/modules/link/src/Plugin/Field/FieldType/LinkItem.php b/core/modules/link/src/Plugin/Field/FieldType/LinkItem.php
index 373c3f03ad..90585a66a0 100644
--- a/core/modules/link/src/Plugin/Field/FieldType/LinkItem.php
+++ b/core/modules/link/src/Plugin/Field/FieldType/LinkItem.php
@@ -185,12 +185,12 @@ public function setValue($values, $notify = TRUE) {
       $values += [
         'options' => [],
       ];
-    }
-    // Unserialize the values, this is deprecated as the storage takes care of
-    // this, options must not be passed as a string anymore.
-    if (is_string($values['options'])) {
-      @trigger_error('Support for passing options as a serialized string is deprecated in 8.7.0 and will be removed before Drupal 9.0.0. Pass them as an array instead. See https://www.drupal.org/node/2961643.', E_USER_DEPRECATED);
-      $values['options'] = unserialize($values['options'], ['allowed_classes' => FALSE]);
+      // Unserialize the values, this is deprecated as the storage takes care of
+      // this, options must not be passed as a string anymore.
+      if (is_string($values['options'])) {
+        @trigger_error('Support for passing options as a serialized string is deprecated in 8.7.0 and will be removed before Drupal 9.0.0. Pass them as an array instead. See https://www.drupal.org/node/2961643.', E_USER_DEPRECATED);
+        $values['options'] = unserialize($values['options'], ['allowed_classes' => FALSE]);
+      }
     }
     parent::setValue($values, $notify);
   }
diff --git a/core/modules/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc
index 37c4522092..7bfce52add 100644
--- a/core/modules/locale/locale.bulk.inc
+++ b/core/modules/locale/locale.bulk.inc
@@ -385,12 +385,16 @@ function locale_translate_batch_finished($success, array $results) {
       // file), simply do nothing.
       if ($results && isset($results['stats'])) {
         foreach ($results['stats'] as $filepath => $report) {
-          $additions += $report['additions'];
-          $updates += $report['updates'];
-          $deletes += $report['deletes'];
-          $skips += $report['skips'];
-          if ($report['skips'] > 0) {
-            $skipped_files[] = $filepath;
+          // @todo is_array check below is needed to get locale tests pass on
+          // PHP 7.4.
+          if (is_array($report)) {
+            $additions += $report['additions'];
+            $updates += $report['updates'];
+            $deletes += $report['deletes'];
+            $skips += $report['skips'];
+            if ($report['skips'] > 0) {
+              $skipped_files[] = $filepath;
+            }
           }
         }
       }
diff --git a/core/modules/migrate/tests/src/Kernel/MigrateConfigRollbackTest.php b/core/modules/migrate/tests/src/Kernel/MigrateConfigRollbackTest.php
index 27aa45bcce..72abb0f287 100644
--- a/core/modules/migrate/tests/src/Kernel/MigrateConfigRollbackTest.php
+++ b/core/modules/migrate/tests/src/Kernel/MigrateConfigRollbackTest.php
@@ -78,8 +78,7 @@ public function testConfigRollback() {
     $this->assertSame('Some site', $config->get('name'));
     $this->assertSame('Awesome slogan', $config->get('slogan'));
     // Confirm the map row is deleted.
-    $map_row = $config_id_map->getRowBySource(['id' => $variable[0]['id']]);
-    $this->assertNull($map_row['destid1']);
+    $this->assertFalse($config_id_map->getRowBySource(['id' => $variable[0]['id']]));
 
     // We use system configuration to demonstrate importing and rolling back
     // configuration translations.
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index bb4580a587..4899ddce67 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -648,7 +648,7 @@ function template_preprocess_node(&$variables) {
     unset($variables['elements']['uid']);
   }
 
-  if (!$skip_custom_preprocessing || !$node->getFieldDefinition('title')->isDisplayConfigurable('view')) {
+  if (isset($variables['elements']['title']) && (!$skip_custom_preprocessing || !$node->getFieldDefinition('title')->isDisplayConfigurable('view'))) {
     $variables['label'] = $variables['elements']['title'];
     unset($variables['elements']['title']);
   }
diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module
index 6dced6adab..a562a427d1 100644
--- a/core/modules/rdf/rdf.module
+++ b/core/modules/rdf/rdf.module
@@ -10,6 +10,13 @@
 use Drupal\Core\Template\Attribute;
 use Drupal\rdf\Entity\RdfMapping;
 
+/**
+ * Override for PHP7.4 compatibility.
+ */
+if (class_exists('\Drupal\rdf\CommonDataConverter')) {
+  class_alias('Drupal\rdf\easyrdf\EasyRdf_ParsedUri', 'EasyRdf_ParsedUri');
+}
+
 /**
  * Implements hook_help().
  */
@@ -495,7 +502,7 @@ function rdf_preprocess_comment(&$variables) {
   }
   // Adds RDFa markup for the date of the comment.
   $created_mapping = $mapping->getPreparedFieldMapping('created');
-  if (!empty($created_mapping)) {
+  if (!empty($created_mapping) && isset($comment->rdf_data)) {
     // The comment date is precomputed as part of the rdf_data so that it can be
     // cached as part of the entity.
     $date_attributes = $comment->rdf_data['date'];
diff --git a/core/modules/rdf/src/Entity/RdfMapping.php b/core/modules/rdf/src/Entity/RdfMapping.php
index e33f24a59a..c28f72cd79 100644
--- a/core/modules/rdf/src/Entity/RdfMapping.php
+++ b/core/modules/rdf/src/Entity/RdfMapping.php
@@ -148,7 +148,9 @@ public function calculateDependencies() {
     $entity_type = \Drupal::entityTypeManager()->getDefinition($this->targetEntityType);
     $this->addDependency('module', $entity_type->getProvider());
     $bundle_config_dependency = $entity_type->getBundleConfigDependency($this->bundle);
-    $this->addDependency($bundle_config_dependency['type'], $bundle_config_dependency['name']);
+    if (!empty($bundle_config_dependency)) {
+      $this->addDependency($bundle_config_dependency['type'], $bundle_config_dependency['name']);
+    }
 
     return $this;
   }
diff --git a/core/modules/rdf/src/easyrdf/EasyRdf_ParsedUri.php b/core/modules/rdf/src/easyrdf/EasyRdf_ParsedUri.php
new file mode 100644
index 0000000000..ee6a289731
--- /dev/null
+++ b/core/modules/rdf/src/easyrdf/EasyRdf_ParsedUri.php
@@ -0,0 +1,341 @@
+<?php
+// @codingStandardsIgnoreFile
+/**
+ * EasyRdf
+ *
+ * LICENSE
+ *
+ * Copyright (c) 2009-2013 Nicholas J Humfrey.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author 'Nicholas J Humfrey" may be used to endorse or
+ *    promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @package    EasyRdf
+ * @copyright  Copyright (c) 2009-2013 Nicholas J Humfrey
+ * @license    http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace Drupal\rdf\easyrdf;
+
+/**
+ * A RFC3986 compliant URI parser
+ *
+ * @package    EasyRdf
+ * @copyright  Copyright (c) 2009-2013 Nicholas J Humfrey
+ * @license    http://www.opensource.org/licenses/bsd-license.php
+ * @link       http://www.ietf.org/rfc/rfc3986.txt
+ */
+class EasyRdf_ParsedUri
+{
+    // For all URIs:
+    private $scheme = null;
+    private $fragment = null;
+
+    // For hierarchical URIs:
+    private $authority = null;
+    private $path = null;
+    private $query = null;
+
+    const URI_REGEX = "|^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?|";
+
+    /** Constructor for creating a new parsed URI
+     *
+     * The $uri parameter can either be a string or an
+     * associative array with the following keys:
+     * scheme, authority, path, query, fragment
+     *
+     * @param  mixed $uri  The URI as a string or an array
+     * @return object EasyRdf_ParsedUri
+     */
+    public function __construct($uri = null)
+    {
+        if (is_string($uri)) {
+            if (preg_match(self::URI_REGEX, $uri, $matches)) {
+                if (!empty($matches[1])) {
+                    $this->scheme = isset($matches[2]) ? $matches[2] : '';
+                }
+                if (!empty($matches[3])) {
+                    $this->authority = isset($matches[4]) ? $matches[4] : '';
+                }
+                $this->path = isset($matches[5]) ? $matches[5] : '';
+                if (!empty($matches[6])) {
+                    $this->query = isset($matches[7]) ? $matches[7] : '';
+                }
+                if (!empty($matches[8])) {
+                    $this->fragment = isset($matches[9]) ? $matches[9] : '';
+                }
+            }
+        } elseif (is_array($uri)) {
+            $this->scheme = isset($uri['scheme']) ? $uri['scheme'] : null;
+            $this->authority = isset($uri['authority']) ? $uri['authority'] : null;
+            $this->path = isset($uri['path']) ? $uri['path'] : null;
+            $this->query = isset($uri['query']) ? $uri['query'] : null;
+            $this->fragment = isset($uri['fragment']) ? $uri['fragment'] : null;
+        }
+    }
+
+
+    /** Returns true if this is an absolute (complete) URI
+     * @return boolean
+     */
+    public function isAbsolute()
+    {
+        return $this->scheme !== null;
+    }
+
+    /** Returns true if this is an relative (partial) URI
+     * @return boolean
+     */
+    public function isRelative()
+    {
+        return $this->scheme === null;
+    }
+
+    /** Returns the scheme of the URI (e.g. http)
+     * @return string
+     */
+    public function getScheme()
+    {
+        return $this->scheme;
+    }
+
+    /** Sets the scheme of the URI (e.g. http)
+     * @param string $scheme The new value for the scheme of the URI
+     */
+    public function setScheme($scheme)
+    {
+        $this->scheme = $scheme;
+    }
+
+    /** Returns the authority of the URI (e.g. www.example.com:8080)
+     * @return string
+     */
+    public function getAuthority()
+    {
+        return $this->authority;
+    }
+
+    /** Sets the authority of the URI (e.g. www.example.com:8080)
+     * @param string $authority The new value for the authority component of the URI
+     */
+    public function setAuthority($authority)
+    {
+        $this->authority = $authority;
+    }
+
+    /** Returns the path of the URI (e.g. /foo/bar)
+     * @return string
+     */
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    /** Set the path of the URI (e.g. /foo/bar)
+     * @param string $path The new value for the path component of the URI
+     */
+    public function setPath($path)
+    {
+        $this->path = $path;
+    }
+
+    /** Returns the query string part of the URI (e.g. foo=bar)
+     * @return string
+     */
+    public function getQuery()
+    {
+        return $this->query;
+    }
+
+    /** Set the query string of the URI (e.g. foo=bar)
+     * @param string $query The new value for the query string component of the URI
+     */
+    public function setQuery($query)
+    {
+        $this->query = $query;
+    }
+
+    /** Returns the fragment part of the URI (i.e. after the #)
+     * @return string
+     */
+    public function getFragment()
+    {
+        return $this->fragment;
+    }
+
+    /** Set the fragment of the URI (i.e. after the #)
+     * @param string $fragment The new value for the fragment component of the URI
+     */
+    public function setFragment($fragment)
+    {
+        $this->fragment = $fragment;
+    }
+
+
+    /**
+     * Normalises the path of this URI if it has one. Normalising a path means
+     * that any unnecessary '.' and '..' segments are removed. For example, the
+     * URI http://example.com/a/b/../c/./d would be normalised to
+     * http://example.com/a/c/d
+     *
+     * @return object EasyRdf_ParsedUri
+     */
+    public function normalise()
+    {
+        if (empty($this->path)) {
+            return $this;
+        }
+
+        // Remove ./ from the start
+        if (substr($this->path, 0, 2) == './') {
+            // Remove both characters
+            $this->path = substr($this->path, 2);
+        }
+
+        // Remove /. from the end
+        if (substr($this->path, -2) == '/.') {
+            // Remove only the last dot, not the slash!
+            $this->path = substr($this->path, 0, -1);
+        }
+
+        if (substr($this->path, -3) == '/..') {
+            $this->path .= '/';
+        }
+
+        // Split the path into its segments
+        $segments = explode('/', $this->path);
+        $newSegments = array();
+
+        // Remove all unnecessary '.' and '..' segments
+        foreach ($segments as $segment) {
+            if ($segment == '..') {
+                // Remove the previous part of the path
+                $count = count($newSegments);
+                if ($count > 0 && $newSegments[$count-1]) {
+                    array_pop($newSegments);
+                }
+            } elseif ($segment == '.') {
+                // Ignore
+                continue;
+            } else {
+                array_push($newSegments, $segment);
+            }
+        }
+
+        // Construct the new normalised path
+        $this->path = implode('/', $newSegments);
+
+        // Allow easy chaining of methods
+        return $this;
+    }
+
+    /**
+     * Resolves a relative URI using this URI as the base URI.
+     */
+    public function resolve($relUri)
+    {
+        // If it is a string, then convert it to a parsed object
+        if (is_string($relUri)) {
+            $relUri = new EasyRdf_ParsedUri($relUri);
+        }
+
+        // This code is based on the pseudocode in section 5.2.2 of RFC3986
+        $target = new EasyRdf_ParsedUri();
+        if ($relUri->scheme) {
+            $target->scheme = $relUri->scheme;
+            $target->authority = $relUri->authority;
+            $target->path = $relUri->path;
+            $target->query = $relUri->query;
+        } else {
+            if ($relUri->authority) {
+                $target->authority = $relUri->authority;
+                $target->path = $relUri->path;
+                $target->query = $relUri->query;
+            } else {
+                if (empty($relUri->path)) {
+                    $target->path = $this->path;
+                    if ($relUri->query) {
+                        $target->query = $relUri->query;
+                    } else {
+                        $target->query = $this->query;
+                    }
+                } else {
+                    if (substr($relUri->path, 0, 1) == '/') {
+                        $target->path = $relUri->path;
+                    } else {
+                        $path = $this->path;
+                        $lastSlash = strrpos($path, '/');
+                        if ($lastSlash !== false) {
+                            $path = substr($path, 0, $lastSlash + 1);
+                        } else {
+                            $path = '/';
+                        }
+
+                        $target->path .= $path . $relUri->path;
+                    }
+                    $target->query = $relUri->query;
+                }
+                $target->authority = $this->authority;
+            }
+            $target->scheme = $this->scheme;
+        }
+
+        $target->fragment = $relUri->fragment;
+
+        $target->normalise();
+
+        return $target;
+    }
+
+    /** Convert the parsed URI back into a string
+     *
+     * @return string The URI as a string
+     */
+    public function toString()
+    {
+        $str = '';
+        if ($this->scheme !== null) {
+            $str .= $this->scheme . ':';
+        }
+        if ($this->authority !== null) {
+            $str .= '//' . $this->authority;
+        }
+        $str .= $this->path;
+        if ($this->query !== null) {
+            $str .= '?' . $this->query;
+        }
+        if ($this->fragment !== null) {
+            $str .= '#' . $this->fragment;
+        }
+        return $str;
+    }
+
+    /** Magic method to convert the URI, when casted, back to a string
+     *
+     * @return string The URI as a string
+     */
+    public function __toString()
+    {
+        return $this->toString();
+    }
+}
diff --git a/core/modules/rest/src/RequestHandler.php b/core/modules/rest/src/RequestHandler.php
index 7d2299a19d..4617f77891 100644
--- a/core/modules/rest/src/RequestHandler.php
+++ b/core/modules/rest/src/RequestHandler.php
@@ -330,7 +330,7 @@ protected function getLegacyParameters(RouteMatchInterface $route_match, $unseri
     $parameters = [];
     // Filter out all internal parameters starting with "_".
     foreach ($route_parameters as $key => $parameter) {
-      if ($key{0} !== '_') {
+      if ((string) $key[0] !== '_') {
         $parameters[] = $parameter;
       }
     }
diff --git a/core/modules/search/src/SearchQuery.php b/core/modules/search/src/SearchQuery.php
index 9e47cf1a42..6e342ca202 100644
--- a/core/modules/search/src/SearchQuery.php
+++ b/core/modules/search/src/SearchQuery.php
@@ -246,7 +246,7 @@ protected function parseSearchExpression() {
 
       // Strip off phrase quotes.
       $phrase = FALSE;
-      if ($match[2]{0} == '"') {
+      if ($match[2][0] == '"') {
         $match[2] = substr($match[2], 1, -1);
         $phrase = TRUE;
         $this->simple = FALSE;
diff --git a/core/modules/taxonomy/src/Plugin/migrate/source/d6/TermLocalizedTranslation.php b/core/modules/taxonomy/src/Plugin/migrate/source/d6/TermLocalizedTranslation.php
index 844fb858b5..e8f15f6b9e 100644
--- a/core/modules/taxonomy/src/Plugin/migrate/source/d6/TermLocalizedTranslation.php
+++ b/core/modules/taxonomy/src/Plugin/migrate/source/d6/TermLocalizedTranslation.php
@@ -70,7 +70,9 @@ public function prepareRow(Row $row) {
     $query->condition('lt.language', $language);
     $query->addField('lt', 'translation');
     $results = $query->execute()->fetchAssoc();
-    $row->setSourceProperty($other_property . '_translated', $results['translation']);
+    // @todo the use of the coalesce operator below is needed to get
+    // migrate tests pass on PHP 7.4.
+    $row->setSourceProperty($other_property . '_translated', $results['translation'] ?? NULL);
 
     parent::prepareRow($row);
   }
diff --git a/core/modules/taxonomy/tests/src/Kernel/Views/ArgumentValidatorTermTest.php b/core/modules/taxonomy/tests/src/Kernel/Views/ArgumentValidatorTermTest.php
index aa4269afc5..75e38fe35a 100644
--- a/core/modules/taxonomy/tests/src/Kernel/Views/ArgumentValidatorTermTest.php
+++ b/core/modules/taxonomy/tests/src/Kernel/Views/ArgumentValidatorTermTest.php
@@ -61,6 +61,10 @@ public function testArgumentValidatorTerm() {
     $view->initHandlers();
 
     // Test the single validator for term IDs.
+    // @todo $view->argument['tid']->validator should be defined, but it's not
+    // here and that's leading to test failures in PHP 7.4. Figure out a more
+    // robust solution rather than creating the stdClass object in the test.
+    $view->argument['tid']->validator = new \stdClass();
     $view->argument['tid']->validator->options['type'] = 'tid';
 
     // Pass in a single valid term.
diff --git a/core/modules/views/src/Plugin/views/display/PathPluginBase.php b/core/modules/views/src/Plugin/views/display/PathPluginBase.php
index e29d8e932b..b73a42519a 100644
--- a/core/modules/views/src/Plugin/views/display/PathPluginBase.php
+++ b/core/modules/views/src/Plugin/views/display/PathPluginBase.php
@@ -101,7 +101,7 @@ public function getPath() {
   protected function isDefaultTabPath() {
     $menu = $this->getOption('menu');
     $tab_options = $this->getOption('tab_options');
-    return $menu['type'] == 'default tab' && !empty($tab_options['type']) && $tab_options['type'] != 'none';
+    return $menu && $menu['type'] == 'default tab' && !empty($tab_options['type']) && $tab_options['type'] != 'none';
   }
 
   /**
diff --git a/core/modules/views/src/Plugin/views/filter/Date.php b/core/modules/views/src/Plugin/views/filter/Date.php
index b7d1821bfd..baa27b70f4 100644
--- a/core/modules/views/src/Plugin/views/filter/Date.php
+++ b/core/modules/views/src/Plugin/views/filter/Date.php
@@ -182,7 +182,10 @@ protected function opBetween($field) {
   }
 
   protected function opSimple($field) {
-    $value = intval(strtotime($this->value['value'], 0));
+    // @todo $this->value['value'] should always be defined, but it's not
+    // and that's leading to test failures in PHP 7.4. Figure out a more
+    // robust solution rather than coalescing to NULL.
+    $value = intval(strtotime($this->value['value'] ?? NULL, 0));
     if (!empty($this->value['type']) && $this->value['type'] == 'offset') {
       // Keep sign.
       $value = '***CURRENT_TIME***' . sprintf('%+d', $value);
diff --git a/core/modules/views/src/Plugin/views/filter/NumericFilter.php b/core/modules/views/src/Plugin/views/filter/NumericFilter.php
index 05d0cc8979..4f9680ec57 100644
--- a/core/modules/views/src/Plugin/views/filter/NumericFilter.php
+++ b/core/modules/views/src/Plugin/views/filter/NumericFilter.php
@@ -354,7 +354,10 @@ protected function opBetween($field) {
   }
 
   protected function opSimple($field) {
-    $this->query->addWhere($this->options['group'], $field, $this->value['value'], $this->operator);
+    // @todo $this->value['value'] should always be defined, but it's not
+    // and that's leading to test failures in PHP 7.4. Figure out a more
+    // robust solution rather than coalescing to NULL.
+    $this->query->addWhere($this->options['group'], $field, $this->value['value'] ?? NULL, $this->operator);
   }
 
   protected function opEmpty($field) {
diff --git a/core/modules/views/src/Plugin/views/filter/StringFilter.php b/core/modules/views/src/Plugin/views/filter/StringFilter.php
index 47adcdbcdd..229eb90733 100644
--- a/core/modules/views/src/Plugin/views/filter/StringFilter.php
+++ b/core/modules/views/src/Plugin/views/filter/StringFilter.php
@@ -341,7 +341,7 @@ protected function opContainsWord($field) {
     foreach ($matches as $match) {
       $phrase = FALSE;
       // Strip off phrase quotes
-      if ($match[2]{0} == '"') {
+      if ($match[2][0] == '"') {
         $match[2] = substr($match[2], 1, -1);
         $phrase = TRUE;
       }
diff --git a/core/modules/views/src/Plugin/views/join/JoinPluginBase.php b/core/modules/views/src/Plugin/views/join/JoinPluginBase.php
index a0bbedf731..1788ec63a7 100644
--- a/core/modules/views/src/Plugin/views/join/JoinPluginBase.php
+++ b/core/modules/views/src/Plugin/views/join/JoinPluginBase.php
@@ -263,7 +263,11 @@ public function buildJoin($select_query, $table, $view_query) {
 
     if ($this->leftTable) {
       $left_table = $view_query->getTableInfo($this->leftTable);
-      $left_field = "$left_table[alias].$this->leftField";
+      // @todo $left_table['alias'] should always be defined, but it's not
+      // and that's leading to test failures in PHP 7.4. Figure out a more
+      // robust solution rather than coalescing to NULL.
+      $left_table_alias = $left_table['alias'] ?? NULL;
+      $left_field = "$left_table_alias.$this->leftField";
     }
     else {
       // This can be used if left_field is a formula or something. It should be used only *very* rarely.
@@ -381,7 +385,11 @@ protected function buildExtra($info, &$arguments, $table, SelectInterface $selec
       // Allow the value to be set either with the 'value' element or
       // with 'left_field'.
       if (isset($info['left_field'])) {
-        $placeholder_sql = "$left[alias].$info[left_field]";
+        // @todo $left['alias'] should always be defined, but it's not
+        // and that's leading to test failures in PHP 7.4. Figure out a more
+        // robust solution rather than coalescing to NULL.
+        $left_alias = $left['alias'] ?? NULL;
+        $placeholder_sql = "$left_alias.$info[left_field]";
       }
       else {
         $arguments[$placeholder] = $info['value'];
diff --git a/core/modules/views/tests/src/Unit/Plugin/field/FieldPluginBaseTest.php b/core/modules/views/tests/src/Unit/Plugin/field/FieldPluginBaseTest.php
index 623436e6ba..b0b6850deb 100644
--- a/core/modules/views/tests/src/Unit/Plugin/field/FieldPluginBaseTest.php
+++ b/core/modules/views/tests/src/Unit/Plugin/field/FieldPluginBaseTest.php
@@ -136,6 +136,9 @@ protected function setUp() {
     $this->executable = $this->getMockBuilder('Drupal\views\ViewExecutable')
       ->disableOriginalConstructor()
       ->getMock();
+    $this->executable->style_plugin = $this->getMockBuilder('Drupal\views\Plugin\views\style\StylePluginBase')
+      ->disableOriginalConstructor()
+      ->getMock();
     $this->display = $this->getMockBuilder('Drupal\views\Plugin\views\display\DisplayPluginBase')
       ->disableOriginalConstructor()
       ->getMock();
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/FieldSqlStorageTest.php b/core/tests/Drupal/KernelTests/Core/Entity/FieldSqlStorageTest.php
index 2a8faf90e3..28766fd829 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/FieldSqlStorageTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/FieldSqlStorageTest.php
@@ -148,7 +148,7 @@ public function testFieldLoad() {
           $this->assertEqual($entity->{$this->fieldName}[$delta]->value, $value);
         }
         else {
-          $this->assertFalse(array_key_exists($delta, $entity->{$this->fieldName}));
+          $this->assertArrayNotHasKey($delta, $entity->{$this->fieldName});
         }
       }
     }
@@ -160,7 +160,7 @@ public function testFieldLoad() {
         $this->assertEqual($entity->{$this->fieldName}[$delta]->value, $value);
       }
       else {
-        $this->assertFalse(array_key_exists($delta, $entity->{$this->fieldName}));
+        $this->assertArrayNotHasKey($delta, $entity->{$this->fieldName});
       }
     }
 
@@ -171,7 +171,7 @@ public function testFieldLoad() {
     $connection->insert($this->table)->fields($columns)->values($values)->execute();
     $connection->insert($this->revisionTable)->fields($columns)->values($values)->execute();
     $entity = $storage->load($entity->id());
-    $this->assertFalse(array_key_exists($unavailable_langcode, $entity->{$this->fieldName}));
+    $this->assertArrayNotHasKey($unavailable_langcode, $entity->{$this->fieldName});
   }
 
   /**
diff --git a/core/tests/Drupal/KernelTests/KernelTestBase.php b/core/tests/Drupal/KernelTests/KernelTestBase.php
index 957ed79582..427052b9a2 100644
--- a/core/tests/Drupal/KernelTests/KernelTestBase.php
+++ b/core/tests/Drupal/KernelTests/KernelTestBase.php
@@ -610,9 +610,9 @@ protected function tearDown() {
 
     // Remove all prefixed tables.
     $original_connection_info = Database::getConnectionInfo('simpletest_original_default');
-    $original_prefix = $original_connection_info['default']['prefix']['default'];
+    $original_prefix = $original_connection_info['default']['prefix']['default'] ?? NULL;
     $test_connection_info = Database::getConnectionInfo('default');
-    $test_prefix = $test_connection_info['default']['prefix']['default'];
+    $test_prefix = $test_connection_info['default']['prefix']['default'] ?? NULL;
     if ($original_prefix != $test_prefix) {
       $tables = Database::getConnection()->schema()->findTables('%');
       foreach ($tables as $table) {
diff --git a/core/tests/Drupal/Tests/BrowserTestBase.php b/core/tests/Drupal/Tests/BrowserTestBase.php
index 96657a93e7..d768bfcc3f 100644
--- a/core/tests/Drupal/Tests/BrowserTestBase.php
+++ b/core/tests/Drupal/Tests/BrowserTestBase.php
@@ -350,11 +350,15 @@ protected function getResponseLogHandler() {
               $html_output = 'Called from ' . $caller['function'] . ' line ' . $caller['line'];
               $html_output .= '<hr />' . $request->getMethod() . ' request to: ' . $request->getUri();
 
+              // Get the response body as a string. Any errors are silenced as
+              // tests should not fail if there is a problem. On PHP 7.4
+              // \Drupal\Tests\migrate\Functional\process\DownloadFunctionalTest
+              // fails without the usage of a silence operator.
+              $body = @(string) $response->getBody();
               // On redirect responses (status code starting with '3') we need
               // to remove the meta tag that would do a browser refresh. We
               // don't want to redirect developers away when they look at the
               // debug output file in their browser.
-              $body = $response->getBody();
               $status_code = (string) $response->getStatusCode();
               if ($status_code[0] === '3') {
                 $body = preg_replace('#<meta http-equiv="refresh" content=.+/>#', '', $body, 1);
diff --git a/core/tests/Drupal/Tests/Core/DependencyInjection/YamlFileLoaderTest.php b/core/tests/Drupal/Tests/Core/DependencyInjection/YamlFileLoaderTest.php
index 4093b6a2eb..26ffcd4b84 100644
--- a/core/tests/Drupal/Tests/Core/DependencyInjection/YamlFileLoaderTest.php
+++ b/core/tests/Drupal/Tests/Core/DependencyInjection/YamlFileLoaderTest.php
@@ -34,7 +34,11 @@ class: \Drupal\Core\ExampleClass
 YAML;
 
     vfsStream::setup('drupal', NULL, [
-      'modules/example/example.yml' => $yml,
+      'modules' => [
+        'example' => [
+          'example.yml' => $yml,
+        ],
+      ],
     ]);
 
     $builder = new ContainerBuilder();
