diff --git a/composer.json b/composer.json
index 1bd76c125e..1004aa2042 100644
--- a/composer.json
+++ b/composer.json
@@ -13,11 +13,11 @@
         "behat/mink": "1.7.x-dev",
         "behat/mink-goutte-driver": "^1.2",
         "behat/mink-selenium2-driver": "1.3.x-dev",
-        "composer/composer": "^1.8",
+        "composer/composer": "^1.8 || dev-master",
         "drupal/coder": "^8.3.2",
         "jcalderonzumba/gastonjs": "^1.0.2",
         "jcalderonzumba/mink-phantomjs-driver": "^0.3.1",
-        "mikey179/vfsstream": "^1.2",
+        "mikey179/vfsstream": "^1.2 || 2.0.x-dev",
         "phpunit/phpunit": "^6.5 || ^7",
         "phpspec/prophecy": "^1.7",
         "symfony/css-selector": "^3.4.0",
diff --git a/core/drupalci.yml b/core/drupalci.yml
index ed02d43c63..3a399d4ba3 100644
--- a/core/drupalci.yml
+++ b/core/drupalci.yml
@@ -15,6 +15,11 @@ build:
         sniff-all-files: false
         halt-on-fail: false
     testing:
+      # Update PHPUnit & friends.
+      container_command:
+        commands:
+          - "sudo -u www-data /usr/local/bin/composer require composer/composer:dev-master mikey179/vfsstream:2.0.x-dev --no-progress"
+          - "sudo -u www-data /usr/local/bin/composer update typo3/phar-stream-wrapper pear/archive_tar --with-dependencies --no-progress"
       # run_tests task is executed several times in order of performance speeds.
       # halt-on-fail can be set on the run_tests tasks in order to fail fast.
       # suppress-deprecations is false in order to be alerted to usages of
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 61019018e8..656afc0fa7 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -2323,7 +2323,9 @@ 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();
+  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..530bbb4d42 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -819,7 +819,7 @@ function template_preprocess_image(&$variables) {
     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 (array_key_exists($key, (array) $variables['attributes'])) {
         continue;
       }
       $variables['attributes'][$key] = $variables[$key];
@@ -1128,7 +1128,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 703ea49a9c..f8a23f2c4e 100644
--- a/core/lib/Drupal/Core/Asset/CssCollectionRenderer.php
+++ b/core/lib/Drupal/Core/Asset/CssCollectionRenderer.php
@@ -49,6 +49,9 @@ public function render(array $css_assets) {
 
     foreach ($css_assets as $css_asset) {
       $element = $link_element_defaults;
+      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 03b458f5e5..8139e0e47a 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 (((string) $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/Controller/ArgumentResolver/RawParameterValueResolver.php b/core/lib/Drupal/Core/Controller/ArgumentResolver/RawParameterValueResolver.php
index 7e2a35a470..35e0854fbf 100644
--- a/core/lib/Drupal/Core/Controller/ArgumentResolver/RawParameterValueResolver.php
+++ b/core/lib/Drupal/Core/Controller/ArgumentResolver/RawParameterValueResolver.php
@@ -15,7 +15,7 @@ final class RawParameterValueResolver implements ArgumentValueResolverInterface
    * {@inheritdoc}
    */
   public function supports(Request $request, ArgumentMetadata $argument) {
-    return !$argument->isVariadic() && $request->attributes->has('_raw_variables') && array_key_exists($argument->getName(), $request->attributes->get('_raw_variables'));
+    return !$argument->isVariadic() && $request->attributes->has('_raw_variables') && $request->attributes->get('_raw_variables')->has($argument->getName());
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Database/Install/Tasks.php b/core/lib/Drupal/Core/Database/Install/Tasks.php
index 7dfe5d0288..528c979e6c 100644
--- a/core/lib/Drupal/Core/Database/Install/Tasks.php
+++ b/core/lib/Drupal/Core/Database/Install/Tasks.php
@@ -243,7 +243,7 @@ public function getFormOptions(array $database) {
     ];
 
     global $install_state;
-    $profile = $install_state['parameters']['profile'];
+    $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..40817d28b5 100644
--- a/core/lib/Drupal/Core/Field/FieldTypePluginManager.php
+++ b/core/lib/Drupal/Core/Field/FieldTypePluginManager.php
@@ -168,7 +168,7 @@ public function getPreconfiguredOptions($field_type) {
    */
   public function getPluginClass($type) {
     $plugin_definition = $this->getDefinition($type, FALSE);
-    return $plugin_definition['class'];
+    return $plugin_definition['class'] ?? NULL;
   }
 
 }
diff --git a/core/lib/Drupal/Core/Field/WidgetBase.php b/core/lib/Drupal/Core/Field/WidgetBase.php
index c3b67ba0b7..689b7128bc 100644
--- a/core/lib/Drupal/Core/Field/WidgetBase.php
+++ b/core/lib/Drupal/Core/Field/WidgetBase.php
@@ -456,8 +456,8 @@ public function flagErrors(FieldItemListInterface $items, ConstraintViolationLis
           }
           // Otherwise, pass errors by delta to the corresponding sub-element.
           else {
-            $original_delta = $field_state['original_deltas'][$delta];
-            $delta_element = $element[$original_delta];
+            $original_delta = $field_state['original_deltas'][$delta] ?? NULL;
+            $delta_element = $element[$original_delta] ?? NULL;
           }
           foreach ($delta_violations as $violation) {
             // @todo: Pass $violation->arrayPropertyPath as property path.
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..77fa286478 100644
--- a/core/lib/Drupal/Core/Form/FormSubmitter.php
+++ b/core/lib/Drupal/Core/Form/FormSubmitter.php
@@ -61,7 +61,7 @@ public function doSubmitForm(&$form, FormStateInterface &$form_state) {
 
       $batch['progressive'] = !$form_state->isProgrammed();
       $response = batch_process();
-      if ($batch['progressive']) {
+      if ($batch['progressive'] ?? NULL) {
         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/lib/Drupal/Core/TypedData/Validation/RecursiveContextualValidator.php b/core/lib/Drupal/Core/TypedData/Validation/RecursiveContextualValidator.php
index 90513008f3..50dcf41482 100644
--- a/core/lib/Drupal/Core/TypedData/Validation/RecursiveContextualValidator.php
+++ b/core/lib/Drupal/Core/TypedData/Validation/RecursiveContextualValidator.php
@@ -126,7 +126,7 @@ protected function validateNode(TypedDataInterface $data, $constraints = NULL, $
 
     $metadata = $this->metadataFactory->getMetadataFor($data);
     $cache_key = spl_object_hash($data);
-    $property_path = $is_root_call ? '' : PropertyPath::append($previous_path, $data->getName());
+    $property_path = $is_root_call ? '' : PropertyPath::append((string) $previous_path, (string) $data->getName());
 
     // Prefer a specific instance of the typed data manager stored by the data
     // if it is available. This is necessary for specialized typed data objects,
diff --git a/core/modules/color/color.module b/core/modules/color/color.module
index 48479e8773..ae0bcdeb07 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/config/src/Form/ConfigImportForm.php b/core/modules/config/src/Form/ConfigImportForm.php
index 7632e18408..674a33891c 100644
--- a/core/modules/config/src/Form/ConfigImportForm.php
+++ b/core/modules/config/src/Form/ConfigImportForm.php
@@ -126,7 +126,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     if ($path = $form_state->getValue('import_tarball')) {
       $this->configStorage->deleteAll();
       try {
-        $archiver = new ArchiveTar($path, 'gz');
+        $archiver = new ArchiveTar($path);
         $files = [];
         foreach ($archiver->listContent() as $file) {
           $files[] = $file['filename'];
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..a6b8cf2b39 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,7 @@ public function prepareRow(Row $row) {
     }
 
     $field_data = unserialize($row->getSourceProperty('field_data'));
-    $row->setSourceProperty('field_settings', $field_data['settings']);
+    $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..6260856bef 100644
--- a/core/modules/locale/locale.bulk.inc
+++ b/core/modules/locale/locale.bulk.inc
@@ -385,12 +385,14 @@ 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;
+          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/locale/tests/src/Functional/LocaleJavascriptTranslationTest.php b/core/modules/locale/tests/src/Functional/LocaleJavascriptTranslationTest.php
index 2219fa4664..02f1e5848d 100644
--- a/core/modules/locale/tests/src/Functional/LocaleJavascriptTranslationTest.php
+++ b/core/modules/locale/tests/src/Functional/LocaleJavascriptTranslationTest.php
@@ -152,7 +152,7 @@ public function testLocaleTranslationJsDependencies() {
 
     // Calculate the filename of the JS including the translations.
     $js_translation_files = \Drupal::state()->get('locale.translation.javascript');
-    $js_filename = $prefix . '_' . $js_translation_files[$prefix] . '.js';
+    $js_filename = $prefix . '_' . ($js_translation_files[$prefix] ?? '') . '.js';
 
     $content = $this->getSession()->getPage()->getContent();
     // Assert translations JS is included before drupal.js.
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 9a8d8c999c..b44430c7fc 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..d09886bcbb 100644
--- a/core/modules/rdf/rdf.module
+++ b/core/modules/rdf/rdf.module
@@ -495,7 +495,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/rest/src/RequestHandler.php b/core/modules/rest/src/RequestHandler.php
index 8180b70cb8..aadc906abe 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..d29b57be3c 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,7 @@ 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']);
+    $row->setSourceProperty($other_property . '_translated', $results['translation'] ?? NULL);
 
     parent::prepareRow($row);
   }
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 dfda4a9fc0..c354a38461 100644
--- a/core/tests/Drupal/KernelTests/KernelTestBase.php
+++ b/core/tests/Drupal/KernelTests/KernelTestBase.php
@@ -605,9 +605,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/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();
