diff --git a/README.md b/README.md
index a153d40fbcdd9fe5aefc1cbe5dd1f951d872ba1e..b83a64fbd41f97c1be9a06462bc8b6fe25827765 100644
--- a/README.md
+++ b/README.md
@@ -9,11 +9,14 @@ This is the development repository for the *CiviCRM* plugin for *WordPress*. Wha
 
 If you want to contribute to the development of this plugin, please bear the following in mind:
 
-* Bug fixes should go in the branch 4.5 branch (stable)
* Structural changes should go under master (trunk, i.e. 4.6).
+* Bug Fixes and structural changes should go in the master branch.
+* Regression fixes should go in the current version branch.
 
 ----
-
### About CiviCRM ###
-
CiviCRM is web-based, open source, Constituent Relationship Management (CRM) software geared toward meeting the needs of non-profit and other civic-sector organizations.
+
+### About CiviCRM ###
+
+CiviCRM is web-based, open source, Constituent Relationship Management (CRM) software geared toward meeting the needs of non-profit and other civic-sector organizations.
 
 As a non profit committed to the public good itself, CiviCRM understands that forging and growing strong relationships with constituents is about more than collecting and tracking constituent data - it is about sustaining relationships with supporters over time.
 
@@ -23,4 +26,4 @@ With CiviCRM's robust feature set, organizations can further their mission throu
 
 CiviCRM is localized in over 20 languages including: Chinese (Taiwan, China), Dutch, English (Australia, Canada, U.S., UK), French (France, Canada), German, Italian, Japanese, Russian, and Swedish.
 
-For more information, visit the [CiviCRM website](https://civicrm.org).
\ No newline at end of file
+For more information, visit the [CiviCRM website](https://civicrm.org).
diff --git a/civicrm.php b/civicrm.php
index 9a11fbfab12d451cc0ed7a519ebd12b30676d6ca..fc1b23a1ba5c2f8c3887552cf0b5d75217e89d16 100644
--- a/civicrm.php
+++ b/civicrm.php
@@ -2,7 +2,9 @@
 /*
 Plugin Name: CiviCRM
 Description: CiviCRM - Growing and Sustaining Relationships
-Version: 5.26.2
+Version: 5.27.0
+Requires at least: 4.9
+Requires PHP:      7.1
 Author: CiviCRM LLC
 Author URI: https://civicrm.org/
 Plugin URI: https://docs.civicrm.org/sysadmin/en/latest/install/wordpress/
@@ -54,7 +56,7 @@ if ( ! defined( 'ABSPATH' ) ) exit;
 
 
 // Set version here: when it changes, will force JS to reload
-define( 'CIVICRM_PLUGIN_VERSION', '5.26.2' );
+define( 'CIVICRM_PLUGIN_VERSION', '5.27.0' );
 
 // Store reference to this file
 if (!defined('CIVICRM_PLUGIN_FILE')) {
diff --git a/civicrm/CRM/ACL/API.php b/civicrm/CRM/ACL/API.php
index 95273671bfd4496cb33bd37de5d3f965c81384ee..ba4991dc2de8d29fb8452427f29347a00458c064 100644
--- a/civicrm/CRM/ACL/API.php
+++ b/civicrm/CRM/ACL/API.php
@@ -88,11 +88,13 @@ class CRM_ACL_API {
     // the default value which is valid for the final AND
     $deleteClause = ' ( 1 ) ';
     if (!$skipDeleteClause) {
-      if (CRM_Core_Permission::check('access deleted contacts') and $onlyDeleted) {
-        $deleteClause = '(contact_a.is_deleted)';
+      if (CRM_Core_Permission::check('access deleted contacts')) {
+        if ($onlyDeleted) {
+          $deleteClause = '(contact_a.is_deleted)';
+        }
       }
       else {
-        // CRM-6181
+        // Exclude deleted contacts due to permissions
         $deleteClause = '(contact_a.is_deleted = 0)';
       }
     }
diff --git a/civicrm/CRM/Activity/BAO/Activity.php b/civicrm/CRM/Activity/BAO/Activity.php
index 332e8bac878612357e3548118608d79003d0b3ac..1efa8202448fde0067d7347565f84e571a285b2a 100644
--- a/civicrm/CRM/Activity/BAO/Activity.php
+++ b/civicrm/CRM/Activity/BAO/Activity.php
@@ -182,6 +182,8 @@ class CRM_Activity_BAO_Activity extends CRM_Activity_DAO_Activity {
         // CRM-13994 delete activity entity_tag
         $query = "DELETE FROM civicrm_entity_tag WHERE entity_table = 'civicrm_activity' AND entity_id = %1";
         $dao = CRM_Core_DAO::executeQuery($query, [1 => [$activity->id, 'Positive']]);
+
+        CRM_Core_BAO_File::deleteEntityFile('civicrm_activity', $activity->id);
       }
     }
     else {
@@ -989,11 +991,13 @@ class CRM_Activity_BAO_Activity extends CRM_Activity_DAO_Activity {
   }
 
   /**
-   * @param int $userID
+   * @param int $sourceContactID
+   *   The contact ID of the email "from".
    * @param string $subject
    * @param string $html
    * @param string $text
    * @param string $additionalDetails
+   *   The additional information of CC and BCC appended to the activity details.
    * @param int $campaignID
    * @param array $attachments
    * @param int $caseID
@@ -1002,12 +1006,12 @@ class CRM_Activity_BAO_Activity extends CRM_Activity_DAO_Activity {
    *   The created activity ID
    * @throws \CRM_Core_Exception
    */
-  public static function createEmailActivity($userID, $subject, $html, $text, $additionalDetails, $campaignID, $attachments, $caseID) {
+  public static function createEmailActivity($sourceContactID, $subject, $html, $text, $additionalDetails, $campaignID, $attachments, $caseID) {
     $activityTypeID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Email');
 
     // CRM-6265: save both text and HTML parts in details (if present)
     if ($html and $text) {
-      $details = "-ALTERNATIVE ITEM 0-\n$html$additionalDetails\n-ALTERNATIVE ITEM 1-\n$text$additionalDetails\n-ALTERNATIVE END-\n";
+      $details = "-ALTERNATIVE ITEM 0-\n{$html}{$additionalDetails}\n-ALTERNATIVE ITEM 1-\n{$text}{$additionalDetails}\n-ALTERNATIVE END-\n";
     }
     else {
       $details = $html ? $html : $text;
@@ -1015,12 +1019,11 @@ class CRM_Activity_BAO_Activity extends CRM_Activity_DAO_Activity {
     }
 
     $activityParams = [
-      'source_contact_id' => $userID,
+      'source_contact_id' => $sourceContactID,
       'activity_type_id' => $activityTypeID,
       'activity_date_time' => date('YmdHis'),
       'subject' => $subject,
       'details' => $details,
-      // FIXME: check for name Completed and get ID from that lookup
       'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'status_id', 'Completed'),
       'campaign_id' => $campaignID,
     ];
@@ -1038,6 +1041,7 @@ class CRM_Activity_BAO_Activity extends CRM_Activity_DAO_Activity {
     }
 
     $activity = civicrm_api3('Activity', 'create', $activityParams);
+
     return $activity['id'];
   }
 
@@ -1077,10 +1081,10 @@ class CRM_Activity_BAO_Activity extends CRM_Activity_DAO_Activity {
    * @throws \CiviCRM_API3_Exception
    */
   public static function sendEmail(
-    &$contactDetails,
-    &$subject,
-    &$text,
-    &$html,
+    $contactDetails,
+    $subject,
+    $text,
+    $html,
     $emailAddress,
     $userID = NULL,
     $from = NULL,
@@ -2587,7 +2591,7 @@ INNER JOIN  civicrm_option_group grp ON (grp.id = option_group_id AND grp.name =
         $activity['DT_RowAttr']['data-entity'] = 'activity';
         $activity['DT_RowAttr']['data-id'] = $activityId;
 
-        $activity['activity_type'] = (!empty($activityIcons[$values['activity_type_id']]) ? '<span class="crm-i ' . $activityIcons[$values['activity_type_id']] . '"></span> ' : '') . $values['activity_type'];
+        $activity['activity_type'] = (!empty($activityIcons[$values['activity_type_id']]) ? '<span class="crm-i ' . $activityIcons[$values['activity_type_id']] . '" aria-hidden="true"></span> ' : '') . $values['activity_type'];
         $activity['subject'] = $values['subject'];
 
         if ($params['contact_id'] == $values['source_contact_id']) {
diff --git a/civicrm/CRM/Activity/Form/ActivityLinks.php b/civicrm/CRM/Activity/Form/ActivityLinks.php
index 024ed70da5c9fbd292ec4c027b0c3380219cf481..bc7ff22f089eb3986e7d77eed41d93a296d35a07 100644
--- a/civicrm/CRM/Activity/Form/ActivityLinks.php
+++ b/civicrm/CRM/Activity/Form/ActivityLinks.php
@@ -30,7 +30,7 @@ class CRM_Activity_Form_ActivityLinks extends CRM_Core_Form {
   public static function commonBuildQuickForm($self) {
     $contactId = CRM_Utils_Request::retrieve('cid', 'Positive', $self);
     if (!$contactId) {
-      $contactId = CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullObject, FALSE, NULL, $_REQUEST);
+      $contactId = CRM_Utils_Request::retrieve('cid', 'Positive');
     }
     $urlParams = "action=add&reset=1&cid={$contactId}&selectedChild=activity&atype=";
 
diff --git a/civicrm/CRM/Activity/Page/AJAX.php b/civicrm/CRM/Activity/Page/AJAX.php
index e652726a608d756508da8738964649b15401f588..1b07ef68a337d2b2a3ce04fe26976af24c76b431 100644
--- a/civicrm/CRM/Activity/Page/AJAX.php
+++ b/civicrm/CRM/Activity/Page/AJAX.php
@@ -209,7 +209,7 @@ class CRM_Activity_Page_AJAX {
       }
       // email column links/icon
       if ($row['email']) {
-        $row['email'] = '<a class="crm-hover-button crm-popup" href="' . CRM_Utils_System::url('civicrm/activity/email/add', 'reset=1&action=add&atype=3&cid=' . $row['cid']) . '&caseid=' . $caseID . '" title="' . ts('Send an Email') . '"><i class="crm-i fa-envelope"></i></a>';
+        $row['email'] = '<a class="crm-hover-button crm-popup" href="' . CRM_Utils_System::url('civicrm/activity/email/add', 'reset=1&action=add&atype=3&cid=' . $row['cid']) . '&caseid=' . $caseID . '" title="' . ts('Send an Email') . '"><i class="crm-i fa-envelope" aria-hidden="true"></i></a>';
       }
       // edit links
       $row['actions'] = '';
@@ -219,7 +219,7 @@ class CRM_Activity_Page_AJAX {
         switch ($row['source']) {
           case 'caseRel':
             $row['actions'] = '<a href="#editCaseRoleDialog" title="' . ts('Reassign %1', [1 => $typeLabel]) . '" class="crm-hover-button case-miniform" data-contact_type="' . $contactType . '" data-rel_type="' . $row['relation_type'] . '_' . $row['relationship_direction'] . '" data-cid="' . $row['cid'] . '" data-rel_id="' . $row['rel_id'] . '"data-key="' . CRM_Core_Key::get('civicrm/ajax/relation') . '">' .
-              '<i class="crm-i fa-pencil"></i>' .
+              '<i class="crm-i fa-pencil" aria-hidden="true"></i>' .
               '</a>' .
               '<a href="#deleteCaseRoleDialog" title="' . ts('Remove %1', [1 => $typeLabel]) . '" class="crm-hover-button case-miniform" data-contact_type="' . $contactType . '" data-rel_type="' . $row['relation_type'] . '_' . $row['relationship_direction'] . '" data-cid="' . $row['cid'] . '" data-key="' . CRM_Core_Key::get('civicrm/ajax/delcaserole') . '">' .
               '<span class="icon delete-icon"></span>' .
@@ -228,7 +228,7 @@ class CRM_Activity_Page_AJAX {
 
           case 'caseRoles':
             $row['actions'] = '<a href="#editCaseRoleDialog" title="' . ts('Assign %1', [1 => $typeLabel]) . '" class="crm-hover-button case-miniform" data-contact_type="' . $contactType . '" data-rel_type="' . $row['relation_type'] . '_a_b" data-key="' . CRM_Core_Key::get('civicrm/ajax/relation') . '">' .
-              '<i class="crm-i fa-pencil"></i>' .
+              '<i class="crm-i fa-pencil" aria-hidden="true"></i>' .
               '</a>';
             break;
         }
@@ -353,7 +353,7 @@ class CRM_Activity_Page_AJAX {
     if (!empty($params['assigneeContactIds'])) {
       $assigneeContacts = array_unique(explode(',', $params['assigneeContactIds']));
     }
-    foreach ($assigneeContacts as $key => $value) {
+    foreach ($assigneeContacts as $value) {
       $assigneeParams = [
         'activity_id' => $mainActivityId,
         'contact_id' => $value,
diff --git a/civicrm/CRM/Admin/Form/MessageTemplates.php b/civicrm/CRM/Admin/Form/MessageTemplates.php
index 11b564a3099319d52b840b66be8033e2bd65a5c5..5f809f25beddd1ce466f51e926583af61a60226f 100644
--- a/civicrm/CRM/Admin/Form/MessageTemplates.php
+++ b/civicrm/CRM/Admin/Form/MessageTemplates.php
@@ -167,6 +167,7 @@ class CRM_Admin_Form_MessageTemplates extends CRM_Core_Form {
 
     //get the tokens.
     $tokens = CRM_Core_SelectValues::contactTokens();
+    $tokens = array_merge($tokens, CRM_Core_SelectValues::domainTokens());
 
     $this->assign('tokens', CRM_Utils_Token::formatTokensForDisplay($tokens));
 
diff --git a/civicrm/CRM/Admin/Form/Preferences/Mailing.php b/civicrm/CRM/Admin/Form/Preferences/Mailing.php
index d786297fe9296893349ddd659e4b0b0638f9b5d3..b2f4a975dc32a8317860a6e3b5a519604e1d1870 100644
--- a/civicrm/CRM/Admin/Form/Preferences/Mailing.php
+++ b/civicrm/CRM/Admin/Form/Preferences/Mailing.php
@@ -33,6 +33,8 @@ class CRM_Admin_Form_Preferences_Mailing extends CRM_Admin_Form_Preferences {
     'dedupe_email_default' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
     'hash_mailing_url' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
     'auto_recipient_rebuild' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
+    'url_tracking_default' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
+    'open_tracking_default' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
   ];
 
   public function postProcess() {
diff --git a/civicrm/CRM/Admin/Form/Setting/Localization.php b/civicrm/CRM/Admin/Form/Setting/Localization.php
index f7a8b6ea7c0a86ce66ac6b78db0e44c937ea7043..55bb965cf45d990934807d83a7c441e9aee92ae0 100644
--- a/civicrm/CRM/Admin/Form/Setting/Localization.php
+++ b/civicrm/CRM/Admin/Form/Setting/Localization.php
@@ -161,30 +161,13 @@ class CRM_Admin_Form_Setting_Localization extends CRM_Admin_Form_Setting {
 
     //cache contact fields retaining localized titles
     //though we changed localization, so reseting cache.
-    Civi::cache('fields')->flush();
+    Civi::cache('fields')->clear();
 
     //CRM-8559, cache navigation do not respect locale if it is changed, so reseting cache.
-    Civi::cache('navigation')->flush();
+    Civi::cache('navigation')->clear();
     // reset ACL and System caches
     CRM_Core_BAO_Cache::resetCaches();
 
-    // we do this only to initialize monetary decimal point and thousand separator
-    $config = CRM_Core_Config::singleton();
-
-    // save enabled currencies and default currency in option group 'currencies_enabled'
-    // CRM-1496
-    if (empty($values['currencyLimit'])) {
-      $values['currencyLimit'] = [$values['defaultCurrency']];
-    }
-    elseif (!in_array($values['defaultCurrency'], $values['currencyLimit'])) {
-      $values['currencyLimit'][] = $values['defaultCurrency'];
-    }
-
-    self::updateEnabledCurrencies($values['currencyLimit'], $values['defaultCurrency']);
-
-    // unset currencyLimit so we dont store there
-    unset($values['currencyLimit']);
-
     // make the site multi-lang if requested
     if (!empty($values['makeMultilingual'])) {
       CRM_Core_I18n_Schema::makeMultilingual($values['lcMessages']);
@@ -214,6 +197,21 @@ class CRM_Admin_Form_Setting_Localization extends CRM_Admin_Form_Setting {
     // if we manipulated the language list, return to the localization admin screen
     $return = (bool) (CRM_Utils_Array::value('makeMultilingual', $values) or CRM_Utils_Array::value('addLanguage', $values));
 
+    // Update enabled currencies
+    // we do this only to initialize monetary decimal point and thousand separator
+    $config = CRM_Core_Config::singleton();
+    // save enabled currencies and default currency in option group 'currencies_enabled'
+    // CRM-1496
+    if (empty($values['currencyLimit'])) {
+      $values['currencyLimit'] = [$values['defaultCurrency']];
+    }
+    elseif (!in_array($values['defaultCurrency'], $values['currencyLimit'])) {
+      $values['currencyLimit'][] = $values['defaultCurrency'];
+    }
+    self::updateEnabledCurrencies($values['currencyLimit'], $values['defaultCurrency']);
+    // unset currencyLimit so we dont store there
+    unset($values['currencyLimit']);
+
     $filteredValues = $values;
     unset($filteredValues['makeMultilingual']);
     unset($filteredValues['makeSinglelingual']);
diff --git a/civicrm/CRM/Admin/Form/Setting/Url.php b/civicrm/CRM/Admin/Form/Setting/Url.php
index 09b1dd1fc45f2d558e771121071e031fe7142626..fa854247eaf4243b053c5c79cd4d964d65e11c77 100644
--- a/civicrm/CRM/Admin/Form/Setting/Url.php
+++ b/civicrm/CRM/Admin/Form/Setting/Url.php
@@ -21,6 +21,7 @@
 class CRM_Admin_Form_Setting_Url extends CRM_Admin_Form_Setting {
   protected $_settings = [
     'disable_core_css' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
+    'defaultExternUrl' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
     'userFrameworkResourceURL' => CRM_Core_BAO_Setting::URL_PREFERENCES_NAME,
     'imageUploadURL' => CRM_Core_BAO_Setting::URL_PREFERENCES_NAME,
     'customCSSURL' => CRM_Core_BAO_Setting::URL_PREFERENCES_NAME,
diff --git a/civicrm/CRM/Admin/Page/APIExplorer.php b/civicrm/CRM/Admin/Page/APIExplorer.php
index 13363ee23122a5dc8e84f61e87f7cc2713acc5b9..c769535be31717c3635ce8370a3e366faba915a8 100644
--- a/civicrm/CRM/Admin/Page/APIExplorer.php
+++ b/civicrm/CRM/Admin/Page/APIExplorer.php
@@ -177,7 +177,7 @@ class CRM_Admin_Page_APIExplorer extends CRM_Core_Page {
     // Fetch block for a specific action
     else {
       $action = strtolower($action);
-      $fnName = 'civicrm_api3_' . _civicrm_api_get_entity_name_from_camel($entity) . '_' . $action;
+      $fnName = 'civicrm_api3_' . CRM_Core_DAO_AllCoreTables::convertEntityNameToLower($entity) . '_' . $action;
       // Support the alternate "1 file per action" structure
       $actionFile = "api/v3/$entity/" . ucfirst($action) . '.php';
       $actionFileContents = file_get_contents("api/v3/$entity/" . ucfirst($action) . '.php', FILE_USE_INCLUDE_PATH);
@@ -205,7 +205,8 @@ class CRM_Admin_Page_APIExplorer extends CRM_Core_Page {
 
   /**
    * Format a docblock to be a bit more readable
-   * Not a proper doc parser... patches welcome :)
+   *
+   * FIXME: APIv4 uses markdown in code docs. Switch to that.
    *
    * @param string $text
    * @return string
@@ -224,8 +225,8 @@ class CRM_Admin_Page_APIExplorer extends CRM_Core_Page {
 
     // Extract code blocks - save for later to skip html conversion
     $code = [];
-    preg_match_all('#@code(.*?)@endcode#is', $text, $code);
-    $text = preg_replace('#@code.*?@endcode#is', '<pre></pre>', $text);
+    preg_match_all('#(@code|```)(.*?)(@endcode|```)#is', $text, $code);
+    $text = preg_replace('#(@code|```)(.*?)(@endcode|```)#is', '<pre></pre>', $text);
 
     // Convert @annotations to titles
     $text = preg_replace_callback(
@@ -242,8 +243,8 @@ class CRM_Admin_Page_APIExplorer extends CRM_Core_Page {
     $text = nl2br($text);
 
     // Add unformatted code blocks back in
-    if ($code && !empty($code[1])) {
-      foreach ($code[1] as $block) {
+    if ($code && !empty($code[2])) {
+      foreach ($code[2] as $block) {
         $text = preg_replace('#<pre></pre>#', "<pre>$block</pre>", $text, 1);
       }
     }
diff --git a/civicrm/CRM/Admin/Page/Admin.php b/civicrm/CRM/Admin/Page/Admin.php
index fc4d291c320f61d4d4870b4bd2d4be30af3c2fff..8fb257635cabff3f79bd8bc21fa9591a589e92cf 100644
--- a/civicrm/CRM/Admin/Page/Admin.php
+++ b/civicrm/CRM/Admin/Page/Admin.php
@@ -39,60 +39,28 @@ class CRM_Admin_Page_Admin extends CRM_Core_Page {
     ];
 
     $config = CRM_Core_Config::singleton();
-    if (in_array('CiviContribute', $config->enableComponents)) {
-      $groups['CiviContribute'] = ts('CiviContribute');
-    }
-
-    if (in_array('CiviMember', $config->enableComponents)) {
-      $groups['CiviMember'] = ts('CiviMember');
-    }
-
-    if (in_array('CiviEvent', $config->enableComponents)) {
-      $groups['CiviEvent'] = ts('CiviEvent');
-    }
-
-    if (in_array('CiviMail', $config->enableComponents)) {
-      $groups['CiviMail'] = ts('CiviMail');
-    }
-
-    if (in_array('CiviCase', $config->enableComponents)) {
-      $groups['CiviCase'] = ts('CiviCase');
-    }
-
-    if (in_array('CiviReport', $config->enableComponents)) {
-      $groups['CiviReport'] = ts('CiviReport');
-    }
 
-    if (in_array('CiviCampaign', $config->enableComponents)) {
-      $groups['CiviCampaign'] = ts('CiviCampaign');
+    foreach ($config->enableComponents as $component) {
+      $comp = CRM_Core_Component::get($component);
+      $groups[$comp->info['name']] = $comp->info['translatedName'];
     }
 
     $values = CRM_Core_Menu::getAdminLinks();
 
-    $this->_showHide = new CRM_Core_ShowHideBlocks();
     foreach ($groups as $group => $title) {
       $groupId = str_replace(' ', '_', $group);
-
-      $this->_showHide->addShow("id_{$groupId}_show");
-      $this->_showHide->addHide("id_{$groupId}");
-      $v = CRM_Core_ShowHideBlocks::links($this, $groupId, '', '', FALSE);
-      if (isset($values[$group])) {
-        $adminPanel[$groupId] = $values[$group];
-        $adminPanel[$groupId]['show'] = $v['show'];
-        $adminPanel[$groupId]['hide'] = $v['hide'];
-        $adminPanel[$groupId]['title'] = $title;
-      }
-      else {
-        $adminPanel[$groupId] = [];
-        $adminPanel[$groupId]['show'] = '';
-        $adminPanel[$groupId]['hide'] = '';
-        $adminPanel[$groupId]['title'] = $title;
-      }
+      $adminPanel[$groupId] = array_merge($values[$group] ?? [], ['title' => $title]);
     }
 
     CRM_Utils_Hook::alterAdminPanel($adminPanel);
+    foreach ($adminPanel as $groupId => $group) {
+      if (count($group) == 1) {
+        // Presumably the only thing is the title; remove the section.
+        // This is done here to give the hook a chance to edit the section.
+        unset($adminPanel[$groupId]);
+      }
+    }
     $this->assign('adminPanel', $adminPanel);
-    $this->_showHide->addToTemplate();
     return parent::run();
   }
 
diff --git a/civicrm/CRM/Admin/Page/ConfigTaskList.php b/civicrm/CRM/Admin/Page/ConfigTaskList.php
index f4a65566036b38dfd99b49cee1b87754f2fb060e..ab7e91de6112f4027258df5654e9f9a0ac7e89a1 100644
--- a/civicrm/CRM/Admin/Page/ConfigTaskList.php
+++ b/civicrm/CRM/Admin/Page/ConfigTaskList.php
@@ -46,7 +46,7 @@ class CRM_Admin_Page_ConfigTaskList extends CRM_Core_Page {
       'sequential' => 1,
       'return' => ["enable_components"],
     ]);
-    $enabled = array();
+    $enabled = [];
     foreach ($result['values'][0]['enable_components'] as $component) {
       $enabled[$component] = 1;
     }
diff --git a/civicrm/CRM/Admin/Page/Job.php b/civicrm/CRM/Admin/Page/Job.php
index f2eed5d69c930d7e063783402c700fdc1f4e71ac..e8ba06d9d549feddc6523d1bfb01834c7c1a3b4c 100644
--- a/civicrm/CRM/Admin/Page/Job.php
+++ b/civicrm/CRM/Admin/Page/Job.php
@@ -144,7 +144,7 @@ class CRM_Admin_Page_Job extends CRM_Core_Page_Basic {
     }
 
     $sj = new CRM_Core_JobManager();
-    $rows = $temp = array();
+    $rows = $temp = [];
     foreach ($sj->jobs as $job) {
       $action = array_sum(array_keys($this->links()));
 
diff --git a/civicrm/CRM/Admin/Page/JobLog.php b/civicrm/CRM/Admin/Page/JobLog.php
index 6f23cf0857c6dfcfdafde58c88d0adb3ea0b0844..c7b1226535f9274e0f4b7a328b529b5773e371eb 100644
--- a/civicrm/CRM/Admin/Page/JobLog.php
+++ b/civicrm/CRM/Admin/Page/JobLog.php
@@ -95,7 +95,7 @@ class CRM_Admin_Page_JobLog extends CRM_Core_Page_Basic {
     }
     $dao->find();
 
-    $rows = array();
+    $rows = [];
     while ($dao->fetch()) {
       unset($row);
       CRM_Core_DAO::storeValues($dao, $row);
diff --git a/civicrm/CRM/Admin/Page/PaymentProcessor.php b/civicrm/CRM/Admin/Page/PaymentProcessor.php
index 814b0064c7929e63875b967f65e4bc458745fcd6..1fb115f8e81481eae5ef91e9edde405bcf61fd62 100644
--- a/civicrm/CRM/Admin/Page/PaymentProcessor.php
+++ b/civicrm/CRM/Admin/Page/PaymentProcessor.php
@@ -108,7 +108,7 @@ class CRM_Admin_Page_PaymentProcessor extends CRM_Core_Page_Basic {
    */
   public function browse($action = NULL) {
     // get all custom groups sorted by weight
-    $paymentProcessor = array();
+    $paymentProcessor = [];
     $dao = new CRM_Financial_DAO_PaymentProcessor();
     $dao->is_test = 0;
     $dao->domain_id = CRM_Core_Config::domainID();
@@ -116,7 +116,7 @@ class CRM_Admin_Page_PaymentProcessor extends CRM_Core_Page_Basic {
     $dao->find();
 
     while ($dao->fetch()) {
-      $paymentProcessor[$dao->id] = array();
+      $paymentProcessor[$dao->id] = [];
       CRM_Core_DAO::storeValues($dao, $paymentProcessor[$dao->id]);
       $paymentProcessor[$dao->id]['payment_processor_type'] = CRM_Core_PseudoConstant::getLabel(
         'CRM_Financial_DAO_PaymentProcessor', 'payment_processor_type_id', $dao->payment_processor_type_id
diff --git a/civicrm/CRM/Batch/BAO/Batch.php b/civicrm/CRM/Batch/BAO/Batch.php
index c777fa4acb36c3cfd20d37010897e7b177a1cb7a..f44c0e4999485fcd03a7a35621bd2bd1ba4e9a9c 100644
--- a/civicrm/CRM/Batch/BAO/Batch.php
+++ b/civicrm/CRM/Batch/BAO/Batch.php
@@ -42,20 +42,10 @@ class CRM_Batch_BAO_Batch extends CRM_Batch_DAO_Batch {
    *   $batch batch object
    */
   public static function create(&$params) {
-    $op = 'edit';
-    $batchId = $params['id'] ?? NULL;
-    if (!$batchId) {
-      $op = 'create';
+    if (empty($params['id']) && empty($params['name'])) {
       $params['name'] = CRM_Utils_String::titleToVar($params['title']);
     }
-    CRM_Utils_Hook::pre($op, 'Batch', $batchId, $params);
-    $batch = new CRM_Batch_DAO_Batch();
-    $batch->copyValues($params);
-    $batch->save();
-
-    CRM_Utils_Hook::post($op, 'Batch', $batch->id, $batch);
-
-    return $batch;
+    return self::writeRecord($params);
   }
 
   /**
diff --git a/civicrm/CRM/Campaign/BAO/Campaign.php b/civicrm/CRM/Campaign/BAO/Campaign.php
index 4ceeec2ea80d7b708f36eb538804ac10dda64685..7800cf925d466a00d0bcf6811ffda6545d82a2c6 100644
--- a/civicrm/CRM/Campaign/BAO/Campaign.php
+++ b/civicrm/CRM/Campaign/BAO/Campaign.php
@@ -34,10 +34,8 @@ class CRM_Campaign_BAO_Campaign extends CRM_Campaign_DAO_Campaign {
     }
 
     if (empty($params['id'])) {
-
       if (empty($params['created_id'])) {
-        $session = CRM_Core_Session::singleton();
-        $params['created_id'] = $session->get('userID');
+        $params['created_id'] = CRM_Core_Session::getLoggedInContactID();
       }
 
       if (empty($params['created_date'])) {
@@ -47,26 +45,11 @@ class CRM_Campaign_BAO_Campaign extends CRM_Campaign_DAO_Campaign {
       if (empty($params['name'])) {
         $params['name'] = CRM_Utils_String::titleToVar($params['title'], 64);
       }
-
-      CRM_Utils_Hook::pre('create', 'Campaign', NULL, $params);
-    }
-    else {
-      CRM_Utils_Hook::pre('edit', 'Campaign', $params['id'], $params);
     }
 
-    $campaign = new CRM_Campaign_DAO_Campaign();
-    $campaign->copyValues($params);
-    $campaign->save();
-
-    if (!empty($params['id'])) {
-      CRM_Utils_Hook::post('edit', 'Campaign', $campaign->id, $campaign);
-    }
-    else {
-      CRM_Utils_Hook::post('create', 'Campaign', $campaign->id, $campaign);
-    }
+    $campaign = self::writeRecord($params);
 
     /* Create the campaign group record */
-
     $groupTableName = CRM_Contact_BAO_Group::getTableName();
 
     if (isset($params['groups']) && !empty($params['groups']['include']) && is_array($params['groups']['include'])) {
@@ -81,9 +64,7 @@ class CRM_Campaign_BAO_Campaign extends CRM_Campaign_DAO_Campaign {
     }
 
     //store custom data
-    if (!empty($params['custom']) &&
-      is_array($params['custom'])
-    ) {
+    if (!empty($params['custom']) && is_array($params['custom'])) {
       CRM_Core_BAO_CustomValueTable::store($params['custom'], 'civicrm_campaign', $campaign->id);
     }
 
diff --git a/civicrm/CRM/Campaign/BAO/Survey.php b/civicrm/CRM/Campaign/BAO/Survey.php
index 9277e4f04014043dc3ce96673032b1623062b272..93b487982be9d7366851a1cc25296566c28f7dbe 100644
--- a/civicrm/CRM/Campaign/BAO/Survey.php
+++ b/civicrm/CRM/Campaign/BAO/Survey.php
@@ -62,36 +62,19 @@ class CRM_Campaign_BAO_Survey extends CRM_Campaign_DAO_Survey {
       CRM_Core_DAO::executeQuery($query);
     }
 
-    if (!(CRM_Utils_Array::value('id', $params))) {
-
-      if (!(CRM_Utils_Array::value('created_id', $params))) {
-        $session = CRM_Core_Session::singleton();
-        $params['created_id'] = $session->get('userID');
+    if (empty($params['id'])) {
+      if (empty($params['created_id'])) {
+        $params['created_id'] = CRM_Core_Session::getLoggedInContactID();
       }
-      if (!(CRM_Utils_Array::value('created_date', $params))) {
+
+      if (empty($params['created_date'])) {
         $params['created_date'] = date('YmdHis');
       }
-
-      CRM_Utils_Hook::pre('create', 'Survey', NULL, $params);
     }
-    else {
-      CRM_Utils_Hook::pre('edit', 'Survey', $params['id'], $params);
-    }
-
-    $dao = new CRM_Campaign_DAO_Survey();
-    $dao->copyValues($params);
-    $dao->save();
 
-    if (!empty($params['id'])) {
-      CRM_Utils_Hook::post('edit', 'Survey', $dao->id, $dao);
-    }
-    else {
-      CRM_Utils_Hook::post('create', 'Survey', $dao->id, $dao);
-    }
+    $dao = self::writeRecord($params);
 
-    if (!empty($params['custom']) &&
-      is_array($params['custom'])
-    ) {
+    if (!empty($params['custom']) && is_array($params['custom'])) {
       CRM_Core_BAO_CustomValueTable::store($params['custom'], 'civicrm_survey', $dao->id);
     }
     return $dao;
diff --git a/civicrm/CRM/Campaign/Form/Search.php b/civicrm/CRM/Campaign/Form/Search.php
index 66d744995c6843179a6cfd0f9c5f0429c5e89ee6..e110db8d6e983de424cd01d8b03facc3ab0b2d1b 100644
--- a/civicrm/CRM/Campaign/Form/Search.php
+++ b/civicrm/CRM/Campaign/Form/Search.php
@@ -55,7 +55,7 @@ class CRM_Campaign_Form_Search extends CRM_Core_Form_Search {
    */
   public function preProcess() {
     $this->_done = FALSE;
-    $this->_defaults = array();
+    $this->_defaults = [];
 
     //set the button name.
     $this->_printButtonName = $this->getButtonName('next', 'print');
diff --git a/civicrm/CRM/Campaign/Page/DashBoard.php b/civicrm/CRM/Campaign/Page/DashBoard.php
index 1318dd1c576bde5d4de18ed6cf33833847cc49e8..dfd6f3126cd7bcbd8b30cef56ad646260d5ddb11 100644
--- a/civicrm/CRM/Campaign/Page/DashBoard.php
+++ b/civicrm/CRM/Campaign/Page/DashBoard.php
@@ -312,11 +312,8 @@ class CRM_Campaign_Page_DashBoard extends CRM_Core_Page {
         }
         $surveysData[$sid]['isActive'] = $isActive;
 
-        $isDefault = NULL;
-        if ($surveysData[$sid]['is_default']) {
-          $isDefault = '<img src="' . $config->resourceBase . 'i/check.gif" alt="' . ts('Default') . '" />';
-        }
-        $surveysData[$sid]['is_default'] = $isDefault;
+        // For some reason, 'is_default' is coming as a string.
+        $surveysData[$sid]['is_default'] = boolval($surveysData[$sid]['is_default']);
 
         if ($surveysData[$sid]['result_id']) {
           $resultSet = '<a href= "javascript:displayResultSet( ' . $sid . ',' . "'" . $surveysData[$sid]['title'] . "'" . ', ' . $surveysData[$sid]['result_id'] . ' )" title="' . ts('view result set') . '">' . ts('Result Set') . '</a>';
@@ -415,11 +412,9 @@ class CRM_Campaign_Page_DashBoard extends CRM_Core_Page {
           $isActive = ts('Yes');
         }
         $petitionsData[$pid]['isActive'] = $isActive;
-        $isDefault = NULL;
-        if ($petitionsData[$pid]['is_default']) {
-          $isDefault = '<img src="' . $config->resourceBase . 'i/check.gif" alt="' . ts('Default') . '" />';
-        }
-        $petitionsData[$pid]['is_default'] = $isDefault;
+
+        // For some reason, 'is_default' is coming as a string.
+        $petitionsData[$pid]['is_default'] = boolval($petitionsData[$pid]['is_default']);
 
         $petitionsData[$pid]['action'] = CRM_Core_Action::formLink(self::petitionActionLinks(),
           $action,
diff --git a/civicrm/CRM/Campaign/xml/Menu/Campaign.xml b/civicrm/CRM/Campaign/xml/Menu/Campaign.xml
index 555eb1f2843bffb3a57e9c664303e15726e178bb..5d051df2493b63a5f8b2d96e2356395e09323c24 100644
--- a/civicrm/CRM/Campaign/xml/Menu/Campaign.xml
+++ b/civicrm/CRM/Campaign/xml/Menu/Campaign.xml
@@ -34,7 +34,6 @@
     <title>Survey Types</title>
     <page_callback>CRM_Campaign_Page_SurveyType</page_callback>
     <access_arguments>administer CiviCampaign</access_arguments>
-    <icon>admin/small/05.png</icon>
     <adminGroup>CiviCampaign</adminGroup>
     <component>CiviCampaign</component>
     <weight>1</weight>
@@ -45,7 +44,6 @@
      <desc>categorize your campaigns using campaign types.</desc>
      <page_callback>CRM_Admin_Page_Options</page_callback>
      <adminGroup>CiviCampaign</adminGroup>
-     <icon>admin/small/05.png</icon>
      <weight>2</weight>
      <component>CiviCampaign</component>
   </item>
@@ -55,7 +53,6 @@
      <desc>Define statuses for campaign here.</desc>
      <page_callback>CRM_Admin_Page_Options</page_callback>
      <adminGroup>CiviCampaign</adminGroup>
-     <icon>admin/small/05.png</icon>
      <weight>3</weight>
      <component>CiviCampaign</component>
   </item>
@@ -65,7 +62,6 @@
      <desc>Engagement levels.</desc>
      <page_callback>CRM_Admin_Page_Options</page_callback>
      <adminGroup>CiviCampaign</adminGroup>
-     <icon>admin/small/05.png</icon>
      <weight>4</weight>
      <component>CiviCampaign</component>
   </item>
diff --git a/civicrm/CRM/Case/BAO/Case.php b/civicrm/CRM/Case/BAO/Case.php
index 2f6ec7c95d395b43d4643eaa12c8a7c58e5d0f81..5a3f0b81061b74262c1fc2138237d556f60a840a 100644
--- a/civicrm/CRM/Case/BAO/Case.php
+++ b/civicrm/CRM/Case/BAO/Case.php
@@ -631,7 +631,7 @@ HERESQL;
           }
         }
         if (isset($case['activity_type_id']) && self::checkPermission($actId, 'edit', $case['activity_type_id'], $userID)) {
-          $casesList[$key]['date'] .= sprintf('<a class="action-item crm-hover-button" href="%s" title="%s"><i class="crm-i fa-pencil"></i></a>',
+          $casesList[$key]['date'] .= sprintf('<a class="action-item crm-hover-button" href="%s" title="%s"><i class="crm-i fa-pencil" aria-hidden="true"></i></a>',
             CRM_Utils_System::url('civicrm/case/activity', ['reset' => 1, 'cid' => $case['contact_id'], 'caseid' => $case['case_id'], 'action' => 'update', 'id' => $actId]),
             ts('Edit activity')
           );
diff --git a/civicrm/CRM/Case/BAO/CaseContact.php b/civicrm/CRM/Case/BAO/CaseContact.php
index 6e5ed5779b92e98124e84cde9c0d780dff847c55..3b409bb65cd4ad2a01271f35d582e1249fb735fe 100644
--- a/civicrm/CRM/Case/BAO/CaseContact.php
+++ b/civicrm/CRM/Case/BAO/CaseContact.php
@@ -29,14 +29,7 @@ class CRM_Case_BAO_CaseContact extends CRM_Case_DAO_CaseContact {
    * @return CRM_Case_BAO_CaseContact
    */
   public static function create($params) {
-    $hook = empty($params['id']) ? 'create' : 'edit';
-    CRM_Utils_Hook::pre($hook, 'CaseContact', CRM_Utils_Array::value('id', $params), $params);
-
-    $caseContact = new self();
-    $caseContact->copyValues($params);
-    $caseContact->save();
-
-    CRM_Utils_Hook::post($hook, 'CaseContact', $caseContact->id, $caseContact);
+    $caseContact = self::writeRecord($params);
 
     // add to recently viewed
     $caseType = CRM_Case_BAO_Case::getCaseType($caseContact->case_id);
diff --git a/civicrm/CRM/Case/Form/Activity.php b/civicrm/CRM/Case/Form/Activity.php
index d84717985ca2fe8bd68f357aa53f01238b60037c..01714e436b05a4dcb596827eb46b2c86688018c4 100644
--- a/civicrm/CRM/Case/Form/Activity.php
+++ b/civicrm/CRM/Case/Form/Activity.php
@@ -639,6 +639,28 @@ class CRM_Case_Form_Activity extends CRM_Activity_Form_Activity {
           ];
           CRM_Case_BAO_Case::processCaseActivity($caseParams);
           $followupStatus = ts("A followup activity has been scheduled.") . '<br /><br />';
+
+          //dev/core#1721
+          if (Civi::settings()->get('activity_assignee_notification') &&
+            !in_array($followupActivity->activity_type_id,
+              Civi::settings()->get('do_not_notify_assignees_for'))
+          ) {
+            $followupActivityIDs = [$followupActivity->id];
+            $followupAssigneeContacts = CRM_Activity_BAO_ActivityAssignment::getAssigneeNames($followupActivityIDs, TRUE, FALSE);
+
+            if (!empty($followupAssigneeContacts)) {
+              $mailToFollowupContacts = [];
+              foreach ($followupAssigneeContacts as $facValues) {
+                $mailToFollowupContacts[$facValues['email']] = $facValues;
+              }
+
+              $facParams['case_id'] = $vval['case_id'];
+              $sentFollowup = CRM_Activity_BAO_Activity::sendToAssignee($followupActivity, $mailToFollowupContacts, $facParams);
+              if ($sentFollowup) {
+                $mailStatus .= '<br />' . ts("A copy of the follow-up activity has also been sent to follow-up assignee contacts(s).");
+              }
+            }
+          }
         }
       }
       $title = ts("%1 Saved", [1 => $this->_activityTypeName]);
diff --git a/civicrm/CRM/Case/Form/AddToCaseAsRole.php b/civicrm/CRM/Case/Form/AddToCaseAsRole.php
index f2809047e3c97398e3caf18df8f555d3f7319307..91d55122a985d4fe3a9c8e2bd24ad54c7cd76041 100644
--- a/civicrm/CRM/Case/Form/AddToCaseAsRole.php
+++ b/civicrm/CRM/Case/Form/AddToCaseAsRole.php
@@ -51,7 +51,10 @@ class CRM_Case_Form_AddToCaseAsRole extends CRM_Contact_Form_Task {
     $roleTypes = [];
 
     foreach ($relType as $k => $v) {
-      $roleTypes[substr($k, 0, strpos($k, '_'))] = $v;
+      //Limit this to relationship types from contact A to B
+      if (substr($k, -4) == "_a_b") {
+        $roleTypes[substr($k, 0, strpos($k, '_'))] = $v;
+      }
     }
 
     return $roleTypes;
diff --git a/civicrm/CRM/Case/Info.php b/civicrm/CRM/Case/Info.php
index 0f996ee0c890116dd80d19f72e9ea8fd796bfc33..bf9d4adca5d870e14a1dad5e104b0320f3d828f2 100644
--- a/civicrm/CRM/Case/Info.php
+++ b/civicrm/CRM/Case/Info.php
@@ -228,6 +228,8 @@ class CRM_Case_Info extends CRM_Core_Component_Info {
    *   List of component names.
    * @param array $metadata
    *   Specification of the setting (per *.settings.php).
+   *
+   * @throws \CRM_Core_Exception.
    */
   public static function onToggleComponents($oldValue, $newValue, $metadata) {
     if (
@@ -238,8 +240,7 @@ class CRM_Case_Info extends CRM_Core_Component_Info {
       $pathToCaseSampleTpl = __DIR__ . '/xml/configuration.sample/';
       self::loadCaseSampleData($pathToCaseSampleTpl . 'case_sample.mysql.tpl');
       if (!CRM_Case_BAO_Case::createCaseViews()) {
-        $msg = ts("Could not create the MySQL views for CiviCase. Your mysql user needs to have the 'CREATE VIEW' permission");
-        CRM_Core_Error::fatal($msg);
+        throw new CRM_Core_Exception(ts("Could not create the MySQL views for CiviCase. Your mysql user needs to have the 'CREATE VIEW' permission"));
       }
     }
   }
diff --git a/civicrm/CRM/Case/Page/Tab.php b/civicrm/CRM/Case/Page/Tab.php
index e89132c3dedb03b506d74f5d6676659fc1bc4237..12f6cd06714d236ee121602e3451e5eb7c47bf2f 100644
--- a/civicrm/CRM/Case/Page/Tab.php
+++ b/civicrm/CRM/Case/Page/Tab.php
@@ -63,7 +63,7 @@ class CRM_Case_Page_Tab extends CRM_Core_Page {
     }
     else {
       if ($this->_action & CRM_Core_Action::VIEW) {
-        CRM_Core_Error::fatal('Contact Id is required for view action.');
+        CRM_Core_Error::statusBounce('Contact Id is required for view action.');
       }
     }
 
diff --git a/civicrm/CRM/Case/XMLProcessor/Process.php b/civicrm/CRM/Case/XMLProcessor/Process.php
index 4eb4e8826ef0321526fa62d964d115e18a2ae657..9307f2bb905a61f408e0121b175186bcd3200943 100644
--- a/civicrm/CRM/Case/XMLProcessor/Process.php
+++ b/civicrm/CRM/Case/XMLProcessor/Process.php
@@ -23,18 +23,16 @@ class CRM_Case_XMLProcessor_Process extends CRM_Case_XMLProcessor {
    * @param string $caseType
    * @param array $params
    *
-   * @return bool
-   * @throws Exception
+   * @throws CRM_Core_Exception
    */
   public function run($caseType, &$params) {
     $xml = $this->retrieve($caseType);
 
     if ($xml === FALSE) {
       $docLink = CRM_Utils_System::docURL2("user/case-management/set-up");
-      CRM_Core_Error::fatal(ts("Configuration file could not be retrieved for case type = '%1' %2.",
+      throw new CRM_Core_Exception(ts("Configuration file could not be retrieved for case type = '%1' %2.",
         [1 => $caseType, 2 => $docLink]
       ));
-      return FALSE;
     }
 
     $xmlProcessorProcess = new CRM_Case_XMLProcessor_Process();
@@ -56,10 +54,9 @@ class CRM_Case_XMLProcessor_Process extends CRM_Case_XMLProcessor {
     $xml = $this->retrieve($caseType);
     if ($xml === FALSE) {
       $docLink = CRM_Utils_System::docURL2("user/case-management/set-up");
-      CRM_Core_Error::fatal(ts("Unable to load configuration file for the referenced case type: '%1' %2.",
+      throw new CRM_Core_Exception(ts("Unable to load configuration file for the referenced case type: '%1' %2.",
         [1 => $caseType, 2 => $docLink]
       ));
-      return FALSE;
     }
 
     switch ($fieldSet) {
@@ -93,8 +90,7 @@ class CRM_Case_XMLProcessor_Process extends CRM_Case_XMLProcessor {
               $params
             )
             ) {
-              CRM_Core_Error::fatal();
-              return FALSE;
+              throw new CRM_Core_Exception('Unable to create case relationships');
             }
           }
         }
@@ -190,7 +186,7 @@ class CRM_Case_XMLProcessor_Process extends CRM_Case_XMLProcessor {
    * @param array $params
    *
    * @return bool
-   * @throws Exception
+   * @throws CRM_Core_Exception
    */
   public function createRelationships($relationshipTypeXML, &$params) {
 
@@ -198,10 +194,9 @@ class CRM_Case_XMLProcessor_Process extends CRM_Case_XMLProcessor {
     list($relationshipType, $relationshipTypeName) = $this->locateNameOrLabel($relationshipTypeXML);
     if ($relationshipType === FALSE) {
       $docLink = CRM_Utils_System::docURL2("user/case-management/set-up");
-      CRM_Core_Error::fatal(ts('Relationship type %1, found in case configuration file, is not present in the database %2',
+      throw new CRM_Core_Exception(ts('Relationship type %1, found in case configuration file, is not present in the database %2',
         [1 => $relationshipTypeName, 2 => $docLink]
       ));
-      return FALSE;
     }
 
     $client = $params['clientID'];
@@ -228,8 +223,7 @@ class CRM_Case_XMLProcessor_Process extends CRM_Case_XMLProcessor {
       }
 
       if (!$this->createRelationship($relationshipParams)) {
-        CRM_Core_Error::fatal();
-        return FALSE;
+        throw new CRM_Core_Exception('Unable to create case relationship');
       }
     }
     return TRUE;
@@ -415,10 +409,9 @@ AND        a.is_deleted = 0
 
     if (!$activityTypeInfo) {
       $docLink = CRM_Utils_System::docURL2("user/case-management/set-up");
-      CRM_Core_Error::fatal(ts('Activity type %1, found in case configuration file, is not present in the database %2',
+      throw new CRM_Core_Exception(ts('Activity type %1, found in case configuration file, is not present in the database %2',
         [1 => $activityTypeName, 2 => $docLink]
       ));
-      return FALSE;
     }
 
     $activityTypeID = $activityTypeInfo['id'];
@@ -549,8 +542,7 @@ AND        a.is_deleted = 0
     $activity = CRM_Activity_BAO_Activity::create($activityParams);
 
     if (!$activity) {
-      CRM_Core_Error::fatal();
-      return FALSE;
+      throw new CRM_Core_Exception('Unable to create Activity');
     }
 
     // create case activity record
@@ -731,17 +723,16 @@ AND        a.is_deleted = 0
 
   /**
    * @param $caseType
-   * @param null $activityTypeName
+   * @param string|null $activityTypeName
    *
    * @return array|bool|mixed
-   * @throws Exception
+   * @throws CRM_Core_Exception
    */
   public function getMaxInstance($caseType, $activityTypeName = NULL) {
     $xml = $this->retrieve($caseType);
 
     if ($xml === FALSE) {
-      CRM_Core_Error::fatal();
-      return FALSE;
+      throw new CRM_Core_Exception('Unable to locate xml definition for case type ' . $caseType);
     }
 
     $activityInstances = $this->activityTypes($xml->ActivityTypes, TRUE);
diff --git a/civicrm/CRM/Case/XMLProcessor/Report.php b/civicrm/CRM/Case/XMLProcessor/Report.php
index c4fcba56709ce4998bdeb5ad9452ed70d056e041..1fdff0091560ac78eaddb1df5ada09e492a3ca8e 100644
--- a/civicrm/CRM/Case/XMLProcessor/Report.php
+++ b/civicrm/CRM/Case/XMLProcessor/Report.php
@@ -63,10 +63,10 @@ class CRM_Case_XMLProcessor_Report extends CRM_Case_XMLProcessor {
     $clientID,
     $caseID
   ) {
-    $case = $this->_redactionRegexRules = array();
+    $case = $this->_redactionRegexRules = [];
 
     if (empty($this->_redactionStringRules)) {
-      $this->_redactionStringRules = array();
+      $this->_redactionStringRules = [];
     }
 
     if ($this->_isRedact == 1) {
@@ -111,7 +111,7 @@ class CRM_Case_XMLProcessor_Report extends CRM_Case_XMLProcessor {
     foreach ($xml->ActivitySets as $activitySetsXML) {
       foreach ($activitySetsXML->ActivitySet as $activitySetXML) {
         if ((string ) $activitySetXML->name == $activitySetName) {
-          $activityTypes = array();
+          $activityTypes = [];
           $allActivityTypes = CRM_Case_PseudoConstant::caseActivityType(TRUE, TRUE);
           foreach ($activitySetXML->ActivityTypes as $activityTypesXML) {
             foreach ($activityTypesXML as $activityTypeXML) {
@@ -192,7 +192,7 @@ AND    ac.case_id = %1
    * @return mixed
    */
   public function &getActivityInfo($clientID, $activityID, $anyActivity = FALSE, $redact = 0) {
-    static $activityInfos = array();
+    static $activityInfos = [];
     if ($redact) {
       $this->_isRedact = 1;
       $this->getRedactionRules();
@@ -205,7 +205,7 @@ AND    ac.case_id = %1
     }
 
     if (!array_key_exists($index, $activityInfos)) {
-      $activityInfos[$index] = array();
+      $activityInfos[$index] = [];
       $selectCaseActivity = "";
       $joinCaseActivity = "";
 
@@ -259,11 +259,11 @@ WHERE      a.id = %1
    */
   public function &getActivity($clientID, $activityDAO, &$activityTypeInfo) {
     if (empty($this->_redactionStringRules)) {
-      $this->_redactionStringRules = array();
+      $this->_redactionStringRules = [];
     }
 
-    $activity = array();
-    $activity['fields'] = array();
+    $activity = [];
+    $activity['fields'] = [];
     if ($clientID) {
       $clientID = CRM_Utils_Type::escape($clientID, 'Integer');
       if (!in_array($activityTypeInfo['name'], array(
@@ -326,7 +326,7 @@ WHERE      a.id = %1
       }
 
       if ($processTarget) {
-        $targetRedacted = array();
+        $targetRedacted = [];
         foreach ($targetNames as $targetID => $target) {
           // add Recipient SortName as well as Display to the strings to be redacted across the case session
           // suffixed with a randomly generated 4-digit number
@@ -513,11 +513,11 @@ WHERE      a.id = %1
 
     $params = array(1 => array($activityDAO->id, 'Integer'));
 
-    $customGroups = array();
+    $customGroups = [];
     foreach ($sql as $tableName => $sqlClause) {
       $dao = CRM_Core_DAO::executeQuery($sqlClause, $params);
       if ($dao->fetch()) {
-        $customGroup = array();
+        $customGroup = [];
         foreach ($typeValues[$tableName] as $columnName => $typeValue) {
 
           if (CRM_Utils_Array::value('type', $typeValue) == 'Date') {
@@ -565,7 +565,7 @@ WHERE      a.id = %1
    * @return mixed
    */
   public function getActivityTypeCustomSQL($activityTypeID, $dateFormat = NULL, $onlyActive = TRUE) {
-    static $cache = array();
+    static $cache = [];
 
     if (is_null($activityTypeID)) {
       $activityTypeID = 0;
@@ -606,11 +606,11 @@ AND " . CRM_Core_Permission::customGroupClause(CRM_Core_Permission::VIEW, 'cg.')
       );
       $dao = CRM_Core_DAO::executeQuery($query, $params);
 
-      $result = $options = $sql = $groupTitle = array();
+      $result = $options = $sql = $groupTitle = [];
       while ($dao->fetch()) {
         if (!array_key_exists($dao->tableName, $result)) {
-          $result[$dao->tableName] = array();
-          $sql[$dao->tableName] = array();
+          $result[$dao->tableName] = [];
+          $sql[$dao->tableName] = [];
         }
         $result[$dao->tableName][$dao->columnName] = array(
           'label' => $dao->label,
@@ -618,7 +618,7 @@ AND " . CRM_Core_Permission::customGroupClause(CRM_Core_Permission::VIEW, 'cg.')
           'fieldID' => $dao->fieldID,
         );
 
-        $options[$dao->fieldID] = array();
+        $options[$dao->fieldID] = [];
         $options[$dao->fieldID]['attributes'] = array(
           'label' => $dao->label,
           'data_type' => $dao->dataType,
@@ -695,7 +695,7 @@ LIMIT  1
    *
    * @return mixed
    */
-  private function redact($string, $printReport = FALSE, $replaceString = array()) {
+  private function redact($string, $printReport = FALSE, $replaceString = []) {
     if ($printReport) {
       return CRM_Utils_String::redaction($string, $replaceString);
     }
@@ -787,7 +787,7 @@ LIMIT  1
     $template->assign_by_ref('activitySet', $activitySet);
 
     //now collect all the information about activities
-    $activities = array();
+    $activities = [];
     $form->getActivities($clientID, $caseID, $activityTypes, $activities);
     $template->assign_by_ref('activities', $activities);
 
@@ -800,7 +800,7 @@ LIMIT  1
     $activitySetName = CRM_Utils_Request::retrieve('asn', 'String');
     $isRedact = CRM_Utils_Request::retrieve('redact', 'Boolean');
     $includeActivities = CRM_Utils_Request::retrieve('all', 'Positive');
-    $params = $otherRelationships = $globalGroupInfo = array();
+    $params = $otherRelationships = $globalGroupInfo = [];
     $report = new CRM_Case_XMLProcessor_Report($isRedact);
     if ($includeActivities) {
       $params['include_activities'] = 1;
@@ -808,7 +808,7 @@ LIMIT  1
 
     if ($isRedact) {
       $params['is_redact'] = 1;
-      $report->_redactionStringRules = array();
+      $report->_redactionStringRules = [];
     }
     $template = CRM_Core_Smarty::singleton();
 
@@ -960,7 +960,7 @@ LIMIT  1
     $customValues = CRM_Core_BAO_CustomValueTable::getEntityValues($caseID, 'Case');
     $extends = array('case');
     $groupTree = CRM_Core_BAO_CustomGroup::getGroupDetail(NULL, NULL, $extends);
-    $caseCustomFields = array();
+    $caseCustomFields = [];
     foreach ($groupTree as $gid => $group_values) {
       foreach ($group_values['fields'] as $id => $field_values) {
         if (array_key_exists($id, $customValues)) {
diff --git a/civicrm/CRM/Case/xml/Menu/Case.xml b/civicrm/CRM/Case/xml/Menu/Case.xml
index 91d5ceb984d2ff1647ca16bb4f8ef265eb7012b9..0d77eb9f8e282739b9975f10a4fc9565a1c3110f 100644
--- a/civicrm/CRM/Case/xml/Menu/Case.xml
+++ b/civicrm/CRM/Case/xml/Menu/Case.xml
@@ -69,7 +69,6 @@
      <title>CiviCase Settings</title>
      <page_callback>CRM_Admin_Form_Setting_Case</page_callback>
      <adminGroup>CiviCase</adminGroup>
-     <icon>admin/small/36.png</icon>
      <weight>380</weight>
   </item>
   <item>
@@ -80,7 +79,6 @@
      <page_callback>CRM_Core_Page_Redirect</page_callback>
      <page_arguments>url=civicrm/a/#/caseType</page_arguments>
      <adminGroup>CiviCase</adminGroup>
-     <icon>admin/small/case_type.png</icon>
      <weight>390</weight>
   </item>
   <item>
@@ -90,7 +88,6 @@
      <page_callback>CRM_Admin_Page_Options</page_callback>
      <access_arguments>administer CiviCase</access_arguments>
      <adminGroup>CiviCase</adminGroup>
-     <icon>admin/small/redaction_type.png</icon>
      <weight>400</weight>
   </item>
   <item>
@@ -100,7 +97,6 @@
      <page_callback>CRM_Admin_Page_Options</page_callback>
      <access_arguments>administer CiviCase</access_arguments>
      <adminGroup>CiviCase</adminGroup>
-     <icon>admin/small/case_type.png</icon>
      <weight>400</weight>
   </item>
   <item>
@@ -110,7 +106,6 @@
      <page_callback>CRM_Admin_Page_Options</page_callback>
      <access_arguments>administer CiviCase</access_arguments>
      <adminGroup>CiviCase</adminGroup>
-     <icon>admin/small/case_type.png</icon>
      <weight>400</weight>
   </item>
   <item>
diff --git a/civicrm/CRM/Contact/BAO/Contact.php b/civicrm/CRM/Contact/BAO/Contact.php
index 5122ec5079aeb324b5961b0545dd363473d30e62..d795677303dafdb5505580db666eb2e118098c24 100644
--- a/civicrm/CRM/Contact/BAO/Contact.php
+++ b/civicrm/CRM/Contact/BAO/Contact.php
@@ -101,7 +101,7 @@ class CRM_Contact_BAO_Contact extends CRM_Contact_DAO_Contact {
    * @return CRM_Contact_DAO_Contact|CRM_Core_Error|NULL
    *   Created or updated contact object or error object.
    *   (error objects are being phased out in favour of exceptions)
-   * @throws \Exception
+   * @throws \CRM_Core_Exception
    */
   public static function add(&$params) {
     $contact = new CRM_Contact_DAO_Contact();
@@ -1320,7 +1320,7 @@ WHERE     civicrm_contact.id = " . CRM_Utils_Type::escape($id, 'Integer');
       return $contactTypes;
     }
     else {
-      CRM_Core_Error::fatal();
+      throw new CRM_Core_Exception();
     }
   }
 
@@ -2017,7 +2017,7 @@ ORDER BY civicrm_email.is_primary DESC";
     }
 
     if (empty($contactID)) {
-      CRM_Core_Error::fatal('Cannot proceed without a valid contact id');
+      throw new CRM_Core_Exception('Cannot proceed without a valid contact id');
     }
 
     // Process group and tag
@@ -3718,8 +3718,14 @@ LEFT JOIN civicrm_address ON ( civicrm_address.contact_id = civicrm_contact.id )
       ['key' => 'contact_type', 'value' => ts('Contact Type')],
       ['key' => 'group', 'value' => ts('Group'), 'entity' => 'GroupContact'],
       ['key' => 'tag', 'value' => ts('Tag'), 'entity' => 'EntityTag'],
+      ['key' => 'city', 'value' => ts('City'), 'type' => 'text', 'entity' => 'Address'],
+      ['key' => 'postal_code', 'value' => ts('Postal Code'), 'type' => 'text', 'entity' => 'Address'],
       ['key' => 'state_province', 'value' => ts('State/Province'), 'entity' => 'Address'],
       ['key' => 'country', 'value' => ts('Country'), 'entity' => 'Address'],
+      ['key' => 'first_name', 'value' => ts('First Name'), 'type' => 'text', 'condition' => ['contact_type' => 'Individual']],
+      ['key' => 'last_name', 'value' => ts('Last Name'), 'type' => 'text', 'condition' => ['contact_type' => 'Individual']],
+      ['key' => 'nick_name', 'value' => ts('Nick Name'), 'type' => 'text', 'condition' => ['contact_type' => 'Individual']],
+      ['key' => 'organization_name', 'value' => ts('Employer name'), 'type' => 'text', 'condition' => ['contact_type' => 'Individual']],
       ['key' => 'gender_id', 'value' => ts('Gender'), 'condition' => ['contact_type' => 'Individual']],
       ['key' => 'is_deceased', 'value' => ts('Deceased'), 'condition' => ['contact_type' => 'Individual']],
       ['key' => 'contact_id', 'value' => ts('Contact ID'), 'type' => 'text'],
diff --git a/civicrm/CRM/Contact/BAO/ContactType.php b/civicrm/CRM/Contact/BAO/ContactType.php
index 947f8dc4305e432b292d8320eb04b8e448cc2092..42fb2ba54dfba4508c01424c1c0732aa5d14fee3 100644
--- a/civicrm/CRM/Contact/BAO/ContactType.php
+++ b/civicrm/CRM/Contact/BAO/ContactType.php
@@ -9,6 +9,8 @@
  +--------------------------------------------------------------------+
  */
 
+use Civi\Api4\ContactType;
+
 /**
  *
  * @package CRM
@@ -53,48 +55,24 @@ class CRM_Contact_BAO_ContactType extends CRM_Contact_DAO_ContactType {
   /**
    * Retrieve basic contact type information.
    *
-   * @param bool $all
+   * @param bool $includeInactive
    *
    * @return array
    *   Array of basic contact types information.
+   *
+   * @throws \API_Exception
+   * @throws \Civi\API\Exception\UnauthorizedException
    */
-  public static function basicTypeInfo($all = FALSE) {
-    static $_cache = NULL;
-
-    if ($_cache === NULL) {
-      $_cache = [];
-    }
-
-    $argString = $all ? 'CRM_CT_BTI_1' : 'CRM_CT_BTI_0';
-    if (!array_key_exists($argString, $_cache)) {
-      $cache = CRM_Utils_Cache::singleton();
-      $_cache[$argString] = $cache->get($argString);
-      if (!$_cache[$argString]) {
-        $sql = "
-SELECT *
-FROM   civicrm_contact_type
-WHERE  parent_id IS NULL
-";
-        if ($all === FALSE) {
-          $sql .= " AND is_active = 1";
-        }
-
-        $params = [];
-        $dao = CRM_Core_DAO::executeQuery($sql,
-          $params,
-          FALSE,
-          'CRM_Contact_DAO_ContactType'
-        );
-        while ($dao->fetch()) {
-          $value = [];
-          CRM_Core_DAO::storeValues($dao, $value);
-          $_cache[$argString][$dao->name] = $value;
-        }
-
-        $cache->set($argString, $_cache[$argString]);
+  public static function basicTypeInfo($includeInactive = FALSE) {
+    $cacheKey = 'CRM_CT_BTI_' . (int) $includeInactive;
+    if (!Civi::cache('contactTypes')->has($cacheKey)) {
+      $contactType = ContactType::get()->setCheckPermissions(FALSE)->setSelect(['*'])->addWhere('parent_id', 'IS NULL');
+      if ($includeInactive === FALSE) {
+        $contactType->addWhere('is_active', '=', 1);
       }
+      Civi::cache('contactTypes')->set($cacheKey, (array) $contactType->execute()->indexBy('name'));
     }
-    return $_cache[$argString];
+    return Civi::cache('contactTypes')->get($cacheKey);
   }
 
   /**
@@ -104,6 +82,9 @@ WHERE  parent_id IS NULL
    *
    * @return array
    *   Array of basic contact types
+   *
+   * @throws \API_Exception
+   * @throws \Civi\API\Exception\UnauthorizedException
    */
   public static function basicTypes($all = FALSE) {
     return array_keys(self::basicTypeInfo($all));
@@ -114,6 +95,8 @@ WHERE  parent_id IS NULL
    * @param string $key
    *
    * @return array
+   * @throws \API_Exception
+   * @throws \Civi\API\Exception\UnauthorizedException
    */
   public static function basicTypePairs($all = FALSE, $key = 'name') {
     $subtypes = self::basicTypeInfo($all);
@@ -189,7 +172,7 @@ WHERE  subtype.name IS NOT NULL AND subtype.parent_id IS NOT NULL {$ctWHERE}
    *   a given basic contact type
    */
   public static function subTypes($contactType = NULL, $all = FALSE, $columnName = 'name', $ignoreCache = FALSE) {
-    if ($columnName == 'name') {
+    if ($columnName === 'name') {
       return array_keys(self::subTypeInfo($contactType, $all, $ignoreCache));
     }
     else {
@@ -236,18 +219,13 @@ WHERE  subtype.name IS NOT NULL AND subtype.parent_id IS NOT NULL {$ctWHERE}
    * Retrieve info array about all types i.e basic + subtypes.
    *
    * @param bool $all
-   * @param bool $reset
    *
    * @return array
    *   Array of basic types + all subtypes.
    */
-  public static function contactTypeInfo($all = FALSE, $reset = FALSE) {
+  public static function contactTypeInfo($all = FALSE) {
     static $_cache = NULL;
 
-    if ($reset === TRUE) {
-      $_cache = NULL;
-    }
-
     if ($_cache === NULL) {
       $_cache = [];
     }
@@ -259,14 +237,14 @@ WHERE  subtype.name IS NOT NULL AND subtype.parent_id IS NOT NULL {$ctWHERE}
       if (!$_cache[$argString]) {
         $_cache[$argString] = [];
 
-        $sql = "
+        $sql = '
 SELECT type.*, parent.name as parent, parent.label as parent_label
 FROM      civicrm_contact_type type
 LEFT JOIN civicrm_contact_type parent ON type.parent_id = parent.id
 WHERE  type.name IS NOT NULL
-";
+';
         if ($all === FALSE) {
-          $sql .= " AND type.is_active = 1";
+          $sql .= ' AND type.is_active = 1';
         }
 
         $dao = CRM_Core_DAO::executeQuery($sql,
@@ -360,19 +338,19 @@ WHERE  type.name IS NOT NULL
       if (!$_cache[$argString]) {
         $_cache[$argString] = [];
 
-        $sql = "
+        $sql = '
 SELECT    c.name as child_name , c.label as child_label , c.id as child_id,
           p.name as parent_name, p.label as parent_label, p.id as parent_id
 FROM      civicrm_contact_type c
 LEFT JOIN civicrm_contact_type p ON ( c.parent_id = p.id )
 WHERE     ( c.name IS NOT NULL )
-";
+';
 
         if ($all === FALSE) {
-          $sql .= "
+          $sql .= '
 AND   c.is_active = 1
 AND   ( p.is_active = 1 OR p.id IS NULL )
-";
+';
         }
         $sql .= " ORDER BY c.id";
 
@@ -578,10 +556,10 @@ WHERE contact_sub_type = '$name'";
 
     // remove navigation entry if any
     if ($name) {
-      $sql = "
+      $sql = '
 DELETE
 FROM civicrm_navigation
-WHERE name = %1";
+WHERE name = %1';
       $params = [1 => ["New $name", 'String']];
       CRM_Core_DAO::executeQuery($sql, $params);
       CRM_Core_BAO_Navigation::resetNavigation();
@@ -597,6 +575,7 @@ WHERE name = %1";
    *   An assoc array of name/value pairs.
    *
    * @return object|void
+   * @throws \CRM_Core_Exception
    */
   public static function add(&$params) {
 
@@ -624,7 +603,7 @@ WHERE name = %1";
 
     if (!empty($params['id'])) {
       $newParams = [
-        'label' => "New $contact",
+        'label' => ts("New %1", [1 => $contact]),
         'is_active' => $active,
       ];
       CRM_Core_BAO_Navigation::processUpdate(['name' => "New $contactName"], $newParams);
@@ -637,7 +616,7 @@ WHERE name = %1";
       $value = ['name' => "New $name"];
       CRM_Core_BAO_Navigation::retrieve($value, $navinfo);
       $navigation = [
-        'label' => "New $contact",
+        'label' => ts("New %1", [1 => $contact]),
         'name' => "New $contactName",
         'url' => "civicrm/contact/add?ct=$name&cst=$contactName&reset=1",
         'permission' => 'add contacts',
@@ -700,6 +679,7 @@ WHERE name = %1";
    *   Subtype.
    *
    * @return bool
+   * @throws \CRM_Core_Exception
    */
   public static function isAllowEdit($contactId, $subType = NULL) {
 
@@ -793,11 +773,12 @@ LIMIT 1";
   }
 
   /**
-   * @todo what does this function do?
    * @param $contactType
    * @param array $subtypeSet
    *
    * @return array
+   * @throws \CRM_Core_Exception
+   * @todo what does this function do?
    */
   public static function getSubtypeCustomPair($contactType, $subtypeSet = []) {
     if (empty($subtypeSet)) {
@@ -810,9 +791,9 @@ LIMIT 1";
       $subtype = CRM_Core_DAO::VALUE_SEPARATOR . $subtype . CRM_Core_DAO::VALUE_SEPARATOR;
       $subTypeClause[] = "extends_entity_column_value LIKE '%{$subtype}%' ";
     }
-    $query = "SELECT table_name
+    $query = 'SELECT table_name
 FROM civicrm_custom_group
-WHERE extends = %1 AND " . implode(" OR ", $subTypeClause);
+WHERE extends = %1 AND ' . implode(" OR ", $subTypeClause);
     $dao = CRM_Core_DAO::executeQuery($query, [1 => [$contactType, 'String']]);
     while ($dao->fetch()) {
       $customSet[] = $dao->table_name;
diff --git a/civicrm/CRM/Contact/BAO/GroupContact.php b/civicrm/CRM/Contact/BAO/GroupContact.php
index 3914a1c3667d1cca5c6ef2163ea4115b081d6285..9058101d05165675a6ff8c4e39ec8c6559218082 100644
--- a/civicrm/CRM/Contact/BAO/GroupContact.php
+++ b/civicrm/CRM/Contact/BAO/GroupContact.php
@@ -129,14 +129,13 @@ class CRM_Contact_BAO_GroupContact extends CRM_Contact_DAO_GroupContact {
 
     CRM_Utils_Hook::pre('create', 'GroupContact', $groupId, $contactIds);
 
-    list($numContactsAdded, $numContactsNotAdded)
-      = self::bulkAddContactsToGroup($contactIds, $groupId, $method, $status, $tracking);
-
+    $result = self::bulkAddContactsToGroup($contactIds, $groupId, $method, $status, $tracking);
+    CRM_Contact_BAO_GroupContactCache::invalidateGroupContactCache($groupId);
     CRM_Contact_BAO_Contact_Utils::clearContactCaches();
 
     CRM_Utils_Hook::post('create', 'GroupContact', $groupId, $contactIds);
 
-    return [count($contactIds), $numContactsAdded, $numContactsNotAdded];
+    return [count($contactIds), $result['count_added'], $result['count_not_added']];
   }
 
   /**
@@ -763,7 +762,7 @@ AND    contact_id IN ( $contactStr )
       }
     }
 
-    return [$numContactsAdded, $numContactsNotAdded];
+    return ['count_added' => $numContactsAdded, 'count_not_added' => $numContactsNotAdded];
   }
 
   /**
diff --git a/civicrm/CRM/Contact/BAO/Query.php b/civicrm/CRM/Contact/BAO/Query.php
index 3cabd6f2e0c5fcbfe54be773889f56567854a2aa..694f6a84d420a3fdc6f2ae0baf8a2142ca97b44a 100644
--- a/civicrm/CRM/Contact/BAO/Query.php
+++ b/civicrm/CRM/Contact/BAO/Query.php
@@ -685,12 +685,12 @@ class CRM_Contact_BAO_Query {
   public function addSpecialFields($apiEntity) {
     static $special = ['contact_type', 'contact_sub_type', 'sort_name', 'display_name'];
     // if get called via Contact.get API having address_id as return parameter
-    if ($apiEntity == 'Contact') {
+    if ($apiEntity === 'Contact') {
       $special[] = 'address_id';
     }
     foreach ($special as $name) {
       if (!empty($this->_returnProperties[$name])) {
-        if ($name == 'address_id') {
+        if ($name === 'address_id') {
           $this->_tables['civicrm_address'] = 1;
           $this->_select['address_id'] = 'civicrm_address.id as address_id';
           $this->_element['address_id'] = 1;
@@ -953,14 +953,14 @@ class CRM_Contact_BAO_Query {
           $this->_tables['civicrm_group_contact_cache'] = 1;
           $this->_pseudoConstantsSelect["{$name}"] = [
             'pseudoField' => "groups",
-            'idCol' => "groups",
+            'idCol' => 'groups',
           ];
         }
         elseif ($name === 'notes') {
           //@todo move this handling outside the big IF & ditch $makeException
           // if note field is subject then return subject else body of the note
           $noteColumn = 'note';
-          if (isset($noteField) && $noteField == 'note_subject') {
+          if (isset($noteField) && $noteField === 'note_subject') {
             $noteColumn = 'subject';
           }
 
@@ -1623,7 +1623,7 @@ class CRM_Contact_BAO_Query {
           }
         }
       }
-      elseif ($id == 'email_on_hold') {
+      elseif ($id === 'email_on_hold') {
         if ($onHoldValue = CRM_Utils_Array::value('email_on_hold', $formValues)) {
           // onHoldValue should be 0 or 1 or an array. Some legacy groups may hold ''
           // so in 5.11 we have an extra if that should become redundant over time.
@@ -1636,10 +1636,10 @@ class CRM_Contact_BAO_Query {
           }
         }
       }
-      elseif (substr($id, 0, 7) == 'custom_'
+      elseif (substr($id, 0, 7) === 'custom_'
         &&  (
-          substr($id, -5, 5) == '_from'
-          || substr($id, -3, 3) == '_to'
+          substr($id, -5, 5) === '_from'
+          || substr($id, -3, 3) === '_to'
         )
       ) {
         self::convertCustomRelativeFields($formValues, $params, $values, $id);
@@ -2982,7 +2982,7 @@ class CRM_Contact_BAO_Query {
       $value = CRM_Utils_Array::value($op, $value, $value);
     }
 
-    if ($name == 'group_type') {
+    if ($name === 'group_type') {
       $value = array_keys($this->getGroupsFromTypeCriteria($value));
     }
 
@@ -2997,7 +2997,7 @@ class CRM_Contact_BAO_Query {
     }
     $hasNonSmartGroups = count($regularGroupIDs);
 
-    $isNotOp = ($op == 'NOT IN' || $op == '!=');
+    $isNotOp = ($op === 'NOT IN' || $op === '!=');
 
     $statusJoinClause = $this->getGroupStatusClause($grouping);
     // If we are searching for 'Removed' contacts then despite it being a smart group we only care about the group_contact table.
@@ -3026,11 +3026,11 @@ class CRM_Contact_BAO_Query {
         }
       }
 
-      $gcTable = "`civicrm_group_contact-" . uniqid() . "`";
+      $gcTable = '`civicrm_group_contact-' . uniqid() . "`";
       $joinClause = ["contact_a.id = {$gcTable}.contact_id"];
 
       // @todo consider just casting != to NOT IN & handling both together.
-      if ($op == '!=') {
+      if ($op === '!=') {
         $groupIds = '';
         if (!empty($regularGroupIDs)) {
           $groupIds = CRM_Utils_Type::validate(
@@ -6201,7 +6201,7 @@ AND   displayRelType.is_active = 1
         // @todo - this is a bit specific (one operator).
         // However it is covered by a unit test so can be altered later with
         // some confidence.
-        if ($op == 'BETWEEN') {
+        if ($op === 'BETWEEN') {
           $separator = ' AND ';
         }
         $fieldValue = implode($separator, $fieldValue);
@@ -6250,7 +6250,7 @@ AND   displayRelType.is_active = 1
    * @return string
    */
   public static function getWildCardedValue($wildcard, $op, $value) {
-    if ($wildcard && $op == 'LIKE') {
+    if ($wildcard && $op === 'LIKE') {
       if (CRM_Core_Config::singleton()->includeWildCardInName && (substr($value, 0, 1) != '%')) {
         return "%$value%";
       }
@@ -6698,7 +6698,10 @@ AND   displayRelType.is_active = 1
    *   Should be clause with proper joins, effective to reduce where clause load.
    *
    * @param bool $skipOrderAndLimit
+   *
    * @return string
+   *
+   * @throws \CRM_Core_Exception
    */
   public function getSearchSQL(
     $offset = 0, $rowCount = 0, $sort = NULL,
@@ -6773,7 +6776,7 @@ AND   displayRelType.is_active = 1
       $name = $values[0] ?? NULL;
       $op = $values[1] ?? NULL;
       $value = $values[2] ?? NULL;
-      if ($name == 'contact_id' and $op == '=') {
+      if ($name === 'contact_id' and $op === '=') {
         if (CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $value, 'is_deleted')) {
           $onlyDeleted = TRUE;
         }
diff --git a/civicrm/CRM/Contact/BAO/Relationship.php b/civicrm/CRM/Contact/BAO/Relationship.php
index 53a3454fb9a878f6c4aba40358fc10bd35ba1e08..24122a128a84b9a4ecc81227948e5ffa2ed92300 100644
--- a/civicrm/CRM/Contact/BAO/Relationship.php
+++ b/civicrm/CRM/Contact/BAO/Relationship.php
@@ -1467,7 +1467,7 @@ LEFT JOIN  civicrm_country ON (civicrm_address.country_id = civicrm_country.id)
               if ($values[$rid]['rtype'] == 'b_a') {
                 $replace['clientid'] = $values[$rid]['cid'];
               }
-              $values[$rid]['case'] = '<a href="' . CRM_Utils_System::url('civicrm/case/ajax/details', sprintf('caseId=%d&cid=%d&snippet=4', $values[$rid]['case_id'], $values[$rid]['cid'])) . '" class="action-item crm-hover-button crm-summary-link"><i class="crm-i fa-folder-open-o"></i></a>';
+              $values[$rid]['case'] = '<a href="' . CRM_Utils_System::url('civicrm/case/ajax/details', sprintf('caseId=%d&cid=%d&snippet=4', $values[$rid]['case_id'], $values[$rid]['cid'])) . '" class="action-item crm-hover-button crm-summary-link"><i class="crm-i fa-folder-open-o" aria-hidden="true"></i></a>';
             }
           }
 
@@ -1489,13 +1489,14 @@ LEFT JOIN  civicrm_country ON (civicrm_address.country_id = civicrm_country.id)
   }
 
   /**
-   * Get get list of relationship type based on the target contact type.
+   * Get list of relationship type based on the target contact type.
+   * Both directions of relationships are included if their labels are not the same.
    *
    * @param string $targetContactType
-   *   It's valid contact tpye(may be Individual , Organization , Household).
+   *   A valid contact type (may be Individual, Organization, Household).
    *
    * @return array
-   *   array reference of all relationship types with context to current contact type .
+   *   array reference of all relationship types with context to current contact type.
    */
   public static function getRelationType($targetContactType) {
     $relationshipType = [];
@@ -1505,6 +1506,11 @@ LEFT JOIN  civicrm_country ON (civicrm_address.country_id = civicrm_country.id)
       if ($type['contact_type_b'] == $targetContactType || empty($type['contact_type_b'])) {
         $relationshipType[$key . '_a_b'] = $type['label_a_b'];
       }
+      if (($type['contact_type_a'] == $targetContactType || empty($type['contact_type_a']))
+        && $type['label_a_b'] != $type['label_b_a']
+      ) {
+        $relationshipType[$key . '_b_a'] = $type['label_b_a'];
+      }
     }
 
     return $relationshipType;
diff --git a/civicrm/CRM/Contact/BAO/RelationshipType.php b/civicrm/CRM/Contact/BAO/RelationshipType.php
index 6c44ea5900dcf7638d3d78de5cc38b4dd1721314..08b0d8103232254090dd1e17fc0c4e5939392b46 100644
--- a/civicrm/CRM/Contact/BAO/RelationshipType.php
+++ b/civicrm/CRM/Contact/BAO/RelationshipType.php
@@ -85,15 +85,7 @@ class CRM_Contact_BAO_RelationshipType extends CRM_Contact_DAO_RelationshipType
     }
 
     // action is taken depending upon the mode
-    $relationshipType = new CRM_Contact_DAO_RelationshipType();
-
-    $hook = empty($params['id']) ? 'create' : 'edit';
-    CRM_Utils_Hook::pre($hook, 'RelationshipType', CRM_Utils_Array::value('id', $params), $params);
-
-    $relationshipType->copyValues($params);
-    $relationshipType->save();
-
-    CRM_Utils_Hook::post($hook, 'RelationshipType', $relationshipType->id, $relationshipType);
+    $relationshipType = self::writeRecord($params);
 
     CRM_Core_PseudoConstant::relationshipType('label', TRUE);
     CRM_Core_PseudoConstant::relationshipType('name', TRUE);
diff --git a/civicrm/CRM/Contact/Form/CustomData.php b/civicrm/CRM/Contact/Form/CustomData.php
index 86dc621b46548bad4166c8562c9c3117ad2373b9..87849e1b28eb8950ef60d64fe91be3881c91173d 100644
--- a/civicrm/CRM/Contact/Form/CustomData.php
+++ b/civicrm/CRM/Contact/Form/CustomData.php
@@ -52,13 +52,6 @@ class CRM_Contact_Form_CustomData extends CRM_Core_Form {
    */
   //protected $_groupTree;
 
-  /**
-   * Which blocks should we show and hide.
-   *
-   * @var CRM_Core_ShowHideBlocks
-   */
-  protected $_showHide;
-
   /**
    * Array group titles.
    *
diff --git a/civicrm/CRM/Contact/Form/Edit/Individual.php b/civicrm/CRM/Contact/Form/Edit/Individual.php
index 29d8dc0a5f4da8cb6f468fa16dd8619ea4e48968..f4d933933a3c30915c800db695d7b833e212d98d 100644
--- a/civicrm/CRM/Contact/Form/Edit/Individual.php
+++ b/civicrm/CRM/Contact/Form/Edit/Individual.php
@@ -88,7 +88,6 @@ class CRM_Contact_Form_Edit_Individual {
         'objectExists',
         ['CRM_Contact_DAO_Contact', $form->_contactId, 'external_identifier']
       );
-      CRM_Core_ShowHideBlocks::links($form, 'demographics', '', '');
     }
   }
 
diff --git a/civicrm/CRM/Contact/Form/Inline.php b/civicrm/CRM/Contact/Form/Inline.php
index fcc034a7c94ff83f6c4eee410a940eefc3412495..2f551a1cead28caba1ff536e2fc654f702b89379 100644
--- a/civicrm/CRM/Contact/Form/Inline.php
+++ b/civicrm/CRM/Contact/Form/Inline.php
@@ -56,7 +56,7 @@ abstract class CRM_Contact_Form_Inline extends CRM_Core_Form {
    * Common preprocess: fetch contact ID and contact type
    */
   public function preProcess() {
-    $this->_contactId = CRM_Utils_Request::retrieve('cid', 'Positive', $this, TRUE, NULL, $_REQUEST);
+    $this->_contactId = CRM_Utils_Request::retrieve('cid', 'Positive', $this, TRUE);
     $this->assign('contactId', $this->_contactId);
 
     // get contact type and subtype
diff --git a/civicrm/CRM/Contact/Form/Inline/Address.php b/civicrm/CRM/Contact/Form/Inline/Address.php
index 4db79aee78cea3ea11acb0f5c600b247defe4226..e33118c9133fea7b35abb60675251187026e2f1a 100644
--- a/civicrm/CRM/Contact/Form/Inline/Address.php
+++ b/civicrm/CRM/Contact/Form/Inline/Address.php
@@ -58,7 +58,7 @@ class CRM_Contact_Form_Inline_Address extends CRM_Contact_Form_Inline {
    * hence calling parent constructor
    */
   public function __construct() {
-    $locBlockNo = CRM_Utils_Request::retrieve('locno', 'Positive', CRM_Core_DAO::$_nullObject, TRUE, NULL, $_REQUEST);
+    $locBlockNo = CRM_Utils_Request::retrieve('locno', 'Positive', CRM_Core_DAO::$_nullObject, TRUE);
     $name = "Address_{$locBlockNo}";
 
     parent::__construct(NULL, CRM_Core_Action::NONE, 'post', $name);
@@ -70,14 +70,14 @@ class CRM_Contact_Form_Inline_Address extends CRM_Contact_Form_Inline {
   public function preProcess() {
     parent::preProcess();
 
-    $this->_locBlockNo = CRM_Utils_Request::retrieve('locno', 'Positive', $this, TRUE, NULL, $_REQUEST);
+    $this->_locBlockNo = CRM_Utils_Request::retrieve('locno', 'Positive', $this, TRUE);
     $this->assign('blockId', $this->_locBlockNo);
 
     $addressSequence = CRM_Core_BAO_Address::addressSequence();
     $this->assign('addressSequence', $addressSequence);
 
     $this->_values = [];
-    $this->_addressId = CRM_Utils_Request::retrieve('aid', 'Positive', $this, FALSE, NULL, $_REQUEST);
+    $this->_addressId = CRM_Utils_Request::retrieve('aid', 'Positive', $this);
 
     $this->_action = CRM_Core_Action::ADD;
     if ($this->_addressId) {
diff --git a/civicrm/CRM/Contact/Form/Inline/CustomData.php b/civicrm/CRM/Contact/Form/Inline/CustomData.php
index 1d30895c360cd69667c5f3f33df9f784020adb25..d8e92f86bbfbd8dd85f7ac8b835276cfb9fdd2a1 100644
--- a/civicrm/CRM/Contact/Form/Inline/CustomData.php
+++ b/civicrm/CRM/Contact/Form/Inline/CustomData.php
@@ -40,10 +40,10 @@ class CRM_Contact_Form_Inline_CustomData extends CRM_Contact_Form_Inline {
   public function preProcess() {
     parent::preProcess();
 
-    $this->_groupID = CRM_Utils_Request::retrieve('groupID', 'Positive', $this, TRUE, NULL, $_REQUEST);
+    $this->_groupID = CRM_Utils_Request::retrieve('groupID', 'Positive', $this, TRUE, NULL);
     $this->assign('customGroupId', $this->_groupID);
-    $customRecId = CRM_Utils_Request::retrieve('customRecId', 'Positive', $this, FALSE, 1, $_REQUEST);
-    $cgcount = CRM_Utils_Request::retrieve('cgcount', 'Positive', $this, FALSE, 1, $_REQUEST);
+    $customRecId = CRM_Utils_Request::retrieve('customRecId', 'Positive', $this, FALSE, 1);
+    $cgcount = CRM_Utils_Request::retrieve('cgcount', 'Positive', $this, FALSE, 1);
     $subType = CRM_Contact_BAO_Contact::getContactSubType($this->_contactId, ',');
     CRM_Custom_Form_CustomData::preProcess($this, NULL, $subType, $cgcount,
       $this->_contactType, $this->_contactId);
diff --git a/civicrm/CRM/Contact/Form/Search/Advanced.php b/civicrm/CRM/Contact/Form/Search/Advanced.php
index 642fcfdd72cdc7bf072eb9945ef08f21477fabc3..9336eb604f3e64b443fd8a61b08a4382eb4adf1d 100644
--- a/civicrm/CRM/Contact/Form/Search/Advanced.php
+++ b/civicrm/CRM/Contact/Form/Search/Advanced.php
@@ -191,6 +191,14 @@ class CRM_Contact_Form_Search_Advanced extends CRM_Contact_Form_Search {
     ], $defaults);
     $this->normalizeDefaultValues($defaults);
 
+    //991/Subtypes not respected when editing smart group criteria
+    if (!empty($defaults['contact_type']) && !empty($this->_formValues['contact_sub_type'])) {
+      foreach ($this->_formValues['contact_sub_type'] as $subtype) {
+        $basicType = CRM_Contact_BAO_ContactType::getBasicType($subtype);
+        $defaults['contact_type'][$subtype] = $basicType . '__' . $subtype;
+      }
+    }
+
     if ($this->_context === 'amtg') {
       $defaults['task'] = CRM_Contact_Task::GROUP_ADD;
     }
diff --git a/civicrm/CRM/Contact/Form/Search/Criteria.php b/civicrm/CRM/Contact/Form/Search/Criteria.php
index d4d711296cb73d1808567d267e0b00c56a5e643c..dbfb6ebe6a34b161d590b87de9a518cbb33d9223 100644
--- a/civicrm/CRM/Contact/Form/Search/Criteria.php
+++ b/civicrm/CRM/Contact/Form/Search/Criteria.php
@@ -244,7 +244,13 @@ class CRM_Contact_Form_Search_Criteria {
    */
   public static function getSearchFieldMetadata() {
     $fields = [
-      'sort_name' => ['title' => ts('Complete OR Partial Name'), 'template_grouping' => 'basic'],
+      'sort_name' => [
+        'title' => ts('Complete OR Partial Name'),
+        'template_grouping' => 'basic',
+        'template' => 'CRM/Contact/Form/Search/Criteria/Fields/sort_name.tpl',
+      ],
+      'first_name' => ['template_grouping' => 'basic'],
+      'last_name' => ['template_grouping' => 'basic'],
       'email' => ['title' => ts('Complete OR Partial Email'), 'entity' => 'Email', 'template_grouping' => 'basic'],
       'contact_tags' => ['name' => 'contact_tags', 'type' => CRM_Utils_Type::T_INT, 'is_pseudofield' => TRUE, 'template_grouping' => 'basic'],
       'created_date' => ['name' => 'created_date', 'template_grouping' => 'changeLog'],
@@ -605,7 +611,6 @@ class CRM_Contact_Form_Search_Criteria {
 
     foreach ($groupDetails as $key => $group) {
       $_groupTitle[$key] = $group['name'];
-      CRM_Core_ShowHideBlocks::links($form, $group['name'], '', '');
 
       foreach ($group['fields'] as $field) {
         $fieldId = $field['id'];
diff --git a/civicrm/CRM/Contact/Form/Search/Custom.php b/civicrm/CRM/Contact/Form/Search/Custom.php
index dbfb6165f2f69a906c077825ae126b53cf3de885..cfc78ef8355b7a73c972547c30a25363667a4d91 100644
--- a/civicrm/CRM/Contact/Form/Search/Custom.php
+++ b/civicrm/CRM/Contact/Form/Search/Custom.php
@@ -34,7 +34,7 @@ class CRM_Contact_Form_Search_Custom extends CRM_Contact_Form_Search {
       ) = CRM_Contact_BAO_SearchCustom::details($csID, $ssID, $gID);
 
     if (!$this->_customSearchID) {
-      CRM_Core_Error::fatal('Could not get details for custom search.');
+      CRM_Core_Error::statusbounce('Could not get details for custom search.');
     }
 
     // stash this as a hidden element so we can potentially go there if the session
diff --git a/civicrm/CRM/Contact/Form/Search/Custom/Base.php b/civicrm/CRM/Contact/Form/Search/Custom/Base.php
index 25bed0be962c37aecb4a1f502ace9a81cc5411ac..43e59d3ff15ab79337ded785cc30a51a36f9311f 100644
--- a/civicrm/CRM/Contact/Form/Search/Custom/Base.php
+++ b/civicrm/CRM/Contact/Form/Search/Custom/Base.php
@@ -193,7 +193,7 @@ class CRM_Contact_Form_Search_Custom_Base {
 
     foreach ($includeStrings as $string) {
       if (stripos($sql, $string) === FALSE) {
-        CRM_Core_Error::fatal(ts('Could not find \'%1\' string in SQL clause.',
+        CRM_Core_Error::statusBounce(ts('Could not find \'%1\' string in SQL clause.',
           [1 => $string]
         ));
       }
@@ -201,7 +201,7 @@ class CRM_Contact_Form_Search_Custom_Base {
 
     foreach ($excludeStrings as $string) {
       if (preg_match('/(\s' . $string . ')|(' . $string . '\s)/i', $sql)) {
-        CRM_Core_Error::fatal(ts('Found illegal \'%1\' string in SQL clause.',
+        CRM_Core_Error::statusBounce(ts('Found illegal \'%1\' string in SQL clause.',
           [1 => $string]
         ));
       }
diff --git a/civicrm/CRM/Contact/Form/Search/Custom/Group.php b/civicrm/CRM/Contact/Form/Search/Custom/Group.php
index b6e4d59d645193e034ac92f847d0f5cb08ac7630..502d041820df01e155eb008b70a86cdc31affdbf 100644
--- a/civicrm/CRM/Contact/Form/Search/Custom/Group.php
+++ b/civicrm/CRM/Contact/Form/Search/Custom/Group.php
@@ -329,7 +329,7 @@ class CRM_Contact_Form_Search_Custom_Group extends CRM_Contact_Form_Search_Custo
             $ssGroup = new CRM_Contact_DAO_Group();
             $ssGroup->id = $values;
             if (!$ssGroup->find(TRUE)) {
-              CRM_Core_Error::fatal();
+              CRM_Core_Error::statusBounce(ts('Smart group sepecifed in exclude groups is not found in the database'));
             }
             CRM_Contact_BAO_GroupContactCache::load($ssGroup);
 
@@ -389,7 +389,7 @@ WHERE  gcc.group_id = {$ssGroup->id}
           $ssGroup = new CRM_Contact_DAO_Group();
           $ssGroup->id = $values;
           if (!$ssGroup->find(TRUE)) {
-            CRM_Core_Error::fatal();
+            CRM_Core_Error::statusBounce(ts('Smart group sepecifed in include groups is not found in the database'));
           }
           CRM_Contact_BAO_GroupContactCache::load($ssGroup);
 
diff --git a/civicrm/CRM/Contact/Form/Search/Custom/PriceSet.php b/civicrm/CRM/Contact/Form/Search/Custom/PriceSet.php
index 56f5c5e53c387cbf63afe1f4cb8f6206e186d0cc..327bc9004502be2787411fa46fa4ad8af9dc7b5b 100644
--- a/civicrm/CRM/Contact/Form/Search/Custom/PriceSet.php
+++ b/civicrm/CRM/Contact/Form/Search/Custom/PriceSet.php
@@ -185,7 +185,7 @@ AND    p.entity_id    = e.id
     }
 
     if (empty($event)) {
-      CRM_Core_Error::fatal(ts('There are no events with Price Sets'));
+      CRM_Core_Error::statusBounce(ts('There are no events with Price Sets'));
     }
 
     $form->add('select',
diff --git a/civicrm/CRM/Contact/Form/Task/Batch.php b/civicrm/CRM/Contact/Form/Task/Batch.php
index 65a7a873153e53b16b587170d4ec3f063f18837c..c213001a72ee04b8e5b1762e0b708a19b022881a 100644
--- a/civicrm/CRM/Contact/Form/Task/Batch.php
+++ b/civicrm/CRM/Contact/Form/Task/Batch.php
@@ -66,7 +66,7 @@ class CRM_Contact_Form_Task_Batch extends CRM_Contact_Form_Task {
     $ufGroupId = $this->get('ufGroupId');
 
     if (!$ufGroupId) {
-      CRM_Core_Error::fatal('ufGroupId is missing');
+      CRM_Core_Error::statusBounce(ts('ufGroupId is missing'));
     }
     $this->_title = ts('Update multiple contacts') . ' - ' . CRM_Core_BAO_UFGroup::getTitle($ufGroupId);
     CRM_Utils_System::setTitle($this->_title);
diff --git a/civicrm/CRM/Contact/Form/Task/Delete.php b/civicrm/CRM/Contact/Form/Task/Delete.php
index 5b2b088003076a6e33b31c5e16f0f2c15ce58aec..a4996b227f5e42c010aa6959b58e9d11636d401f 100644
--- a/civicrm/CRM/Contact/Form/Task/Delete.php
+++ b/civicrm/CRM/Contact/Form/Task/Delete.php
@@ -68,10 +68,10 @@ class CRM_Contact_Form_Task_Delete extends CRM_Contact_Form_Task {
 
     if ($cid) {
       if (!CRM_Contact_BAO_Contact_Permission::allow($cid, CRM_Core_Permission::EDIT)) {
-        CRM_Core_Error::fatal(ts('You do not have permission to delete this contact. Note: you can delete contacts if you can edit them.'));
+        CRM_Core_Error::statusBounce(ts('You do not have permission to delete this contact. Note: you can delete contacts if you can edit them.'));
       }
       elseif (CRM_Contact_BAO_Contact::checkDomainContact($cid)) {
-        CRM_Core_Error::fatal(ts('This contact is a special one for the contact information associated with the CiviCRM installation for this domain. No one is allowed to delete it because the information is used for special system purposes.'));
+        CRM_Core_Error::statusBounce(ts('This contact is a special one for the contact information associated with the CiviCRM installation for this domain. No one is allowed to delete it because the information is used for special system purposes.'));
       }
 
       $this->_contactIds = [$cid];
diff --git a/civicrm/CRM/Contact/Form/Task/EmailTrait.php b/civicrm/CRM/Contact/Form/Task/EmailTrait.php
index 2abc3f913298ca83b603e9823b6fc5153347de0b..f5fe8739cfb6fc1771a43822cc691f013eabe8da 100644
--- a/civicrm/CRM/Contact/Form/Task/EmailTrait.php
+++ b/civicrm/CRM/Contact/Form/Task/EmailTrait.php
@@ -79,6 +79,15 @@ trait CRM_Contact_Form_Task_EmailTrait {
 
   public $contactEmails = [];
 
+  /**
+   * Contacts form whom emails could not be sent.
+   *
+   * An array of contact ids and the relevant message details.
+   *
+   * @var array
+   */
+  protected $suppressedEmails = [];
+
   /**
    * Getter for isSearchContext.
    *
@@ -174,29 +183,19 @@ trait CRM_Contact_Form_Task_EmailTrait {
       $setDefaults = FALSE;
     }
 
-    $elements = ['to'];
     $this->_allContactIds = $this->_toContactIds = $this->_contactIds;
-    foreach ($elements as $element) {
-      if ($$element->getValue()) {
 
-        foreach ($this->getEmails($$element) as $value) {
-          $contactId = $value['contact_id'];
-          $email = $value['email'];
-          if ($contactId) {
-            switch ($element) {
-              case 'to':
-                $this->_contactIds[] = $this->_toContactIds[] = $contactId;
-                $this->_toContactEmails[] = $email;
-                break;
-
-            }
-
-            $this->_allContactIds[] = $contactId;
-          }
+    if ($to->getValue()) {
+      foreach ($this->getEmails($to) as $value) {
+        $contactId = $value['contact_id'];
+        $email = $value['email'];
+        if ($contactId) {
+          $this->_contactIds[] = $this->_toContactIds[] = $contactId;
+          $this->_toContactEmails[] = $email;
+          $this->_allContactIds[] = $contactId;
         }
-
-        $setDefaults = TRUE;
       }
+      $setDefaults = TRUE;
     }
 
     //get the group of contacts as per selected by user in case of Find Activities
@@ -207,42 +206,24 @@ trait CRM_Contact_Form_Task_EmailTrait {
 
     // check if we need to setdefaults and check for valid contact emails / communication preferences
     if (is_array($this->_allContactIds) && $setDefaults) {
-      $returnProperties = [
-        'sort_name' => 1,
-        'email' => 1,
-        'do_not_email' => 1,
-        'is_deceased' => 1,
-        'on_hold' => 1,
-        'display_name' => 1,
-        'preferred_mail_format' => 1,
-      ];
-
       // get the details for all selected contacts ( to, cc and bcc contacts )
-      list($this->_contactDetails) = CRM_Utils_Token::getTokenDetails($this->_allContactIds,
-        $returnProperties,
-        FALSE,
-        FALSE
-      );
-
-      // make a copy of all contact details
-      $this->_allContactDetails = $this->_contactDetails;
+      $allContactDetails = civicrm_api3('Contact', 'get', [
+        'id' => ['IN' => $this->_allContactIds],
+        'return' => ['sort_name', 'email', 'do_not_email', 'is_deceased', 'on_hold', 'display_name', 'preferred_mail_format'],
+        'options' => ['limit' => 0],
+      ])['values'];
 
       // perform all validations on unique contact Ids
-      foreach (array_unique($this->_allContactIds) as $key => $contactId) {
-        $value = $this->_contactDetails[$contactId];
+      foreach ($allContactDetails as $contactId => $value) {
         if ($value['do_not_email'] || empty($value['email']) || !empty($value['is_deceased']) || $value['on_hold']) {
-          $suppressedEmails++;
-
-          // unset contact details for contacts that we won't be sending email. This is prevent extra computation
-          // during token evaluation etc.
-          unset($this->_contactDetails[$contactId]);
+          $this->setSuppressedEmail($contactId, $value);
         }
         else {
           $email = $value['email'];
 
           // build array's which are used to setdefaults
           if (in_array($contactId, $this->_toContactIds)) {
-            $this->_toContactDetails[$contactId] = $this->_contactDetails[$contactId];
+            $this->_toContactDetails[$contactId] = $this->_contactDetails[$contactId] = $value;
             // If a particular address has been specified as the default, use that instead of contact's primary email
             if (!empty($this->_toEmail) && $this->_toEmail['contact_id'] == $contactId) {
               $email = $this->_toEmail['email'];
@@ -262,7 +243,7 @@ trait CRM_Contact_Form_Task_EmailTrait {
 
     $this->assign('toContact', json_encode($toArray));
 
-    $this->assign('suppressedEmails', $suppressedEmails);
+    $this->assign('suppressedEmails', count($this->suppressedEmails));
 
     $this->assign('totalSelectedContacts', count($this->_contactIds));
 
@@ -360,6 +341,7 @@ trait CRM_Contact_Form_Task_EmailTrait {
    * @throws \CRM_Core_Exception
    * @throws \CiviCRM_API3_Exception
    * @throws \Civi\API\Exception\UnauthorizedException
+   * @throws \API_Exception
    */
   public function postProcess() {
     $this->bounceIfSimpleMailLimitExceeded(count($this->_contactIds));
@@ -404,7 +386,6 @@ trait CRM_Contact_Form_Task_EmailTrait {
     // If we have had a contact email used here the value returned from the line above will be the
     // numerical key where as $from for use in the sendEmail in Activity needs to be of format of "To Name" <toemailaddress>
     $from = CRM_Utils_Mail::formatFromAddress($from);
-    $subject = $formValues['subject'];
 
     $ccArray = $formValues['cc_id'] ? explode(',', $formValues['cc_id']) : [];
     $cc = $this->getEmailString($ccArray);
@@ -414,21 +395,8 @@ trait CRM_Contact_Form_Task_EmailTrait {
     $bcc = $this->getEmailString($bccArray);
     $additionalDetails .= empty($bccArray) ? '' : "\nbcc : " . $this->getEmailUrlString($bccArray);
 
-    // CRM-5916: prepend case id hash to CiviCase-originating emails’ subjects
-    if (isset($this->_caseId) && is_numeric($this->_caseId)) {
-      $hash = substr(sha1(CIVICRM_SITE_KEY . $this->_caseId), 0, 7);
-      $subject = "[case #$hash] $subject";
-    }
-
-    $attachments = [];
-    CRM_Core_BAO_File::formatAttachment($formValues,
-      $attachments,
-      NULL, NULL
-    );
-
     // format contact details array to handle multiple emails from same contact
     $formattedContactDetails = [];
-    $tempEmails = [];
     foreach ($this->_contactIds as $key => $contactId) {
       // if we dont have details on this contactID, we should ignore
       // potentially this is due to the contact not wanting to receive email
@@ -438,69 +406,33 @@ trait CRM_Contact_Form_Task_EmailTrait {
       $email = $this->_toContactEmails[$key];
       // prevent duplicate emails if same email address is selected CRM-4067
       // we should allow same emails for different contacts
-      $emailKey = "{$contactId}::{$email}";
-      if (!in_array($emailKey, $tempEmails)) {
-        $tempEmails[] = $emailKey;
-        $details = $this->_contactDetails[$contactId];
-        $details['email'] = $email;
-        unset($details['email_id']);
-        $formattedContactDetails[] = $details;
-      }
-    }
-
-    $contributionIds = [];
-    if ($this->getVar('_contributionIds')) {
-      $contributionIds = $this->getVar('_contributionIds');
+      $details = $this->_contactDetails[$contactId];
+      $details['email'] = $email;
+      unset($details['email_id']);
+      $formattedContactDetails["{$contactId}::{$email}"] = $details;
     }
 
     // send the mail
     list($sent, $activityId) = CRM_Activity_BAO_Activity::sendEmail(
       $formattedContactDetails,
-      $subject,
+      $this->getSubject($formValues['subject']),
       $formValues['text_message'],
       $formValues['html_message'],
       NULL,
       NULL,
       $from,
-      $attachments,
+      $this->getAttachments($formValues),
       $cc,
       $bcc,
       array_keys($this->_toContactDetails),
       $additionalDetails,
-      $contributionIds,
+      $this->getVar('_contributionIds') ?? [],
       CRM_Utils_Array::value('campaign_id', $formValues),
       $this->getVar('_caseId')
     );
 
-    $followupStatus = '';
     if ($sent) {
-      $followupActivity = NULL;
-      if (!empty($formValues['followup_activity_type_id'])) {
-        $params['followup_activity_type_id'] = $formValues['followup_activity_type_id'];
-        $params['followup_activity_subject'] = $formValues['followup_activity_subject'];
-        $params['followup_date'] = $formValues['followup_date'];
-        $params['target_contact_id'] = $this->_contactIds;
-        $params['followup_assignee_contact_id'] = explode(',', $formValues['followup_assignee_contact_id']);
-        $followupActivity = CRM_Activity_BAO_Activity::createFollowupActivity($activityId, $params);
-        $followupStatus = ts('A followup activity has been scheduled.');
-
-        if (Civi::settings()->get('activity_assignee_notification')) {
-          if ($followupActivity) {
-            $mailToFollowupContacts = [];
-            $assignee = [$followupActivity->id];
-            $assigneeContacts = CRM_Activity_BAO_ActivityAssignment::getAssigneeNames($assignee, TRUE, FALSE);
-            foreach ($assigneeContacts as $values) {
-              $mailToFollowupContacts[$values['email']] = $values;
-            }
-
-            $sentFollowup = CRM_Activity_BAO_Activity::sendToAssignee($followupActivity, $mailToFollowupContacts);
-            if ($sentFollowup) {
-              $followupStatus .= '<br />' . ts('A copy of the follow-up activity has also been sent to follow-up assignee contacts(s).');
-            }
-          }
-        }
-      }
-
+      $followupStatus = $this->createFollowUpActivities($formValues, $activityId);
       $count_success = count($this->_toContactDetails);
       CRM_Core_Session::setStatus(ts('One message was sent successfully. ', [
         'plural' => '%count messages were sent successfully. ',
@@ -508,23 +440,10 @@ trait CRM_Contact_Form_Task_EmailTrait {
       ]) . $followupStatus, ts('Message Sent', ['plural' => 'Messages Sent', 'count' => $count_success]), 'success');
     }
 
-    // Display the name and number of contacts for those email is not sent.
-    // php 5.4 throws out a notice since the values of these below arrays are arrays.
-    // the behavior is not documented in the php manual, but it does the right thing
-    // suppressing the notices to get things in good shape going forward
-    $emailsNotSent = @array_diff_assoc($this->_allContactDetails, $this->_contactDetails);
-
-    if ($emailsNotSent) {
-      $not_sent = [];
-      foreach ($emailsNotSent as $contactId => $values) {
-        $displayName = $values['display_name'];
-        $email = $values['email'];
-        $contactViewUrl = CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid=$contactId");
-        $not_sent[] = "<a href='$contactViewUrl' title='$email'>$displayName</a>" . ($values['on_hold'] ? '(' . ts('on hold') . ')' : '');
-      }
-      $status = '(' . ts('because no email address on file or communication preferences specify DO NOT EMAIL or Contact is deceased or Primary email address is On Hold') . ')<ul><li>' . implode('</li><li>', $not_sent) . '</li></ul>';
+    if (!empty($this->suppressedEmails)) {
+      $status = '(' . ts('because no email address on file or communication preferences specify DO NOT EMAIL or Contact is deceased or Primary email address is On Hold') . ')<ul><li>' . implode('</li><li>', $this->suppressedEmails) . '</li></ul>';
       CRM_Core_Session::setStatus($status, ts('One Message Not Sent', [
-        'count' => count($emailsNotSent),
+        'count' => count($this->suppressedEmails),
         'plural' => '%count Messages Not Sent',
       ]), 'info');
     }
@@ -647,4 +566,91 @@ trait CRM_Contact_Form_Task_EmailTrait {
     return $urlString;
   }
 
+  /**
+   * Set the emails that are not to be sent out.
+   *
+   * @param int $contactID
+   * @param array $values
+   */
+  protected function setSuppressedEmail($contactID, $values) {
+    $contactViewUrl = CRM_Utils_System::url('civicrm/contact/view', 'reset=1&cid=' . $contactID);
+    $this->suppressedEmails[$contactID] = "<a href='$contactViewUrl' title='{$values['email']}'>{$values['display_name']}</a>" . ($values['on_hold'] ? '(' . ts('on hold') . ')' : '');
+  }
+
+  /**
+   * Get any attachments.
+   *
+   * @param array $formValues
+   *
+   * @return array
+   */
+  protected function getAttachments(array $formValues): array {
+    $attachments = [];
+    CRM_Core_BAO_File::formatAttachment($formValues,
+      $attachments,
+      NULL, NULL
+    );
+    return $attachments;
+  }
+
+  /**
+   * Get the subject for the message.
+   *
+   * The case handling should possibly be on the case form.....
+   *
+   * @param string $subject
+   *
+   * @return string
+   */
+  protected function getSubject(string $subject):string {
+    // CRM-5916: prepend case id hash to CiviCase-originating emails’ subjects
+    if (isset($this->_caseId) && is_numeric($this->_caseId)) {
+      $hash = substr(sha1(CIVICRM_SITE_KEY . $this->_caseId), 0, 7);
+      $subject = "[case #$hash] $subject";
+    }
+    return $subject;
+  }
+
+  /**
+   * Create any follow up activities.
+   *
+   * @param array $formValues
+   * @param int $activityId
+   *
+   * @return string
+   *
+   * @throws \CRM_Core_Exception
+   */
+  protected function createFollowUpActivities($formValues, $activityId): string {
+    $params = [];
+    $followupStatus = '';
+    $followupActivity = NULL;
+    if (!empty($formValues['followup_activity_type_id'])) {
+      $params['followup_activity_type_id'] = $formValues['followup_activity_type_id'];
+      $params['followup_activity_subject'] = $formValues['followup_activity_subject'];
+      $params['followup_date'] = $formValues['followup_date'];
+      $params['target_contact_id'] = $this->_contactIds;
+      $params['followup_assignee_contact_id'] = explode(',', $formValues['followup_assignee_contact_id']);
+      $followupActivity = CRM_Activity_BAO_Activity::createFollowupActivity($activityId, $params);
+      $followupStatus = ts('A followup activity has been scheduled.');
+
+      if (Civi::settings()->get('activity_assignee_notification')) {
+        if ($followupActivity) {
+          $mailToFollowupContacts = [];
+          $assignee = [$followupActivity->id];
+          $assigneeContacts = CRM_Activity_BAO_ActivityAssignment::getAssigneeNames($assignee, TRUE, FALSE);
+          foreach ($assigneeContacts as $values) {
+            $mailToFollowupContacts[$values['email']] = $values;
+          }
+
+          $sentFollowup = CRM_Activity_BAO_Activity::sendToAssignee($followupActivity, $mailToFollowupContacts);
+          if ($sentFollowup) {
+            $followupStatus .= '<br />' . ts('A copy of the follow-up activity has also been sent to follow-up assignee contacts(s).');
+          }
+        }
+      }
+    }
+    return $followupStatus;
+  }
+
 }
diff --git a/civicrm/CRM/Contact/Form/Task/Map.php b/civicrm/CRM/Contact/Form/Task/Map.php
index d9f1d339c95ec9238447cc56c6086355eb830fc9..82b79608378261dd9b4ccbc77c93754f4f75804f 100644
--- a/civicrm/CRM/Contact/Form/Task/Map.php
+++ b/civicrm/CRM/Contact/Form/Task/Map.php
@@ -58,7 +58,7 @@ class CRM_Contact_Form_Task_Map extends CRM_Contact_Form_Task {
         // CRM-11766
         $profileIDs = CRM_Profile_Page_Listings::getProfileContact($profileGID);
         if (!in_array($cid, $profileIDs)) {
-          CRM_Core_Error::fatal();
+          CRM_Core_Error::statusBounce(ts('Contact not found when building list of contacts in the profile'));
         }
       }
       elseif ($context) {
diff --git a/civicrm/CRM/Contact/Form/Task/SaveSearch/Update.php b/civicrm/CRM/Contact/Form/Task/SaveSearch/Update.php
index 72a4b81dcd4ed9e0b0801bfcef4a0b7b9b10117f..76505b00ab254e704c2a2fd7f975de3e286d059c 100644
--- a/civicrm/CRM/Contact/Form/Task/SaveSearch/Update.php
+++ b/civicrm/CRM/Contact/Form/Task/SaveSearch/Update.php
@@ -51,7 +51,7 @@ class CRM_Contact_Form_Task_SaveSearch_Update extends CRM_Contact_Form_Task_Save
       $types = explode(CRM_Core_DAO::VALUE_SEPARATOR,
         substr($defaults['group_type'], 1, -1)
       );
-      $defaults['group_type'] = array();
+      $defaults['group_type'] = [];
       foreach ($types as $type) {
         $defaults['group_type'][$type] = 1;
       }
diff --git a/civicrm/CRM/Contact/Import/Form/Preview.php b/civicrm/CRM/Contact/Import/Form/Preview.php
index 5c268de118023bbc3d821fa897b60446031d941a..77ddd7afa22a1ed5f3a20663920baf8c103c39cf 100644
--- a/civicrm/CRM/Contact/Import/Form/Preview.php
+++ b/civicrm/CRM/Contact/Import/Form/Preview.php
@@ -151,37 +151,9 @@ class CRM_Contact_Import_Form_Preview extends CRM_Import_Form_Preview {
       }
     }
 
-    $path = "_qf_MapField_display=true";
-    $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String', $form);
-    if (CRM_Utils_Rule::qfKey($qfKey)) {
-      $path .= "&qfKey=$qfKey";
-    }
-
-    $previousURL = CRM_Utils_System::url('civicrm/import/contact', $path, FALSE, NULL, FALSE);
-    $cancelURL = CRM_Utils_System::url('civicrm/import/contact', 'reset=1');
-
-    $buttons = array(
-      array(
-        'type' => 'back',
-        'name' => ts('Previous'),
-        'js' => array('onclick' => "location.href='{$previousURL}'; return false;"),
-      ),
-      array(
-        'type' => 'next',
-        'name' => ts('Import Now'),
-        'spacing' => '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
-        'isDefault' => TRUE,
-      ),
-      array(
-        'type' => 'cancel',
-        'name' => ts('Cancel'),
-        'js' => array('onclick' => "location.href='{$cancelURL}'; return false;"),
-      ),
-    );
-
-    $this->addButtons($buttons);
-
     $this->addFormRule(array('CRM_Contact_Import_Form_Preview', 'formRule'), $this);
+
+    parent::buildQuickForm();
   }
 
   /**
@@ -197,7 +169,7 @@ class CRM_Contact_Import_Form_Preview extends CRM_Import_Form_Preview {
    *   list of errors to be posted back to the form
    */
   public static function formRule($fields, $files, $self) {
-    $errors = array();
+    $errors = [];
     $invalidTagName = $invalidGroupName = FALSE;
 
     if (!empty($fields['newTagName'])) {
@@ -291,7 +263,7 @@ class CRM_Contact_Import_Form_Preview extends CRM_Import_Form_Preview {
     // check if there is any error occurred
     $errorStack = CRM_Core_Error::singleton();
     $errors = $errorStack->getErrors();
-    $errorMessage = array();
+    $errorMessage = [];
 
     if (is_array($errors)) {
       foreach ($errors as $key => $value) {
@@ -345,14 +317,14 @@ class CRM_Contact_Import_Form_Preview extends CRM_Import_Form_Preview {
 
     $mapper = $this->controller->exportValue('MapField', 'mapper');
 
-    $mapperKeys = array();
-    $mapperLocTypes = array();
-    $mapperPhoneTypes = array();
-    $mapperRelated = array();
-    $mapperRelatedContactType = array();
-    $mapperRelatedContactDetails = array();
-    $mapperRelatedContactLocType = array();
-    $mapperRelatedContactPhoneType = array();
+    $mapperKeys = [];
+    $mapperLocTypes = [];
+    $mapperPhoneTypes = [];
+    $mapperRelated = [];
+    $mapperRelatedContactType = [];
+    $mapperRelatedContactDetails = [];
+    $mapperRelatedContactLocType = [];
+    $mapperRelatedContactPhoneType = [];
 
     foreach ($mapper as $key => $value) {
       $mapperKeys[$key] = $mapper[$key][0];
@@ -403,7 +375,7 @@ class CRM_Contact_Import_Form_Preview extends CRM_Import_Form_Preview {
     $phoneTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Phone', 'phone_type_id');
 
     foreach ($mapper as $key => $value) {
-      $header = array();
+      $header = [];
       list($id, $first, $second) = explode('_', $mapper[$key][0]);
       if (($first == 'a' && $second == 'b') || ($first == 'b' && $second == 'a')) {
         $relationType = new CRM_Contact_DAO_RelationshipType();
@@ -475,7 +447,7 @@ class CRM_Contact_Import_Form_Preview extends CRM_Import_Form_Preview {
     }
 
     if (is_array($groups)) {
-      $groupAdditions = array();
+      $groupAdditions = [];
       foreach ($groups as $groupId) {
         $addCount = CRM_Contact_BAO_GroupContact::addContactsToGroup($contactIds, $groupId);
         if (!empty($relatedContactIds)) {
@@ -513,15 +485,14 @@ class CRM_Contact_Import_Form_Preview extends CRM_Import_Form_Preview {
         'description' => $newTagDesc,
         'is_active' => TRUE,
       );
-      $id = array();
-      $addedTag = CRM_Core_BAO_Tag::add($tagParams, $id);
+      $addedTag = CRM_Core_BAO_Tag::add($tagParams);
       $tag[$addedTag->id] = 1;
     }
     //add Tag to Import
 
     if (is_array($tag)) {
 
-      $tagAdditions = array();
+      $tagAdditions = [];
       foreach ($tag as $tagId => $val) {
         $addTagCount = CRM_Core_BAO_EntityTag::addContactsToTag($contactIds, $tagId);
         if (!empty($relatedContactIds)) {
@@ -556,7 +527,7 @@ class CRM_Contact_Import_Form_Preview extends CRM_Import_Form_Preview {
 
     $errorStack = CRM_Core_Error::singleton();
     $errors = $errorStack->getErrors();
-    $errorMessage = array();
+    $errorMessage = [];
 
     if (is_array($errors)) {
       foreach ($errors as $key => $value) {
diff --git a/civicrm/CRM/Contact/Import/ImportJob.php b/civicrm/CRM/Contact/Import/ImportJob.php
index 81e875b502a2e86abf9e093730a12215dd1684ce..7940288294753483f09debf74bc25282075c5796 100644
--- a/civicrm/CRM/Contact/Import/ImportJob.php
+++ b/civicrm/CRM/Contact/Import/ImportJob.php
@@ -42,7 +42,7 @@ class CRM_Contact_Import_ImportJob {
   protected $_allTags;
 
   protected $_mapper;
-  protected $_mapperKeys = array();
+  protected $_mapperKeys = [];
   protected $_mapFields;
 
   protected $_parser;
@@ -52,7 +52,7 @@ class CRM_Contact_Import_ImportJob {
    * @param null $createSql
    * @param bool $createTable
    *
-   * @throws Exception
+   * @throws \CRM_Core_Exception
    */
   public function __construct($tableName = NULL, $createSql = NULL, $createTable = FALSE) {
     $dao = new CRM_Core_DAO();
@@ -60,7 +60,7 @@ class CRM_Contact_Import_ImportJob {
 
     if ($createTable) {
       if (!$createSql) {
-        CRM_Core_Error::fatal('Either an existing table name or an SQL query to build one are required');
+        throw new CRM_Core_Exception(ts('Either an existing table name or an SQL query to build one are required'));
       }
 
       // FIXME: we should regen this table's name if it exists rather than drop it
@@ -72,7 +72,7 @@ class CRM_Contact_Import_ImportJob {
     }
 
     if (!$tableName) {
-      CRM_Core_Error::fatal('Import Table is required.');
+      throw new CRM_Core_Exception(ts('Import Table is required.'));
     }
 
     $this->_tableName = $tableName;
@@ -124,7 +124,7 @@ class CRM_Contact_Import_ImportJob {
    */
   public function runImport(&$form, $timeout = 55) {
     $mapper = $this->_mapper;
-    $mapperFields = array();
+    $mapperFields = [];
     $parserParameters = CRM_Contact_Import_Parser_Contact::getParameterForParser(count($mapper));
     $phoneTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Phone', 'phone_type_id');
     $imProviders = CRM_Core_PseudoConstant::get('CRM_Core_DAO_IM', 'provider_id');
@@ -294,7 +294,7 @@ class CRM_Contact_Import_ImportJob {
 
     if ($newGroupName) {
       /* Create a new group */
-      $newGroupType = $newGroupType ?? array();
+      $newGroupType = $newGroupType ?? [];
       $gParams = array(
         'title' => $newGroupName,
         'description' => $newGroupDesc,
@@ -306,7 +306,7 @@ class CRM_Contact_Import_ImportJob {
     }
 
     if (is_array($this->_groups)) {
-      $groupAdditions = array();
+      $groupAdditions = [];
       foreach ($this->_groups as $groupId) {
         $addCount = CRM_Contact_BAO_GroupContact::addContactsToGroup($contactIds, $groupId);
         $totalCount = $addCount[1];
@@ -355,14 +355,13 @@ class CRM_Contact_Import_ImportJob {
         'is_selectable' => TRUE,
         'used_for' => 'civicrm_contact',
       );
-      $id = array();
-      $addedTag = CRM_Core_BAO_Tag::add($tagParams, $id);
+      $addedTag = CRM_Core_BAO_Tag::add($tagParams);
       $this->_tag[$addedTag->id] = 1;
     }
     //add Tag to Import
 
     if (is_array($this->_tag)) {
-      $tagAdditions = array();
+      $tagAdditions = [];
       foreach ($this->_tag as $tagId => $val) {
         $addTagCount = CRM_Core_BAO_EntityTag::addEntitiesToTag($contactIds, $tagId, 'civicrm_contact', FALSE);
         $totalTagCount = $addTagCount[1];
diff --git a/civicrm/CRM/Contact/Import/Parser.php b/civicrm/CRM/Contact/Import/Parser.php
index 80d10ff5865d2dd8c3a294e695f5717a8f7af5a3..4a4034037ac96f51be83b347b39518bc6af29970 100644
--- a/civicrm/CRM/Contact/Import/Parser.php
+++ b/civicrm/CRM/Contact/Import/Parser.php
@@ -548,6 +548,11 @@ abstract class CRM_Contact_Import_Parser extends CRM_Import_Parser {
               'website_type_id' => $this->_activeFields[$i]->_relatedContactWebsiteType,
             ];
           }
+          elseif (empty($this->_activeFields[$i]->_value) && isset($this->_activeFields[$i]->_relatedContactLocType)) {
+            if (empty($params[$this->_activeFields[$i]->_related][$this->_activeFields[$i]->_relatedContactDetails])) {
+              $params[$this->_activeFields[$i]->_related][$this->_activeFields[$i]->_relatedContactDetails] = [];
+            }
+          }
           else {
             $params[$this->_activeFields[$i]->_related][$this->_activeFields[$i]->_relatedContactDetails] = $this->_activeFields[$i]->_value;
           }
@@ -832,6 +837,7 @@ abstract class CRM_Contact_Import_Parser extends CRM_Import_Parser {
               $this->formatLocationBlock($value, $formatted);
             }
             else {
+              // @todo - this is still reachable - e.g. import with related contact info like firstname,lastname,spouse-first-name,spouse-last-name,spouse-home-phone
               CRM_Core_Error::deprecatedFunctionWarning('this is not expected to be reachable now');
               $this->formatContactParameters($value, $formatted);
             }
diff --git a/civicrm/CRM/Contact/Import/Parser/Contact.php b/civicrm/CRM/Contact/Import/Parser/Contact.php
index bb4be04495888a35366838bcae94bfa870a9060c..760d16059b8df74c45976bc8d5615d2e74765e66 100644
--- a/civicrm/CRM/Contact/Import/Parser/Contact.php
+++ b/civicrm/CRM/Contact/Import/Parser/Contact.php
@@ -1167,7 +1167,10 @@ class CRM_Contact_Import_Parser_Contact extends CRM_Contact_Import_Parser {
         /* validate the data against the CF type */
 
         if ($value) {
-          if ($customFields[$customFieldID]['data_type'] == 'Date') {
+          $dataType = $customFields[$customFieldID]['data_type'];
+          $htmlType = $customFields[$customFieldID]['html_type'];
+          $isSerialized = CRM_Core_BAO_CustomField::isSerialized($customFields[$customFieldID]);
+          if ($dataType == 'Date') {
             if (array_key_exists($customFieldID, $addressCustomFields) && CRM_Utils_Date::convertToDefaultDate($params[$key][0], $dateType, $key)) {
               $value = $params[$key][0][$key];
             }
@@ -1178,29 +1181,26 @@ class CRM_Contact_Import_Parser_Contact extends CRM_Contact_Import_Parser {
               self::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage);
             }
           }
-          elseif ($customFields[$customFieldID]['data_type'] == 'Boolean') {
+          elseif ($dataType == 'Boolean') {
             if (CRM_Utils_String::strtoboolstr($value) === FALSE) {
               self::addToErrorMsg($customFields[$customFieldID]['label'] . '::' . $customFields[$customFieldID]['groupTitle'], $errorMessage);
             }
           }
           // need not check for label filed import
-          $htmlType = [
+          $selectHtmlTypes = [
             'CheckBox',
-            'Multi-Select',
             'Select',
             'Radio',
-            'Multi-Select State/Province',
-            'Multi-Select Country',
           ];
-          if (!in_array($customFields[$customFieldID]['html_type'], $htmlType) || $customFields[$customFieldID]['data_type'] == 'Boolean' || $customFields[$customFieldID]['data_type'] == 'ContactReference') {
-            $valid = CRM_Core_BAO_CustomValue::typecheck($customFields[$customFieldID]['data_type'], $value);
+          if ((!$isSerialized && !in_array($htmlType, $selectHtmlTypes)) || $dataType == 'Boolean' || $dataType == 'ContactReference') {
+            $valid = CRM_Core_BAO_CustomValue::typecheck($dataType, $value);
             if (!$valid) {
               self::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage);
             }
           }
 
           // check for values for custom fields for checkboxes and multiselect
-          if ($customFields[$customFieldID]['html_type'] == 'CheckBox' || $customFields[$customFieldID]['html_type'] == 'Multi-Select') {
+          if ($isSerialized) {
             $value = trim($value);
             $value = str_replace('|', ',', $value);
             $mulValues = explode(',', $value);
@@ -1222,7 +1222,7 @@ class CRM_Contact_Import_Parser_Contact extends CRM_Contact_Import_Parser {
               }
             }
           }
-          elseif ($customFields[$customFieldID]['html_type'] == 'Select' || ($customFields[$customFieldID]['html_type'] == 'Radio' && $customFields[$customFieldID]['data_type'] != 'Boolean')) {
+          elseif ($htmlType == 'Select' || ($htmlType == 'Radio' && $dataType != 'Boolean')) {
             $customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldID, TRUE);
             $flag = FALSE;
             foreach ($customOption as $v2) {
@@ -1234,7 +1234,7 @@ class CRM_Contact_Import_Parser_Contact extends CRM_Contact_Import_Parser {
               self::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage);
             }
           }
-          elseif ($customFields[$customFieldID]['html_type'] == 'Multi-Select State/Province') {
+          elseif ($isSerialized && $dataType === 'StateProvince') {
             $mulValues = explode(',', $value);
             foreach ($mulValues as $stateValue) {
               if ($stateValue) {
@@ -1247,7 +1247,7 @@ class CRM_Contact_Import_Parser_Contact extends CRM_Contact_Import_Parser {
               }
             }
           }
-          elseif ($customFields[$customFieldID]['html_type'] == 'Multi-Select Country') {
+          elseif ($isSerialized && $dataType == 'Country') {
             $mulValues = explode(',', $value);
             foreach ($mulValues as $countryValue) {
               if ($countryValue) {
diff --git a/civicrm/CRM/Contact/Page/Inline/Actions.php b/civicrm/CRM/Contact/Page/Inline/Actions.php
index 136e8e71ed536782bfe666f60c5529d105d43b8a..ef91f017d801096c49f91b8f9bb9267804f5dc09 100644
--- a/civicrm/CRM/Contact/Page/Inline/Actions.php
+++ b/civicrm/CRM/Contact/Page/Inline/Actions.php
@@ -26,7 +26,7 @@ class CRM_Contact_Page_Inline_Actions extends CRM_Core_Page {
    * This method is called after the page is created.
    */
   public function run() {
-    $contactId = CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullObject, TRUE, NULL, $_REQUEST);
+    $contactId = CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullObject, TRUE);
 
     $this->assign('contactId', $contactId);
     $this->assign('actionsMenuList', CRM_Contact_BAO_Contact::contextMenu($contactId));
diff --git a/civicrm/CRM/Contact/Page/Inline/Address.php b/civicrm/CRM/Contact/Page/Inline/Address.php
index 99ee03591cdbe0492e83a823bae84656ac0db1bb..ce179303e0ca6389cadeb3185febb5e7b6a864ac 100644
--- a/civicrm/CRM/Contact/Page/Inline/Address.php
+++ b/civicrm/CRM/Contact/Page/Inline/Address.php
@@ -27,9 +27,9 @@ class CRM_Contact_Page_Inline_Address extends CRM_Core_Page {
    */
   public function run() {
     // get the emails for this contact
-    $contactId = CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullObject, TRUE, NULL, $_REQUEST);
-    $locBlockNo = CRM_Utils_Request::retrieve('locno', 'Positive', CRM_Core_DAO::$_nullObject, TRUE, NULL, $_REQUEST);
-    $addressId = CRM_Utils_Request::retrieve('aid', 'Positive', CRM_Core_DAO::$_nullObject, FALSE, NULL, $_REQUEST);
+    $contactId = CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullObject, TRUE);
+    $locBlockNo = CRM_Utils_Request::retrieve('locno', 'Positive', CRM_Core_DAO::$_nullObject, TRUE);
+    $addressId = CRM_Utils_Request::retrieve('aid', 'Positive');
 
     $address = [];
     if ($addressId > 0) {
diff --git a/civicrm/CRM/Contact/Page/Inline/ContactInfo.php b/civicrm/CRM/Contact/Page/Inline/ContactInfo.php
index 9243a5fde90d5789d018b4ae57273650e3d42e49..9dad262779deb4049b0bc878f56e6f27199ead36 100644
--- a/civicrm/CRM/Contact/Page/Inline/ContactInfo.php
+++ b/civicrm/CRM/Contact/Page/Inline/ContactInfo.php
@@ -27,7 +27,7 @@ class CRM_Contact_Page_Inline_ContactInfo extends CRM_Core_Page {
    */
   public function run() {
     // get the emails for this contact
-    $contactId = CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullObject, TRUE, NULL, $_REQUEST);
+    $contactId = CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullObject, TRUE);
 
     $params = ['id' => $contactId];
 
diff --git a/civicrm/CRM/Contact/Page/Inline/ContactName.php b/civicrm/CRM/Contact/Page/Inline/ContactName.php
index 4e7e7a960c2510844e7df2d9169bbe67d75ac002..aef76c6834e3b935186d1b2a95c3f0670059ea04 100644
--- a/civicrm/CRM/Contact/Page/Inline/ContactName.php
+++ b/civicrm/CRM/Contact/Page/Inline/ContactName.php
@@ -27,7 +27,7 @@ class CRM_Contact_Page_Inline_ContactName extends CRM_Core_Page {
    */
   public function run() {
     // get the emails for this contact
-    $contactId = CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullObject, TRUE, NULL, $_REQUEST);
+    $contactId = CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullObject, TRUE);
 
     $isDeleted = (bool) CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $contactId, 'is_deleted');
 
diff --git a/civicrm/CRM/Contact/Page/Inline/CustomData.php b/civicrm/CRM/Contact/Page/Inline/CustomData.php
index a2ffabdfd78d7908be99fcd2ffbcbaa5d0694889..8dd50fd7867b2fc1d71916b73562205948075729 100644
--- a/civicrm/CRM/Contact/Page/Inline/CustomData.php
+++ b/civicrm/CRM/Contact/Page/Inline/CustomData.php
@@ -27,10 +27,10 @@ class CRM_Contact_Page_Inline_CustomData extends CRM_Core_Page {
    */
   public function run() {
     // get the emails for this contact
-    $contactId = CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullObject, TRUE, NULL, $_REQUEST);
-    $cgId = CRM_Utils_Request::retrieve('groupID', 'Positive', CRM_Core_DAO::$_nullObject, TRUE, NULL, $_REQUEST);
-    $customRecId = CRM_Utils_Request::retrieve('customRecId', 'Positive', CRM_Core_DAO::$_nullObject, FALSE, 1, $_REQUEST);
-    $cgcount = CRM_Utils_Request::retrieve('cgcount', 'Positive', CRM_Core_DAO::$_nullObject, FALSE, 1, $_REQUEST);
+    $contactId = CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullObject, TRUE);
+    $cgId = CRM_Utils_Request::retrieve('groupID', 'Positive', CRM_Core_DAO::$_nullObject, TRUE);
+    $customRecId = CRM_Utils_Request::retrieve('customRecId', 'Positive', CRM_Core_DAO::$_nullObject, FALSE, 1);
+    $cgcount = CRM_Utils_Request::retrieve('cgcount', 'Positive', CRM_Core_DAO::$_nullObject, FALSE, 1);
 
     //custom groups Inline
     $entityType = CRM_Contact_BAO_Contact::getContactType($contactId);
diff --git a/civicrm/CRM/Contact/Page/Inline/Demographics.php b/civicrm/CRM/Contact/Page/Inline/Demographics.php
index b211df4dcf0eb74c81db0895a2ef736eae3c5b91..e3a714872fb88a335f404ab0870d049e1a90f525 100644
--- a/civicrm/CRM/Contact/Page/Inline/Demographics.php
+++ b/civicrm/CRM/Contact/Page/Inline/Demographics.php
@@ -27,7 +27,7 @@ class CRM_Contact_Page_Inline_Demographics extends CRM_Core_Page {
    */
   public function run() {
     // get the emails for this contact
-    $contactId = CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullObject, TRUE, NULL, $_REQUEST);
+    $contactId = CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullObject, TRUE);
 
     $params = ['id' => $contactId];
 
diff --git a/civicrm/CRM/Contact/Page/Inline/Email.php b/civicrm/CRM/Contact/Page/Inline/Email.php
index 8b4fed55d78a1e114ccd9d8969f08986dc162fb4..7ca97943c11b48c65e85c73f13ef80ff8fcf76ba 100644
--- a/civicrm/CRM/Contact/Page/Inline/Email.php
+++ b/civicrm/CRM/Contact/Page/Inline/Email.php
@@ -27,7 +27,7 @@ class CRM_Contact_Page_Inline_Email extends CRM_Core_Page {
    */
   public function run() {
     // get the emails for this contact
-    $contactId = CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullObject, TRUE, NULL, $_REQUEST);
+    $contactId = CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullObject, TRUE);
 
     $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id', ['labelColumn' => 'display_name']);
 
diff --git a/civicrm/CRM/Contact/Page/Inline/IM.php b/civicrm/CRM/Contact/Page/Inline/IM.php
index 8882d5ba6d429d73cb1c1160d4b577641f163848..e20782a3265a9f1447ab5c5b20c9dfa405be4d02 100644
--- a/civicrm/CRM/Contact/Page/Inline/IM.php
+++ b/civicrm/CRM/Contact/Page/Inline/IM.php
@@ -27,7 +27,7 @@ class CRM_Contact_Page_Inline_IM extends CRM_Core_Page {
    */
   public function run() {
     // get the emails for this contact
-    $contactId = CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullObject, TRUE, NULL, $_REQUEST);
+    $contactId = CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullObject, TRUE);
 
     $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id', ['labelColumn' => 'display_name']);
     $IMProviders = CRM_Core_PseudoConstant::get('CRM_Core_DAO_IM', 'provider_id');
diff --git a/civicrm/CRM/Contact/Page/View/Relationship.php b/civicrm/CRM/Contact/Page/View/Relationship.php
index ed03a112dd5184387aa2f0baae368989be3ccc63..c1d88ae79c015d2cdb1a45a5445c995141465112 100644
--- a/civicrm/CRM/Contact/Page/View/Relationship.php
+++ b/civicrm/CRM/Contact/Page/View/Relationship.php
@@ -104,7 +104,7 @@ class CRM_Contact_Page_View_Relationship extends CRM_Core_Page {
     );
 
     $session = CRM_Core_Session::singleton();
-    $recentOther = array();
+    $recentOther = [];
 
     if (($session->get('userID') == $this->getContactId()) ||
       CRM_Contact_BAO_Contact_Permission::allow($this->getContactId(), CRM_Core_Permission::EDIT)
diff --git a/civicrm/CRM/Contact/Page/View/Summary.php b/civicrm/CRM/Contact/Page/View/Summary.php
index f411a21c42da3b752699b21d679384e5fecddf80..7861eb14918258cbabbc76802b017707f8cc913e 100644
--- a/civicrm/CRM/Contact/Page/View/Summary.php
+++ b/civicrm/CRM/Contact/Page/View/Summary.php
@@ -114,8 +114,8 @@ class CRM_Contact_Page_View_Summary extends CRM_Contact_Page_View {
     $session->pushUserContext($url);
     $this->assignFieldMetadataToTemplate('Contact');
 
-    $params = array();
-    $defaults = array();
+    $params = [];
+    $defaults = [];
 
     $params['id'] = $params['contact_id'] = $this->_contactId;
     $params['noRelationships'] = $params['noNotes'] = $params['noGroups'] = TRUE;
@@ -218,7 +218,7 @@ class CRM_Contact_Page_View_Summary extends CRM_Contact_Page_View {
     }
 
     // get contact name of shared contact names
-    $sharedAddresses = array();
+    $sharedAddresses = [];
     $shareAddressContactNames = CRM_Contact_BAO_Contact_Utils::getAddressShareContactNames($defaults['address']);
     foreach ($defaults['address'] as $key => $addressValue) {
       if (!empty($addressValue['master_id']) &&
@@ -274,7 +274,7 @@ class CRM_Contact_Page_View_Summary extends CRM_Contact_Page_View {
    */
   public function getTemplateFileName() {
     if ($this->_contactId) {
-      $contactSubtypes = $this->get('contactSubtype') ? explode(CRM_Core_DAO::VALUE_SEPARATOR, $this->get('contactSubtype')) : array();
+      $contactSubtypes = $this->get('contactSubtype') ? explode(CRM_Core_DAO::VALUE_SEPARATOR, $this->get('contactSubtype')) : [];
 
       // there could be multiple subtypes. We check templates for each of the subtype, and return the first one found.
       foreach ($contactSubtypes as $csType) {
diff --git a/civicrm/CRM/Contact/Selector.php b/civicrm/CRM/Contact/Selector.php
index 0e1c82f78c5a059cfe078383ce78e4102e032df0..2296ddd2e43c8d7c262ea4055119131fbe5b206f 100644
--- a/civicrm/CRM/Contact/Selector.php
+++ b/civicrm/CRM/Contact/Selector.php
@@ -61,6 +61,7 @@ class CRM_Contact_Selector extends CRM_Core_Selector_Base implements CRM_Core_Se
     'status',
     'do_not_email',
     'do_not_phone',
+    'do_not_sms',
     'do_not_mail',
   ];
 
diff --git a/civicrm/CRM/Contact/Task.php b/civicrm/CRM/Contact/Task.php
index e4cac5e8627ed75e5e61706027d7fab33e2c4814..9498e45db2fd61da95c02e8549502eb334cce117 100644
--- a/civicrm/CRM/Contact/Task.php
+++ b/civicrm/CRM/Contact/Task.php
@@ -262,12 +262,12 @@ class CRM_Contact_Task extends CRM_Core_Task {
    * @return array
    *   set of tasks that are valid for the user
    */
-  public static function permissionedTaskTitles($permission, $params = array()) {
+  public static function permissionedTaskTitles($permission, $params = []) {
     if (!isset($params['deletedContacts'])) {
       $params['deletedContacts'] = FALSE;
     }
     self::tasks();
-    $tasks = array();
+    $tasks = [];
     if ($params['deletedContacts']) {
       if (CRM_Core_Permission::check('access deleted contacts')) {
         $tasks[self::RESTORE] = self::$_tasks[self::RESTORE]['title'];
diff --git a/civicrm/CRM/Contribute/BAO/Contribution.php b/civicrm/CRM/Contribute/BAO/Contribution.php
index 48b01b3e6c1277fa54ba0ee0bccede7c2c7f2985..655a9c63ac4d3df7b3a7dd69fbb4a1db0352ec5a 100644
--- a/civicrm/CRM/Contribute/BAO/Contribution.php
+++ b/civicrm/CRM/Contribute/BAO/Contribution.php
@@ -835,6 +835,8 @@ class CRM_Contribute_BAO_Contribution extends CRM_Contribute_DAO_Contribution {
         CRM_Core_BAO_CustomField::getFieldsForImport('Contribution', FALSE, FALSE, FALSE, $checkPermission)
       );
 
+      $fields['financial_type_id']['title'] = ts('Financial Type ID');
+
       self::$_exportableFields = $fields;
     }
 
@@ -1290,7 +1292,8 @@ class CRM_Contribute_BAO_Contribution extends CRM_Contribute_DAO_Contribution {
       if (empty($resultDAO->payment_processor_id) && CRM_Core_Permission::check('edit contributions')) {
         $links = [
           CRM_Core_Action::UPDATE => [
-            'name' => "<i class='crm-i fa-pencil'></i>",
+            'name' => ts('Edit Payment'),
+            'icon' => 'fa-pencil',
             'url' => 'civicrm/payment/edit',
             'class' => 'medium-popup',
             'qs' => "reset=1&id=%%id%%&contribution_id=%%contribution_id%%",
@@ -2755,7 +2758,7 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
    * @return bool
    * @throws Exception
    */
-  public function loadRelatedObjects(&$input, &$ids, $loadAll = FALSE) {
+  public function loadRelatedObjects($input, &$ids, $loadAll = FALSE) {
     // @todo deprecate this function - the steps should be
     // 1) add additional functions like 'getRelatedMemberships'
     // 2) switch all calls that refer to ->_relatedObjects to
@@ -3679,6 +3682,10 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
           $newFinancialAccount = CRM_Financial_BAO_FinancialAccount::getFinancialAccountForFinancialTypeByRelationship($params['financial_type_id'], $accountRelationship);
           if ($oldFinancialAccount != $newFinancialAccount) {
             $params['total_amount'] = 0;
+            // If we have a fee amount set reverse this as well.
+            if (isset($params['fee_amount'])) {
+              $params['trxnParams']['fee_amount'] = 0 - $params['fee_amount'];
+            }
             if (in_array($params['contribution']->contribution_status_id, $pendingStatus)) {
               $params['trxnParams']['to_financial_account_id'] = CRM_Financial_BAO_FinancialAccount::getFinancialAccountForFinancialTypeByRelationship(
                 $params['prevContribution']->financial_type_id, $accountRelationship);
@@ -3700,6 +3707,10 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
             /* $params['trxnParams']['to_financial_account_id'] = $trxnParams['to_financial_account_id']; */
             $params['financial_account_id'] = $newFinancialAccount;
             $params['total_amount'] = $params['trxnParams']['total_amount'] = $params['trxnParams']['net_amount'] = $trxnParams['total_amount'];
+            // Set the transaction fee amount back to the original value for creating the new positive financial trxn.
+            if (isset($params['fee_amount'])) {
+              $params['trxnParams']['fee_amount'] = $params['fee_amount'];
+            }
             self::updateFinancialAccounts($params);
             CRM_Core_BAO_FinancialTrxn::createDeferredTrxn(CRM_Utils_Array::value('line_item', $params), $params['contribution'], TRUE);
             $params['trxnParams']['to_financial_account_id'] = $trxnParams['to_financial_account_id'];
diff --git a/civicrm/CRM/Contribute/BAO/ContributionRecur.php b/civicrm/CRM/Contribute/BAO/ContributionRecur.php
index b83716daa12c0460f818d42a25c866e68cd63ec2..5862d4b777ba9ac39187b05eb707c5fcf69f0867 100644
--- a/civicrm/CRM/Contribute/BAO/ContributionRecur.php
+++ b/civicrm/CRM/Contribute/BAO/ContributionRecur.php
@@ -326,9 +326,12 @@ class CRM_Contribute_BAO_ContributionRecur extends CRM_Contribute_DAO_Contributi
    * @return null|Object
    */
   public static function getSubscriptionDetails($entityID, $entity = 'recur') {
+    // Note: processor_id used to be aliased as subscription_id so we include it here
+    // both as processor_id and subscription_id for legacy compatibility.
     $sql = "
 SELECT rec.id                   as recur_id,
        rec.processor_id         as subscription_id,
+       rec.processor_id,
        rec.frequency_interval,
        rec.installments,
        rec.frequency_unit,
@@ -442,7 +445,8 @@ INNER JOIN civicrm_contribution       con ON ( con.id = mp.contribution_id )
     if ($templateContributions->count()) {
       $templateContribution = $templateContributions->first();
       $result = array_merge($templateContribution, $overrides);
-      $result['line_item'] = CRM_Contribute_BAO_ContributionRecur::calculateRecurLineItems($id, $result['total_amount'], $result['financial_type_id']);
+      $lineItems = CRM_Price_BAO_LineItem::getLineItemsByContributionID($templateContribution['id']);
+      $result['line_item'] = self::reformatLineItemsForRepeatContribution($result['total_amount'], $result['financial_type_id'], $lineItems, (array) $templateContribution);
       return $result;
     }
     return [];
@@ -613,6 +617,8 @@ INNER JOIN civicrm_contribution       con ON ( con.id = mp.contribution_id )
    * @param \CRM_Contribute_BAO_Contribution $contribution
    *
    * @return array
+   * @throws \CRM_Core_Exception
+   * @throws \CiviCRM_API3_Exception
    */
   public static function addRecurLineItems($recurId, $contribution) {
     $foundLineItems = FALSE;
@@ -901,6 +907,7 @@ INNER JOIN civicrm_contribution       con ON ( con.id = mp.contribution_id )
    * @param int $financial_type_id
    *
    * @return array
+   * @throws \CiviCRM_API3_Exception
    */
   public static function calculateRecurLineItems($recurId, $total_amount, $financial_type_id) {
     $originalContribution = civicrm_api3('Contribution', 'getsingle', [
@@ -910,41 +917,7 @@ INNER JOIN civicrm_contribution       con ON ( con.id = mp.contribution_id )
       'return' => ['id', 'financial_type_id'],
     ]);
     $lineItems = CRM_Price_BAO_LineItem::getLineItemsByContributionID($originalContribution['id']);
-    $lineSets = [];
-    if (count($lineItems) == 1) {
-      foreach ($lineItems as $index => $lineItem) {
-        if ($lineItem['financial_type_id'] != $originalContribution['financial_type_id']) {
-          // CRM-20685, Repeattransaction produces incorrect Financial Type ID (in specific circumstance) - if number of lineItems = 1, So this conditional will set the financial_type_id as the original if line_item and contribution comes with different data.
-          $financial_type_id = $lineItem['financial_type_id'];
-        }
-        if ($financial_type_id) {
-          // CRM-17718 allow for possibility of changed financial type ID having been set prior to calling this.
-          $lineItem['financial_type_id'] = $financial_type_id;
-        }
-        $taxAmountMatches = FALSE;
-        if ((!empty($lineItem['tax_amount']) && ($lineItem['line_total'] + $lineItem['tax_amount']) == $total_amount)) {
-          $taxAmountMatches = TRUE;
-        }
-        if ($lineItem['line_total'] != $total_amount && !$taxAmountMatches) {
-          // We are dealing with a changed amount! Per CRM-16397 we can work out what to do with these
-          // if there is only one line item, and the UI should prevent this situation for those with more than one.
-          $lineItem['line_total'] = $total_amount;
-          $lineItem['unit_price'] = round($total_amount / $lineItem['qty'], 2);
-        }
-        $priceField = new CRM_Price_DAO_PriceField();
-        $priceField->id = $lineItem['price_field_id'];
-        $priceField->find(TRUE);
-        $lineSets[$priceField->price_set_id][$lineItem['price_field_id']] = $lineItem;
-      }
-    }
-    // CRM-19309 if more than one then just pass them through:
-    elseif (count($lineItems) > 1) {
-      foreach ($lineItems as $index => $lineItem) {
-        $lineSets[$index][$lineItem['price_field_id']] = $lineItem;
-      }
-    }
-
-    return $lineSets;
+    return self::reformatLineItemsForRepeatContribution($total_amount, $financial_type_id, $lineItems, $originalContribution);
   }
 
   /**
@@ -989,4 +962,53 @@ INNER JOIN civicrm_contribution       con ON ( con.id = mp.contribution_id )
     return CRM_Core_PseudoConstant::get(__CLASS__, $fieldName, $params, $context);
   }
 
+  /**
+   * Reformat line items for getTemplateContribution / repeat contribution.
+   *
+   * This is an extraction and may be subject to further cleanup.
+   *
+   * @param float $total_amount
+   * @param int $financial_type_id
+   * @param array $lineItems
+   * @param array $originalContribution
+   *
+   * @return array
+   */
+  protected static function reformatLineItemsForRepeatContribution($total_amount, $financial_type_id, array $lineItems, array $originalContribution): array {
+    $lineSets = [];
+    if (count($lineItems) == 1) {
+      foreach ($lineItems as $index => $lineItem) {
+        if ($lineItem['financial_type_id'] != $originalContribution['financial_type_id']) {
+          // CRM-20685, Repeattransaction produces incorrect Financial Type ID (in specific circumstance) - if number of lineItems = 1, So this conditional will set the financial_type_id as the original if line_item and contribution comes with different data.
+          $financial_type_id = $lineItem['financial_type_id'];
+        }
+        if ($financial_type_id) {
+          // CRM-17718 allow for possibility of changed financial type ID having been set prior to calling this.
+          $lineItem['financial_type_id'] = $financial_type_id;
+        }
+        $taxAmountMatches = FALSE;
+        if ((!empty($lineItem['tax_amount']) && ($lineItem['line_total'] + $lineItem['tax_amount']) == $total_amount)) {
+          $taxAmountMatches = TRUE;
+        }
+        if ($lineItem['line_total'] != $total_amount && !$taxAmountMatches) {
+          // We are dealing with a changed amount! Per CRM-16397 we can work out what to do with these
+          // if there is only one line item, and the UI should prevent this situation for those with more than one.
+          $lineItem['line_total'] = $total_amount;
+          $lineItem['unit_price'] = round($total_amount / $lineItem['qty'], 2);
+        }
+        $priceField = new CRM_Price_DAO_PriceField();
+        $priceField->id = $lineItem['price_field_id'];
+        $priceField->find(TRUE);
+        $lineSets[$priceField->price_set_id][$lineItem['price_field_id']] = $lineItem;
+      }
+    }
+    // CRM-19309 if more than one then just pass them through:
+    elseif (count($lineItems) > 1) {
+      foreach ($lineItems as $index => $lineItem) {
+        $lineSets[$index][$lineItem['price_field_id']] = $lineItem;
+      }
+    }
+    return $lineSets;
+  }
+
 }
diff --git a/civicrm/CRM/Contribute/BAO/Query.php b/civicrm/CRM/Contribute/BAO/Query.php
index c0f5e17f9a7f64769f41a7012f0af4fb57cd7a4e..2925909affaf14afd5ef8c51f6b5735e9bda55eb 100644
--- a/civicrm/CRM/Contribute/BAO/Query.php
+++ b/civicrm/CRM/Contribute/BAO/Query.php
@@ -893,8 +893,10 @@ class CRM_Contribute_BAO_Query extends CRM_Core_BAO_Query {
       'receive_date',
       'contribution_cancel_date',
       'contribution_page_id',
+      'contribution_id',
     ];
     $metadata = civicrm_api3('Contribution', 'getfields', [])['values'];
+    $metadata['contribution_id'] = $metadata['id'];
     return array_intersect_key($metadata, array_flip($fields));
   }
 
@@ -960,7 +962,6 @@ class CRM_Contribute_BAO_Query extends CRM_Core_BAO_Query {
     $form->addYesNo('contribution_recurring', ts('Contribution is Recurring?'), TRUE);
 
     $form->addYesNo('contribution_test', ts('Contribution is a Test?'), TRUE);
-
     // Add field for transaction ID search
     $form->addElement('text', 'contribution_trxn_id', ts("Transaction ID"));
     $form->addElement('text', 'contribution_check_number', ts('Check Number'));
diff --git a/civicrm/CRM/Contribute/DAO/Contribution.php b/civicrm/CRM/Contribute/DAO/Contribution.php
index d56ce8b8c62382dc9b6a473a52c04bf38aa26988..8d736f25f108d4ee5de0701162a23160670e9380 100644
--- a/civicrm/CRM/Contribute/DAO/Contribution.php
+++ b/civicrm/CRM/Contribute/DAO/Contribution.php
@@ -6,7 +6,7 @@
  *
  * Generated from xml/schema/CRM/Contribute/Contribution.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:389ae44affe94e52a14c9c6daa79be1d)
+ * (GenCodeChecksum:0954aa2ae3574927fda6614f14168fc8)
  */
 
 /**
@@ -282,6 +282,9 @@ class CRM_Contribute_DAO_Contribution extends CRM_Core_DAO {
           'entity' => 'Contribution',
           'bao' => 'CRM_Contribute_BAO_Contribution',
           'localizable' => 0,
+          'html' => [
+            'type' => 'Text',
+          ],
         ],
         'contribution_contact_id' => [
           'name' => 'contact_id',
diff --git a/civicrm/CRM/Contribute/DAO/ContributionRecur.php b/civicrm/CRM/Contribute/DAO/ContributionRecur.php
index 060a304ed0eedf8dba24618ae4ae841f34fe6541..192721f6e66711024e83d3d64e504249f4158953 100644
--- a/civicrm/CRM/Contribute/DAO/ContributionRecur.php
+++ b/civicrm/CRM/Contribute/DAO/ContributionRecur.php
@@ -6,7 +6,7 @@
  *
  * Generated from xml/schema/CRM/Contribute/ContributionRecur.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:d0c493bb1a98e2b75ebaceb330a40fb4)
+ * (GenCodeChecksum:1c6a0e2a296ffbb530eee381975b9d17)
  */
 
 /**
@@ -43,7 +43,7 @@ class CRM_Contribute_DAO_ContributionRecur extends CRM_Core_DAO {
   public $contact_id;
 
   /**
-   * Amount to be contributed or charged each recurrence.
+   * Amount to be collected (including any sales tax) by payment processor each recurrence.
    *
    * @var float
    */
@@ -294,7 +294,7 @@ class CRM_Contribute_DAO_ContributionRecur extends CRM_Core_DAO {
           'name' => 'amount',
           'type' => CRM_Utils_Type::T_MONEY,
           'title' => ts('Amount'),
-          'description' => ts('Amount to be contributed or charged each recurrence.'),
+          'description' => ts('Amount to be collected (including any sales tax) by payment processor each recurrence.'),
           'required' => TRUE,
           'precision' => [
             20,
diff --git a/civicrm/CRM/Contribute/Form/AbstractEditPayment.php b/civicrm/CRM/Contribute/Form/AbstractEditPayment.php
index 57e1e5a84555fd7b8d87e77d5280acdf889c55a1..fd7447efd8d2fd076a377796f9336674433016bd 100644
--- a/civicrm/CRM/Contribute/Form/AbstractEditPayment.php
+++ b/civicrm/CRM/Contribute/Form/AbstractEditPayment.php
@@ -148,13 +148,6 @@ class CRM_Contribute_Form_AbstractEditPayment extends CRM_Contact_Form_Task {
    */
   public $_honorID = NULL;
 
-  /**
-   * Store the financial Type ID
-   *
-   * @var array
-   */
-  public $_contributionType;
-
   /**
    * The contribution values if an existing contribution
    * @var array
@@ -289,7 +282,6 @@ class CRM_Contribute_Form_AbstractEditPayment extends CRM_Contact_Form_Task {
       $this->_noteID = $daoNote->id;
       $values['note'] = $daoNote->note;
     }
-    $this->_contributionType = $values['financial_type_id'];
   }
 
   /**
diff --git a/civicrm/CRM/Contribute/Form/CancelSubscription.php b/civicrm/CRM/Contribute/Form/CancelSubscription.php
index 831b5e9b551960e24e1ada70ca21bbc3e1574781..3cd63518c969a88c854047256504c2600663e740 100644
--- a/civicrm/CRM/Contribute/Form/CancelSubscription.php
+++ b/civicrm/CRM/Contribute/Form/CancelSubscription.php
@@ -133,8 +133,7 @@ class CRM_Contribute_Form_CancelSubscription extends CRM_Contribute_Form_Contrib
   public function buildQuickForm() {
     $this->buildQuickEntityForm();
     // Determine if we can cancel recurring contribution via API with this processor
-    $cancelSupported = $this->_paymentProcessorObj->supports('CancelRecurring');
-    if ($cancelSupported) {
+    if ($this->_paymentProcessorObj->supports('CancelRecurringNotifyOptional')) {
       $searchRange = [];
       $searchRange[] = $this->createElement('radio', NULL, NULL, ts('Yes'), '1');
       $searchRange[] = $this->createElement('radio', NULL, NULL, ts('No'), '0');
@@ -149,7 +148,6 @@ class CRM_Contribute_Form_CancelSubscription extends CRM_Contribute_Form_Contrib
     else {
       $this->assign('cancelRecurNotSupportedText', $this->_paymentProcessorObj->getText('cancelRecurNotSupportedText', []));
     }
-    $this->assign('cancelSupported', $cancelSupported);
 
     if (!empty($this->_donorEmail)) {
       $this->add('checkbox', 'is_notify', ts('Notify Contributor?') . " ({$this->_donorEmail})");
@@ -195,6 +193,8 @@ class CRM_Contribute_Form_CancelSubscription extends CRM_Contribute_Form_Contrib
 
   /**
    * Process the form submission.
+   *
+   * @throws \CRM_Core_Exception
    */
   public function postProcess() {
     $message = NULL;
@@ -212,16 +212,17 @@ class CRM_Contribute_Form_CancelSubscription extends CRM_Contribute_Form_Contrib
       }
     }
 
-    if (CRM_Utils_Array::value('send_cancel_request', $params) == 1) {
-      try {
-        $propertyBag = new PropertyBag();
-        $propertyBag->setContributionRecurID($this->getSubscriptionDetails()->recur_id);
-        $propertyBag->setRecurProcessorID($this->getSubscriptionDetails()->subscription_id);
-        $message = $this->_paymentProcessorObj->doCancelRecurring($propertyBag)['message'];
-      }
-      catch (\Civi\Payment\Exception\PaymentProcessorException $e) {
-        CRM_Core_Error::statusBounce($e->getMessage());
+    try {
+      $propertyBag = new PropertyBag();
+      if (isset($params['send_cancel_request'])) {
+        $propertyBag->setIsNotifyProcessorOnCancelRecur(!empty($params['send_cancel_request']));
       }
+      $propertyBag->setContributionRecurID($this->getSubscriptionDetails()->recur_id);
+      $propertyBag->setRecurProcessorID($this->getSubscriptionDetails()->processor_id);
+      $message = $this->_paymentProcessorObj->doCancelRecurring($propertyBag)['message'];
+    }
+    catch (\Civi\Payment\Exception\PaymentProcessorException $e) {
+      CRM_Core_Error::statusBounce($e->getMessage());
     }
 
     if ($cancelSubscription) {
diff --git a/civicrm/CRM/Contribute/Form/Contribution.php b/civicrm/CRM/Contribute/Form/Contribution.php
index b3a7e36cfe2408069faaac8b97b2c1411b91c5cc..b063ed269d3cfde58d086887abcb5d1ee17dd91e 100644
--- a/civicrm/CRM/Contribute/Form/Contribution.php
+++ b/civicrm/CRM/Contribute/Form/Contribution.php
@@ -77,13 +77,6 @@ class CRM_Contribute_Form_Contribution extends CRM_Contribute_Form_AbstractEditP
    */
   public $_params;
 
-  /**
-   * Store the contribution Type ID
-   *
-   * @var array
-   */
-  public $_contributionType;
-
   /**
    * The contribution values if an existing contribution
    * @var array
@@ -277,7 +270,7 @@ class CRM_Contribute_Form_Contribution extends CRM_Contribute_Form_AbstractEditP
 
     // when custom data is included in this page
     if (!empty($_POST['hidden_custom'])) {
-      $this->applyCustomData('Contribution', CRM_Utils_Array::value('financial_type_id', $_POST), $this->_id);
+      $this->applyCustomData('Contribution', $this->getFinancialTypeID(), $this->_id);
     }
 
     $this->_lineItems = [];
@@ -382,10 +375,6 @@ class CRM_Contribute_Form_Contribution extends CRM_Contribute_Form_AbstractEditP
       }
     }
 
-    if ($this->_contributionType) {
-      $defaults['financial_type_id'] = $this->_contributionType;
-    }
-
     if (empty($defaults['payment_instrument_id'])) {
       $defaults['payment_instrument_id'] = $this->getDefaultPaymentInstrumentId();
     }
@@ -612,7 +601,7 @@ class CRM_Contribute_Form_Contribution extends CRM_Contribute_Form_AbstractEditP
 
     //need to assign custom data type and subtype to the template
     $this->assign('customDataType', 'Contribution');
-    $this->assign('customDataSubType', $this->_contributionType);
+    $this->assign('customDataSubType', $this->getFinancialTypeID());
     $this->assign('entityID', $this->_id);
 
     $contactField = $this->addEntityRef('contact_id', ts('Contributor'), ['create' => TRUE, 'api' => ['extra' => ['email']]], TRUE);
@@ -1737,7 +1726,7 @@ class CRM_Contribute_Form_Contribution extends CRM_Contribute_Form_AbstractEditP
         $this->assign('totalTaxAmount', $submittedValues['tax_amount']);
         $this->assign('getTaxDetails', $getTaxDetails);
         $this->assign('dataArray', $taxRate);
-        $this->assign('taxTerm', CRM_Utils_Array::value('tax_term', $invoiceSettings));
+        $this->assign('taxTerm', Civi::settings()->get('tax_term'));
       }
       else {
         $this->assign('totalTaxAmount', CRM_Utils_Array::value('tax_amount', $submittedValues));
@@ -1808,4 +1797,21 @@ class CRM_Contribute_Form_Contribution extends CRM_Contribute_Form_AbstractEditP
     return 0;
   }
 
+  /**
+   * Get the financial Type ID for the contribution either from the submitted values or from the contribution values if possible.
+   *
+   * This is important for dev/core#1728 - ie ensure that if we are returned to the form for a form
+   * error that any custom fields based on the selected financial type are loaded.
+   *
+   * @return int
+   */
+  protected function getFinancialTypeID() {
+    if (!empty($this->_submitValues['financial_type_id'])) {
+      return $this->_submitValues['financial_type_id'];
+    }
+    if (!empty($this->_values['financial_type_id'])) {
+      return $this->_values['financial_type_id'];
+    }
+  }
+
 }
diff --git a/civicrm/CRM/Contribute/Form/Contribution/Main.php b/civicrm/CRM/Contribute/Form/Contribution/Main.php
index 12cb2813df3a0fa2d7bf82851f5961ab2c1073f7..b55883cbee07507fc61baea02d5c5dff05052d35 100644
--- a/civicrm/CRM/Contribute/Form/Contribution/Main.php
+++ b/civicrm/CRM/Contribute/Form/Contribution/Main.php
@@ -479,10 +479,14 @@ class CRM_Contribute_Form_Contribution_Main extends CRM_Contribute_Form_Contribu
     if (!($allAreBillingModeProcessors && !$this->_values['is_pay_later'])) {
       $submitButton = [
         'type' => 'upload',
-        'name' => !empty($this->_values['is_confirm_enabled']) ? ts('Review your contribution') : ts('Contribute'),
+        'name' => ts('Contribute'),
         'spacing' => '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
         'isDefault' => TRUE,
       ];
+      if (!empty($this->_values['is_confirm_enabled'])) {
+        $submitButton['name'] = ts('Review your contribution');
+        $submitButton['icon'] = 'fa-chevron-right';
+      }
       // Add submit-once behavior when confirm page disabled
       if (empty($this->_values['is_confirm_enabled'])) {
         $this->submitOnce = TRUE;
diff --git a/civicrm/CRM/Contribute/Form/ContributionBase.php b/civicrm/CRM/Contribute/Form/ContributionBase.php
index db2870fa146a305f725d5a2fe4b7c224fad2bc5d..ca2805d551eb1e0849df356b4d6be2f3501cc2a3 100644
--- a/civicrm/CRM/Contribute/Form/ContributionBase.php
+++ b/civicrm/CRM/Contribute/Form/ContributionBase.php
@@ -371,12 +371,12 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
         $postProfileType = CRM_Core_BAO_UFField::getProfileType($this->_values['custom_post_id']);
       }
 
-      if (((isset($postProfileType) && $postProfileType == 'Membership') ||
-          (isset($preProfileType) && $preProfileType == 'Membership')
+      if (((isset($postProfileType) && $postProfileType === 'Membership') ||
+          (isset($preProfileType) && $preProfileType === 'Membership')
         ) &&
         !$this->_membershipBlock['is_active']
       ) {
-        CRM_Core_Error::fatal(ts('This page includes a Profile with Membership fields - but the Membership Block is NOT enabled. Please notify the site administrator.'));
+        CRM_Core_Error::statusBounce(ts('This page includes a Profile with Membership fields - but the Membership Block is NOT enabled. Please notify the site administrator.'));
       }
 
       $pledgeBlock = CRM_Pledge_BAO_PledgeBlock::getPledgeBlock($this->_id);
@@ -437,7 +437,7 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
       !$this->_membershipBlock['is_active'] &&
       !$this->_priceSetId
     ) {
-      CRM_Core_Error::fatal(ts('The requested online contribution page is missing a required Contribution Amount section or Membership section or Price Set. Please check with the site administrator for assistance.'));
+      CRM_Core_Error::statusBounce(ts('The requested online contribution page is missing a required Contribution Amount section or Membership section or Price Set. Please check with the site administrator for assistance.'));
     }
 
     if ($this->_values['amount_block_is_active']) {
@@ -577,7 +577,7 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
     //Hence, assign the existing location type email by iterating through the params.
     if ($this->_emailExists && empty($this->_params["email-{$this->_bltID}"])) {
       foreach ($this->_params as $key => $val) {
-        if (substr($key, 0, 6) == 'email-') {
+        if (substr($key, 0, 6) === 'email-') {
           $this->assign('email', $this->_params[$key]);
           break;
         }
@@ -749,6 +749,12 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
     CRM_Utils_ReCAPTCHA::enableCaptchaOnForm($this);
   }
 
+  /**
+   * Assign payment field information to the template.
+   *
+   * @throws \CRM_Core_Exception
+   * @throws \CiviCRM_API3_Exception
+   */
   public function assignPaymentFields() {
     //fix for CRM-3767
     $isMonetary = FALSE;
@@ -765,7 +771,7 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
     // The concept of contributeMode is deprecated.
     // The payment processor object can provide info about the fields it shows.
     if ($isMonetary && is_a($this->_paymentProcessor['object'], 'CRM_Core_Payment')) {
-      /** @var  $paymentProcessorObject \CRM_Core_Payment */
+      /** @var  \CRM_Core_Payment $paymentProcessorObject */
       $paymentProcessorObject = $this->_paymentProcessor['object'];
 
       $paymentFields = $paymentProcessorObject->getPaymentFormFields();
@@ -824,6 +830,8 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
    *
    * @param int $id
    * @param CRM_Core_Form $form
+   *
+   * @throws \CRM_Core_Exception
    */
   public function buildComponentForm($id, $form) {
     if (empty($id)) {
@@ -833,13 +841,13 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
     $contactID = $this->getContactID();
 
     foreach (['soft_credit', 'on_behalf'] as $module) {
-      if ($module == 'soft_credit') {
+      if ($module === 'soft_credit') {
         if (empty($form->_values['honoree_profile_id'])) {
           continue;
         }
 
         if (!CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $form->_values['honoree_profile_id'], 'is_active')) {
-          CRM_Core_Error::fatal(ts('This contribution page has been configured for contribution on behalf of honoree and the selected honoree profile is either disabled or not found.'));
+          CRM_Core_Error::statusBounce(ts('This contribution page has been configured for contribution on behalf of honoree and the selected honoree profile is either disabled or not found.'));
         }
 
         $profileContactType = CRM_Core_BAO_UFGroup::getContactType($form->_values['honoree_profile_id']);
@@ -850,7 +858,7 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
         ];
         $validProfile = CRM_Core_BAO_UFGroup::checkValidProfile($form->_values['honoree_profile_id'], $requiredProfileFields[$profileContactType]);
         if (!$validProfile) {
-          CRM_Core_Error::fatal(ts('This contribution page has been configured for contribution on behalf of honoree and the required fields of the selected honoree profile are disabled or doesn\'t exist.'));
+          CRM_Core_Error::statusBounce(ts('This contribution page has been configured for contribution on behalf of honoree and the required fields of the selected honoree profile are disabled or doesn\'t exist.'));
         }
 
         foreach (['honor_block_title', 'honor_block_text'] as $name) {
@@ -892,7 +900,7 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
         }
 
         if (!CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $form->_values['onbehalf_profile_id'], 'is_active')) {
-          CRM_Core_Error::fatal(ts('This contribution page has been configured for contribution on behalf of an organization and the selected onbehalf profile is either disabled or not found.'));
+          CRM_Core_Error::statusBounce(ts('This contribution page has been configured for contribution on behalf of an organization and the selected onbehalf profile is either disabled or not found.'));
         }
 
         $member = CRM_Member_BAO_Membership::getMembershipBlock($form->_id);
@@ -911,7 +919,7 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
                 in_array('Contribution', $onBehalfProfile)
               )
             ) {
-              CRM_Core_Error::fatal($msg);
+              CRM_Core_Error::statusBounce($msg);
             }
           }
         }
@@ -1031,7 +1039,7 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
    */
   public function getTemplateFileName() {
     $fileName = $this->checkTemplateFileExists();
-    return $fileName ? $fileName : parent::getTemplateFileName();
+    return $fileName ?: parent::getTemplateFileName();
   }
 
   /**
@@ -1049,6 +1057,8 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
 
   /**
    * Authenticate pledge user during online payment.
+   *
+   * @throws \CRM_Core_Exception
    */
   public function authenticatePledgeUser() {
     //get the userChecksum and contact id
@@ -1087,12 +1097,12 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
     }
 
     if (!$validUser) {
-      CRM_Core_Error::fatal(ts("Oops. It looks like you have an incorrect or incomplete link (URL). Please make sure you've copied the entire link, and try again. Contact the site administrator if this error persists."));
+      CRM_Core_Error::statusBounce(ts("Oops. It looks like you have an incorrect or incomplete link (URL). Please make sure you've copied the entire link, and try again. Contact the site administrator if this error persists."));
     }
 
     //check for valid pledge status.
     if (!in_array($pledgeValues['status_id'], $validStatus)) {
-      CRM_Core_Error::fatal(ts('Oops. You cannot make a payment for this pledge - pledge status is %1.', [1 => CRM_Utils_Array::value($pledgeValues['status_id'], $allStatus)]));
+      CRM_Core_Error::statusBounce(ts('Oops. You cannot make a payment for this pledge - pledge status is %1.', [1 => CRM_Utils_Array::value($pledgeValues['status_id'], $allStatus)]));
     }
   }
 
@@ -1102,6 +1112,8 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
    * In case user cancel recurring contribution,
    * When we get the control back from payment gate way
    * lets delete the recurring and related contribution.
+   *
+   * @throws \CRM_Core_Exception
    */
   public function cancelRecurring() {
     $isCancel = CRM_Utils_Request::retrieve('cancel', 'Boolean');
@@ -1135,6 +1147,9 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
    *
    * @return bool
    *   Is this a separate membership payment
+   *
+   * @throws \CiviCRM_API3_Exception
+   * @throws \CRM_Core_Exception
    */
   protected function buildMembershipBlock(
     $cid,
diff --git a/civicrm/CRM/Contribute/Form/Task/Status.php b/civicrm/CRM/Contribute/Form/Task/Status.php
index 09f1285c99b43ffa64a42b0fb6396d172b6f6a45..c8acc1e8ff88e65a559b25f33d5f0a29e5a1e31c 100644
--- a/civicrm/CRM/Contribute/Form/Task/Status.php
+++ b/civicrm/CRM/Contribute/Form/Task/Status.php
@@ -215,8 +215,6 @@ AND    co.id IN ( $contribIDs )";
     $statusID = $params['contribution_status_id'] ?? NULL;
     $baseIPN = new CRM_Core_Payment_BaseIPN();
 
-    $transaction = new CRM_Core_Transaction();
-
     // get the missing pieces for each contribution
     $contribIDs = implode(',', $form->_contributionIds);
     $details = self::getDetails($contribIDs);
@@ -246,11 +244,13 @@ AND    co.id IN ( $contribIDs )";
       );
 
       if ($statusID == array_search('Cancelled', $contributionStatuses)) {
+        $transaction = new CRM_Core_Transaction();
         $baseIPN->cancelled($objects, $transaction);
         $transaction->commit();
         continue;
       }
       elseif ($statusID == array_search('Failed', $contributionStatuses)) {
+        $transaction = new CRM_Core_Transaction();
         $baseIPN->failed($objects, $transaction);
         $transaction->commit();
         continue;
@@ -261,7 +261,6 @@ AND    co.id IN ( $contribIDs )";
           $contributionStatuses
         )
       ) {
-        $transaction->commit();
         continue;
       }
 
@@ -282,8 +281,8 @@ AND    co.id IN ( $contribIDs )";
       $input['trxn_date'] = $params["trxn_date_{$row['contribution_id']}"] . ' ' . date('H:i:s');
       $input['is_email_receipt'] = !empty($params['is_email_receipt']);
 
-      // @todo calling baseIPN like this is a pattern in it's last gasps. Call contribute.completetransaction api.
-      $baseIPN->completeTransaction($input, $ids, $objects, $transaction, FALSE);
+      // @todo calling CRM_Contribute_BAO_Contribution::completeOrder like this is a pattern in it's last gasps. Call contribute.completetransaction api.
+      CRM_Contribute_BAO_Contribution::completeOrder($input, $ids, $objects);
 
       // reset template values before processing next transactions
       $template->clearTemplateVars();
diff --git a/civicrm/CRM/Contribute/Form/UpdateBilling.php b/civicrm/CRM/Contribute/Form/UpdateBilling.php
index 1370802e5494bea13208fecef2c0151dbae44ce5..b7d9e550f121c606709c1f41a4532cbb88150ced 100644
--- a/civicrm/CRM/Contribute/Form/UpdateBilling.php
+++ b/civicrm/CRM/Contribute/Form/UpdateBilling.php
@@ -55,12 +55,12 @@ class CRM_Contribute_Form_UpdateBilling extends CRM_Contribute_Form_Contribution
     }
 
     if ((!$this->_crid && !$this->_coid && !$this->_mid) || (!$this->_subscriptionDetails)) {
-      CRM_Core_Error::fatal('Required information missing.');
+      throw new CRM_Core_Exception('Required information missing.');
     }
 
     if (!$this->_paymentProcessor['object']->supports('updateSubscriptionBillingInfo')) {
-      CRM_Core_Error::fatal(ts("%1 processor doesn't support updating subscription billing details.",
-        array(1 => $this->_paymentProcessor['object']->_processorName)
+      throw new CRM_Core_Exception(ts("%1 processor doesn't support updating subscription billing details.",
+        [1 => $this->_paymentProcessor['title']]
       ));
     }
     $this->assign('paymentProcessor', $this->_paymentProcessor);
@@ -84,10 +84,10 @@ class CRM_Contribute_Form_UpdateBilling extends CRM_Contribute_Form_Contribution
    *   Default values
    */
   public function setDefaultValues() {
-    $this->_defaults = array();
+    $this->_defaults = [];
 
     if ($this->_subscriptionDetails->contact_id) {
-      $fields = array();
+      $fields = [];
       $names = array(
         'first_name',
         'middle_name',
@@ -170,7 +170,7 @@ class CRM_Contribute_Form_UpdateBilling extends CRM_Contribute_Form_Contribution
    *   true if no errors, else array of errors
    */
   public static function formRule($fields, $files, $self) {
-    $errors = array();
+    $errors = [];
     CRM_Core_Form::validateMandatoryFields($self->_fields, $fields, $errors);
 
     // validate the payment instrument values (e.g. credit card number)
@@ -192,7 +192,7 @@ class CRM_Contribute_Form_UpdateBilling extends CRM_Contribute_Form_Contribution
     }
     $fields["email-{$this->_bltID}"] = 1;
 
-    $processorParams = array();
+    $processorParams = [];
     foreach ($params as $key => $val) {
       $key = str_replace('billing_', '', $key);
       list($key) = explode('-', $key);
@@ -202,7 +202,7 @@ class CRM_Contribute_Form_UpdateBilling extends CRM_Contribute_Form_Contribution
     $processorParams['country'] = CRM_Core_PseudoConstant::country($params["billing_country_id-{$this->_bltID}"], FALSE);
     $processorParams['month'] = $processorParams['credit_card_exp_date']['M'];
     $processorParams['year'] = $processorParams['credit_card_exp_date']['Y'];
-    $processorParams['subscriptionId'] = $this->_subscriptionDetails->subscription_id;
+    $processorParams['subscriptionId'] = $this->getSubscriptionDetails()->processor_id;
     $processorParams['amount'] = $this->_subscriptionDetails->amount;
     $updateSubscription = $this->_paymentProcessor['object']->updateSubscriptionBillingInfo($message, $processorParams);
     if (is_a($updateSubscription, 'CRM_Core_Error')) {
diff --git a/civicrm/CRM/Contribute/Form/UpdateSubscription.php b/civicrm/CRM/Contribute/Form/UpdateSubscription.php
index be18481f5e79f620624336e2c36e97b6bd880c18..51e218b99f3b1c97c847c0d2f56d08ad90721444 100644
--- a/civicrm/CRM/Contribute/Form/UpdateSubscription.php
+++ b/civicrm/CRM/Contribute/Form/UpdateSubscription.php
@@ -206,7 +206,7 @@ class CRM_Contribute_Form_UpdateSubscription extends CRM_Contribute_Form_Contrib
     $params['id'] = $this->_subscriptionDetails->recur_id;
     $message = '';
 
-    $params['subscriptionId'] = $this->_subscriptionDetails->subscription_id;
+    $params['subscriptionId'] = $this->getSubscriptionDetails()->processor_id;
     $updateSubscription = TRUE;
     if ($this->_paymentProcessorObj->supports('changeSubscriptionAmount')) {
       try {
diff --git a/civicrm/CRM/Contribute/Page/ContributionPage.php b/civicrm/CRM/Contribute/Page/ContributionPage.php
index 56ae53b7e20ec75e8b54fb44c94238fc8e29ea61..835089da894a9c32e0f880e79370c4e9822cf943 100644
--- a/civicrm/CRM/Contribute/Page/ContributionPage.php
+++ b/civicrm/CRM/Contribute/Page/ContributionPage.php
@@ -411,14 +411,14 @@ AND         cp.page_type = 'contribute'
 
     $this->search();
 
-    $params = array();
+    $params = [];
 
     $whereClause = $this->whereClause($params, FALSE);
     $config = CRM_Core_Config::singleton();
     if ($config->includeAlphabeticalPager) {
       $this->pagerAToZ($whereClause, $params);
     }
-    $params = array();
+    $params = [];
     $whereClause = $this->whereClause($params, TRUE);
     $this->pager($whereClause, $params);
 
@@ -434,7 +434,7 @@ AND         cp.page_type = 'contribute'
    ORDER BY is_active desc, title asc
    LIMIT  $offset, $rowCount";
     $contribPage = CRM_Core_DAO::executeQuery($query, $params, TRUE, 'CRM_Contribute_DAO_ContributionPage');
-    $contribPageIds = array();
+    $contribPageIds = [];
     while ($contribPage->fetch()) {
       $contribPageIds[$contribPage->id] = $contribPage->id;
     }
@@ -457,7 +457,7 @@ ORDER BY is_active desc, title asc
     $configureActionLinks = self::configureActionLinks();
 
     while ($dao->fetch()) {
-      $contribution[$dao->id] = array();
+      $contribution[$dao->id] = [];
       CRM_Core_DAO::storeValues($dao, $contribution[$dao->id]);
 
       // form all action links
@@ -485,7 +485,7 @@ ORDER BY is_active desc, title asc
       }
 
       //build the configure links.
-      $sectionsInfo = CRM_Utils_Array::value($dao->id, $contriPageSectionInfo, array());
+      $sectionsInfo = CRM_Utils_Array::value($dao->id, $contriPageSectionInfo, []);
       $contribution[$dao->id]['configureActionLinks'] = CRM_Core_Action::formLink(self::formatConfigureLinks($sectionsInfo),
         $action,
         array('id' => $dao->id),
@@ -565,7 +565,7 @@ ORDER BY is_active desc, title asc
    */
   public function whereClause(&$params, $sortBy = TRUE) {
     // @todo Unused local variable can be safely removed.
-    $values = $clauses = array();
+    $values = $clauses = [];
     $title = $this->get('title');
     $createdId = $this->get('cid');
 
@@ -584,7 +584,7 @@ ORDER BY is_active desc, title asc
     }
 
     $value = $this->get('financial_type_id');
-    $val = array();
+    $val = [];
     if ($value) {
       if (is_array($value)) {
         foreach ($value as $k => $v) {
@@ -627,7 +627,7 @@ ORDER BY is_active desc, title asc
   public function getCampaignIds() {
     // The unfiltered value from the session cannot be trusted, it needs to be
     // processed to get a clean array of positive integers.
-    $ids = array();
+    $ids = [];
     foreach ((array) $this->get('campaign_id') as $id) {
       if ((string) (int) $id === (string) $id && $id > 0) {
         $ids[] = $id;
@@ -695,7 +695,7 @@ ORDER BY UPPER(LEFT(title, 1))
       }
 
       if (empty($sectionsInfo[$sectionName])) {
-        $classes = array();
+        $classes = [];
         if (isset($link['class'])) {
           $classes = $link['class'];
         }
diff --git a/civicrm/CRM/Contribute/xml/Menu/Contribute.xml b/civicrm/CRM/Contribute/xml/Menu/Contribute.xml
index 548902dbc07c9f1f342db5c4f497bf73436047c2..cfafe7f6c395621ed2ce8d3235107f623a07318b 100644
--- a/civicrm/CRM/Contribute/xml/Menu/Contribute.xml
+++ b/civicrm/CRM/Contribute/xml/Menu/Contribute.xml
@@ -41,7 +41,6 @@
     <page_callback>CRM_Contribute_Page_ContributionPage</page_callback>
     <desc>CiviContribute allows you to create and maintain any number of Online Contribution Pages. You can create different pages for different programs or campaigns - and customize text, amounts, types of information collected from contributors, etc.</desc>
     <adminGroup>CiviContribute</adminGroup>
-    <icon>admin/small/online_contribution_pages.png</icon>
     <weight>360</weight>
   </item>
   <item>
@@ -110,7 +109,6 @@
     <page_callback>CRM_Contribute_Page_ManagePremiums</page_callback>
     <desc>CiviContribute allows you to configure any number of Premiums which can be offered to contributors as incentives / thank-you gifts. Define the premiums you want to offer here.</desc>
     <adminGroup>CiviContribute</adminGroup>
-    <icon>admin/small/Premiums.png</icon>
     <weight>365</weight>
   </item>
   <item>
@@ -136,7 +134,6 @@
     <page_callback>CRM_Financial_Page_FinancialAccount</page_callback>
     <desc>Financial types are used to categorize contributions for reporting and accounting purposes. These are also referred to as Funds.</desc>
     <adminGroup>CiviContribute</adminGroup>
-    <icon>admin/small/contribution_types.png</icon>
     <weight>370</weight>
   </item>
   <item>
@@ -145,7 +142,6 @@
     <page_callback>CRM_Admin_Page_Options</page_callback>
     <desc>You may choose to record the payment instrument used for each contribution. Common payment methods are installed by default (e.g. Check, Cash, Credit Card...). If your site requires additional payment methods, add them here.</desc>
     <adminGroup>CiviContribute</adminGroup>
-    <icon>admin/small/payment_instruments.png</icon>
     <weight>380</weight>
   </item>
   <item>
@@ -154,7 +150,6 @@
     <page_callback>CRM_Admin_Page_Options</page_callback>
     <desc>Credit card options that will be offered to contributors using your Online Contribution pages.</desc>
     <adminGroup>CiviContribute</adminGroup>
-    <icon>admin/small/accepted_creditcards.png</icon>
     <weight>395</weight>
   </item>
   <item>
@@ -163,7 +158,6 @@
     <page_callback>CRM_Admin_Page_Options</page_callback>
     <desc>Soft Credit Types that will be offered to contributors during soft credit contribution</desc>
     <adminGroup>CiviContribute</adminGroup>
-    <icon>admin/small/soft_credit_type.png</icon>
   </item>
   <item>
     <path>civicrm/contact/view/contribution</path>
diff --git a/civicrm/CRM/Contribute/xml/Menu/PCP.xml b/civicrm/CRM/Contribute/xml/Menu/PCP.xml
index 916b55489d5505bd8807754eb9f50cc1723d8aa9..731d9be935c1f27573188b23d2d207c35db34c84 100644
--- a/civicrm/CRM/Contribute/xml/Menu/PCP.xml
+++ b/civicrm/CRM/Contribute/xml/Menu/PCP.xml
@@ -8,7 +8,6 @@
      <path_arguments>context=contribute</path_arguments>
      <desc>View and manage existing personal campaign pages.</desc>
      <adminGroup>CiviContribute</adminGroup>
-     <icon>admin/small/contribution_types.png</icon>
      <weight>362</weight>
   </item>
   <item>
diff --git a/civicrm/CRM/Core/Action.php b/civicrm/CRM/Core/Action.php
index 0e54b2445b738ebb6384987de42597380d3e3712..f1475076020c2c74c22a86799107120ccb391a78 100644
--- a/civicrm/CRM/Core/Action.php
+++ b/civicrm/CRM/Core/Action.php
@@ -247,7 +247,7 @@ class CRM_Core_Action {
           $urlPath,
           $classes,
           !empty($link['title']) ? "title='{$link['title']}' " : '',
-          $link['name']
+          empty($link['icon']) ? $link['name'] : CRM_Core_Page::crmIcon($link['icon'], $link['name'], TRUE, ['title' => ''])
         );
       }
     }
@@ -312,7 +312,7 @@ class CRM_Core_Action {
 
     // make links indexed sequentially instead of by bitmask
     // otherwise it's next to impossible to reliably add new ones
-    $seqLinks = array();
+    $seqLinks = [];
     foreach ($links as $bit => $link) {
       $link['bit'] = $bit;
       $seqLinks[] = $link;
diff --git a/civicrm/CRM/Core/BAO/ActionLog.php b/civicrm/CRM/Core/BAO/ActionLog.php
index c04e3f30cceb68fd6bfa9226d829453096b81e30..877c88b8ae5aa9173fd847ddfb37ae2429ad15b1 100644
--- a/civicrm/CRM/Core/BAO/ActionLog.php
+++ b/civicrm/CRM/Core/BAO/ActionLog.php
@@ -30,30 +30,9 @@ class CRM_Core_BAO_ActionLog extends CRM_Core_DAO_ActionLog {
    * @return array
    */
   public static function create($params) {
-    $actionLog = new CRM_Core_DAO_ActionLog();
+    $params['action_date_time'] = $params['action_date_time'] ?? date('YmdHis');
 
-    $params['action_date_time'] = CRM_Utils_Array::value('action_date_time', $params, date('YmdHis'));
-
-    $actionLog->copyValues($params);
-
-    $edit = (bool) $actionLog->id;
-    if ($edit) {
-      CRM_Utils_Hook::pre('edit', 'ActionLog', $actionLog->id, $actionLog);
-    }
-    else {
-      CRM_Utils_Hook::pre('create', 'ActionLog', NULL, $actionLog);
-    }
-
-    $actionLog->save();
-
-    if ($edit) {
-      CRM_Utils_Hook::post('edit', 'ActionLog', $actionLog->id, $actionLog);
-    }
-    else {
-      CRM_Utils_Hook::post('create', 'ActionLog', NULL, $actionLog);
-    }
-
-    return $actionLog;
+    return self::writeRecord($params);
   }
 
 }
diff --git a/civicrm/CRM/Core/BAO/ActionSchedule.php b/civicrm/CRM/Core/BAO/ActionSchedule.php
index c1d7ec1a104270d55cf61ca6ab974523641da72c..a53e0e623278acb7ba7a1eb4b8f4df4aa58b8267 100644
--- a/civicrm/CRM/Core/BAO/ActionSchedule.php
+++ b/civicrm/CRM/Core/BAO/ActionSchedule.php
@@ -226,6 +226,7 @@ FROM civicrm_action_schedule cas
    * @param int $id
    *   ID of the Reminder to be deleted.
    *
+   * @throws CRM_Core_Exception
    */
   public static function del($id) {
     if ($id) {
@@ -236,7 +237,7 @@ FROM civicrm_action_schedule cas
         return;
       }
     }
-    CRM_Core_Error::fatal(ts('Invalid value passed to delete function.'));
+    throw new CRM_Core_Exception(ts('Invalid value passed to delete function.'));
   }
 
   /**
diff --git a/civicrm/CRM/Core/BAO/Address.php b/civicrm/CRM/Core/BAO/Address.php
index e22a25cecd2c1e61a94eb029b36528920b8f7956..c0f6561c8a41c624b622d2725403b33c9bbb304d 100644
--- a/civicrm/CRM/Core/BAO/Address.php
+++ b/civicrm/CRM/Core/BAO/Address.php
@@ -1181,7 +1181,7 @@ SELECT is_primary,
     $relTypeId = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_RelationshipType', 'Household Member of', 'id', 'name_a_b');
 
     if (!$relTypeId) {
-      CRM_Core_Error::fatal(ts("You seem to have deleted the relationship type 'Household Member of'"));
+      throw new CRM_Core_Exception(ts("You seem to have deleted the relationship type 'Household Member of'"));
     }
 
     $relParam = [
diff --git a/civicrm/CRM/Core/BAO/Block.php b/civicrm/CRM/Core/BAO/Block.php
index a36b698e66d446923e0ffe0844ac2c7973d3f260..876cd323c5b2d8024ea3d2de1e68afba3bfc2b95 100644
--- a/civicrm/CRM/Core/BAO/Block.php
+++ b/civicrm/CRM/Core/BAO/Block.php
@@ -40,6 +40,7 @@ class CRM_Core_BAO_Block {
    *
    * @return array
    *   Array of $block objects.
+   * @throws CRM_Core_Exception
    */
   public static function &getValues($blockName, $params) {
     if (empty($params)) {
@@ -52,7 +53,7 @@ class CRM_Core_BAO_Block {
     if (!isset($params['entity_table'])) {
       $block->contact_id = $params['contact_id'];
       if (!$block->contact_id) {
-        CRM_Core_Error::fatal();
+        throw new CRM_Core_Exception('Invalid Contact ID parameter passed');
       }
       $blocks = self::retrieveBlock($block, $blockName);
     }
diff --git a/civicrm/CRM/Core/BAO/Cache.php b/civicrm/CRM/Core/BAO/Cache.php
index ee678348dd60813e4ec3a6575532bbacf8647c9f..a13a6fee8588cfa91a32a185e93cc35e45477f6a 100644
--- a/civicrm/CRM/Core/BAO/Cache.php
+++ b/civicrm/CRM/Core/BAO/Cache.php
@@ -149,6 +149,7 @@ class CRM_Core_BAO_Cache extends CRM_Core_DAO_Cache {
    * @param int $componentID
    *   The optional component ID (so componenets can share the same name space).
    * @deprecated
+   * @throws CRM_Core_Exception
    */
   public static function setItem(&$data, $group, $path, $componentID = NULL) {
     CRM_Core_Error::deprecatedFunctionWarning(
@@ -167,7 +168,7 @@ class CRM_Core_BAO_Cache extends CRM_Core_DAO_Cache {
     // CRM-11234
     $lock = Civi::lockManager()->acquire("cache.{$group}_{$path}._{$componentID}");
     if (!$lock->isAcquired()) {
-      CRM_Core_Error::fatal();
+      throw new CRM_Core_Exception('Cannot acquire database lock');
     }
 
     $table = self::getTableName();
diff --git a/civicrm/CRM/Core/BAO/CustomField.php b/civicrm/CRM/Core/BAO/CustomField.php
index b8c8f9543c602b7239fad83dd54cd2e7bdb5bfd5..0fcf801bd65d68ba6651ea6918515c7321fe70f8 100644
--- a/civicrm/CRM/Core/BAO/CustomField.php
+++ b/civicrm/CRM/Core/BAO/CustomField.php
@@ -242,6 +242,20 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
     return $options;
   }
 
+  /**
+   * @inheritDoc
+   */
+  public static function buildOptions($fieldName, $context = NULL, $props = []) {
+    $options = parent::buildOptions($fieldName, $context, $props);
+    // This provides legacy support for APIv3, allowing no-longer-existent html types
+    if ($fieldName == 'html_type' && isset($props['version']) && $props['version'] == 3) {
+      $options['Multi-Select'] = 'Multi-Select';
+      $options['Multi-Select Country'] = 'Multi-Select Country';
+      $options['Multi-Select State/Province'] = 'Multi-Select State/Province';
+    }
+    return $options;
+  }
+
   /**
    * Store and return an array of all active custom fields.
    *
@@ -264,6 +278,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
    *
    * @return array
    *   an array of active custom fields.
+   * @throws \CRM_Core_Exception
    */
   public static function &getFields(
     $customDataType = 'Individual',
@@ -378,6 +393,10 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
         if ($onlyParent) {
           $extends .= " AND $cgTable.extends_entity_column_value IS NULL AND $cgTable.extends_entity_column_id IS NULL ";
         }
+        // Temporary hack - in 5.27 a new field is added to civicrm_custom_field. There is a high
+        // risk this function is called before the upgrade page can be reached and if
+        // so it will potentially result in fatal error.
+        $serializeField = CRM_Core_BAO_Domain::isDBVersionAtLeast('5.27.alpha1') ? "$cfTable.serialize," : '';
 
         $query = "SELECT $cfTable.id, $cfTable.label,
                             $cgTable.title,
@@ -396,6 +415,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
                             $cfTable.date_format,
                             $cfTable.time_format,
                             $cgTable.is_multiple,
+                            $serializeField
                             $cgTable.table_name,
                             og.name as option_group_name
                      FROM $cfTable
@@ -478,6 +498,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
           $fields[$dao->id]['is_required'] = $dao->is_required;
           $fields[$dao->id]['table_name'] = $dao->table_name;
           $fields[$dao->id]['column_name'] = $dao->column_name;
+          $fields[$dao->id]['serialize'] = $serializeField ? $dao->serialize : (int) self::isSerialized($dao);
           $fields[$dao->id]['where'] = $dao->table_name . '.' . $dao->column_name;
           // Probably we should use a different fn to get the extends tables but this is a refactor so not changing that now.
           $fields[$dao->id]['extends_table'] = array_key_exists($dao->extends, CRM_Core_BAO_CustomQuery::$extendsMap) ? CRM_Core_BAO_CustomQuery::$extendsMap[$dao->extends] : '';
@@ -592,6 +613,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
    *
    * @return CRM_Core_BAO_CustomField
    *   The field object.
+   * @throws CRM_Core_Exception
    */
   public static function getFieldObject($fieldID) {
     $field = new CRM_Core_BAO_CustomField();
@@ -603,7 +625,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
     if (empty($fieldValues)) {
       $field->id = $fieldID;
       if (!$field->find(TRUE)) {
-        CRM_Core_Error::fatal();
+        throw new CRM_Core_Exception('Cannot find Custom Field');
       }
 
       $fieldValues = [];
@@ -659,11 +681,8 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
     // FIXME: Why are select state/country separate widget types?
     $isSelect = (in_array($widget, [
       'Select',
-      'Multi-Select',
       'Select State/Province',
-      'Multi-Select State/Province',
       'Select Country',
-      'Multi-Select Country',
       'CheckBox',
       'Radio',
     ]));
@@ -680,7 +699,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
       $selectAttributes = ['class' => 'crm-select2'];
 
       // Search field is always multi-select
-      if ($search || strpos($field->html_type, 'Multi') !== FALSE) {
+      if ($search || (self::isSerialized($field) && $widget === 'Select')) {
         $selectAttributes['class'] .= ' huge';
         $selectAttributes['multiple'] = 'multiple';
         $selectAttributes['placeholder'] = $placeholder;
@@ -874,7 +893,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
       case 'Autocomplete-Select':
         static $customUrls = [];
         // Fixme: why is this a string in the first place??
-        $attributes = array();
+        $attributes = [];
         if ($field->attributes) {
           foreach (explode(' ', $field->attributes) as $at) {
             if (strpos($at, '=')) {
@@ -1053,9 +1072,6 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
       case 'Select Country':
       case 'Select State/Province':
       case 'CheckBox':
-      case 'Multi-Select':
-      case 'Multi-Select State/Province':
-      case 'Multi-Select Country':
         if ($field['data_type'] == 'ContactReference' && $value) {
           if (is_numeric($value)) {
             $display = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $value, 'display_name');
@@ -1233,7 +1249,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
     if ($customField->data_type == 'Money' && isset($value)) {
       $value = number_format($value, 2);
     }
-    if (self::isSerialized($customField)) {
+    if (self::isSerialized($customField) && $value) {
       $customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldId, FALSE);
       $defaults[$elementName] = [];
       $checkedValue = CRM_Utils_Array::explodePadded($value);
@@ -1773,11 +1789,13 @@ WHERE  id IN ( %1, %2 )
    *   FK to civicrm_custom_field.
    * @param int $newGroupID
    *   FK to civicrm_custom_group.
+   *
+   * @throws CRM_Core_Exception
    */
   public static function moveField($fieldID, $newGroupID) {
     $validation = self::_moveFieldValidate($fieldID, $newGroupID);
     if (TRUE !== $validation) {
-      CRM_Core_Error::fatal(implode(' ', $validation));
+      throw new CRM_Core_Exception(implode(' ', $validation));
     }
     $field = new CRM_Core_DAO_CustomField();
     $field->id = $fieldID;
@@ -1887,7 +1905,12 @@ WHERE  id IN ( %1, %2 )
       $params['date_format'] = Civi::settings()->get('dateInputFormat');
     }
 
-    if ($htmlType === 'CheckBox' || $htmlType === 'Multi-Select') {
+    // Checkboxes are always serialized in current schema
+    if ($htmlType == 'CheckBox') {
+      $params['serialize'] = CRM_Core_DAO::SERIALIZE_SEPARATOR_BOOKEND;
+    }
+
+    if (!empty($params['serialize'])) {
       if (isset($params['default_checkbox_option'])) {
         $defaultArray = [];
         foreach (array_keys($params['default_checkbox_option']) as $k => $v) {
@@ -1941,7 +1964,7 @@ WHERE  id IN ( %1, %2 )
       // retrieve it from one of the other custom fields which use this option group
       if (empty($params['default_value'])) {
         //don't insert only value separator as default value, CRM-4579
-        $defaultValue = self::getOptionGroupDefault($params['option_group_id'], $htmlType);
+        $defaultValue = self::getOptionGroupDefault($params['option_group_id'], !empty($params['serialize']));
 
         if (!CRM_Utils_System::isNull(explode(CRM_Core_DAO::VALUE_SEPARATOR, $defaultValue))) {
           $params['default_value'] = $defaultValue;
@@ -2061,7 +2084,7 @@ WHERE  id IN ( %1, %2 )
    *
    * @return array
    *   fatal is fieldID does not exists, else array of tableName, columnName
-   * @throws \Exception
+   * @throws \CRM_Core_Exception
    */
   public static function getTableColumnGroup($fieldID, $force = FALSE) {
     $cacheKey = "CRM_Core_DAO_CustomField_CustomGroup_TableColumn_{$fieldID}";
@@ -2078,7 +2101,7 @@ AND    cf.id = %1";
       $dao = CRM_Core_DAO::executeQuery($query, $params);
 
       if (!$dao->fetch()) {
-        CRM_Core_Error::fatal();
+        throw new CRM_Core_Exception("Cannot find table and column information for Custom Field " . $fieldID);
       }
       $fieldValues = [$dao->table_name, $dao->column_name, $dao->id];
       $cache->set($cacheKey, $fieldValues);
@@ -2200,49 +2223,33 @@ WHERE  option_group_id = {$optionGroupId}";
    * Get option group default.
    *
    * @param int $optionGroupId
-   * @param string $htmlType
+   * @param bool $serialize
    *
    * @return null|string
    */
-  public static function getOptionGroupDefault($optionGroupId, $htmlType) {
+  public static function getOptionGroupDefault($optionGroupId, $serialize) {
     $query = "
-SELECT   default_value, html_type
+SELECT   default_value, serialize
 FROM     civicrm_custom_field
 WHERE    option_group_id = {$optionGroupId}
-AND      default_value IS NOT NULL
-ORDER BY html_type";
+AND      default_value IS NOT NULL";
 
     $dao = CRM_Core_DAO::executeQuery($query);
-    $defaultValue = NULL;
-    $defaultHTMLType = NULL;
     while ($dao->fetch()) {
-      if ($dao->html_type == $htmlType) {
+      if ($dao->serialize == $serialize) {
         return $dao->default_value;
       }
-      if ($defaultValue == NULL) {
-        $defaultValue = $dao->default_value;
-        $defaultHTMLType = $dao->html_type;
-      }
+      $defaultValue = $dao->default_value;
     }
 
-    // some conversions are needed if either the old or new has a html type which has potential
-    // multiple default values.
-    if (($htmlType == 'CheckBox' || $htmlType == 'Multi-Select') &&
-      ($defaultHTMLType != 'CheckBox' && $defaultHTMLType != 'Multi-Select')
-    ) {
-      $defaultValue = CRM_Core_DAO::VALUE_SEPARATOR . $defaultValue . CRM_Core_DAO::VALUE_SEPARATOR;
+    // Convert serialization
+    if (isset($defaultValue) && $serialize) {
+      return CRM_Utils_Array::implodePadded([$defaultValue]);
     }
-    elseif (($defaultHTMLType == 'CheckBox' || $defaultHTMLType == 'Multi-Select') &&
-      ($htmlType != 'CheckBox' && $htmlType != 'Multi-Select')
-    ) {
-      $defaultValue = substr($defaultValue, 1, -1);
-      $values = explode(CRM_Core_DAO::VALUE_SEPARATOR,
-        substr($defaultValue, 1, -1)
-      );
-      $defaultValue = $values[0];
+    elseif (isset($defaultValue)) {
+      return CRM_Utils_Array::explodePadded($defaultValue)[0];
     }
-
-    return $defaultValue;
+    return NULL;
   }
 
   /**
@@ -2529,15 +2536,20 @@ WHERE cf.id = %1 AND cg.is_multiple = 1";
   /**
    * Does this field store a serialized string?
    *
-   * @param array|object $field
+   * @param CRM_Core_DAO_CustomField|array $field
    *
    * @return bool
    */
   public static function isSerialized($field) {
     // Fields retrieved via api are an array, or from the dao are an object. We'll accept either.
     $html_type = is_object($field) ? $field->html_type : $field['html_type'];
-    // FIXME: Currently the only way to know if data is serialized is by looking at the html_type. It would be cleaner to decouple this.
-    return ($html_type === 'CheckBox' || strpos($html_type, 'Multi') !== FALSE);
+    // APIv3 has a "legacy" mode where it returns old-style html_type of "Multi-Select"
+    // If anyone is using this function in conjunction with legacy api output, we'll accomodate:
+    if ($html_type === 'CheckBox' || strpos($html_type, 'Multi') !== FALSE) {
+      return TRUE;
+    }
+    // Otherwise this is the new standard as of 5.27
+    return is_object($field) ? !empty($field->serialize) : !empty($field['serialize']);
   }
 
   /**
diff --git a/civicrm/CRM/Core/BAO/CustomGroup.php b/civicrm/CRM/Core/BAO/CustomGroup.php
index 4dc6d36f3fece9c2c004c78c2df2204ff23ce97c..0f08b2779845a96f62783cc979f69d4921524cd2 100644
--- a/civicrm/CRM/Core/BAO/CustomGroup.php
+++ b/civicrm/CRM/Core/BAO/CustomGroup.php
@@ -399,6 +399,7 @@ class CRM_Core_BAO_CustomGroup extends CRM_Core_DAO_CustomGroup {
         'time_format',
         'option_group_id',
         'in_selector',
+        'serialize',
       ],
       'custom_group' => [
         'id',
@@ -1533,7 +1534,6 @@ ORDER BY civicrm_custom_group.weight,
     $form->assign_by_ref("{$prefix}groupTree", $groupTree);
 
     foreach ($groupTree as $id => $group) {
-      CRM_Core_ShowHideBlocks::links($form, $group['title'], '', '');
       foreach ($group['fields'] as $field) {
         $required = $field['is_required'] ?? NULL;
         //fix for CRM-1620
diff --git a/civicrm/CRM/Core/BAO/CustomOption.php b/civicrm/CRM/Core/BAO/CustomOption.php
index 31c522bc7712ca608c5fe29b82c0c1a25f58926f..e876436f75ff03a34da1d192348282ea46563921 100644
--- a/civicrm/CRM/Core/BAO/CustomOption.php
+++ b/civicrm/CRM/Core/BAO/CustomOption.php
@@ -138,20 +138,10 @@ class CRM_Core_BAO_CustomOption {
       }
 
       if (in_array($field->html_type, ['CheckBox', 'Multi-Select'])) {
-        if (isset($defVal) && in_array($dao->value, $defVal)) {
-          $options[$dao->id]['is_default'] = '<img src="' . $config->resourceBase . 'i/check.gif" />';
-        }
-        else {
-          $options[$dao->id]['is_default'] = '';
-        }
+        $options[$dao->id]['is_default'] = (isset($defVal) && in_array($dao->value, $defVal));
       }
       else {
-        if ($field->default_value == $dao->value) {
-          $options[$dao->id]['is_default'] = '<img src="' . $config->resourceBase . 'i/check.gif" />';
-        }
-        else {
-          $options[$dao->id]['is_default'] = '';
-        }
+        $options[$dao->id]['is_default'] = ($field->default_value == $dao->value);
       }
       $options[$dao->id]['description'] = $dao->description;
       $options[$dao->id]['class'] = $dao->id . ',' . $class;
@@ -287,7 +277,7 @@ SET    {$dao->columnName} = REPLACE( {$dao->columnName}, %1, %2 )";
           break;
 
         default:
-          CRM_Core_Error::fatal();
+          throw new CRM_Core_Exception('Invalid HTML Type');
       }
       $dao = CRM_Core_DAO::executeQuery($query, $queryParams);
     }
diff --git a/civicrm/CRM/Core/BAO/CustomValue.php b/civicrm/CRM/Core/BAO/CustomValue.php
index 8cbe6b3d08a89a8cc2d71619c09a9c48c76a6bf0..3a64cdb29d3b57c3f1a09e9120664b726c1433ae 100644
--- a/civicrm/CRM/Core/BAO/CustomValue.php
+++ b/civicrm/CRM/Core/BAO/CustomValue.php
@@ -206,4 +206,16 @@ class CRM_Core_BAO_CustomValue extends CRM_Core_DAO {
     );
   }
 
+  /**
+   * ACL clause for an APIv4 custom pseudo-entity (aka multi-record custom group extending Contact).
+   * @return array
+   */
+  public function addSelectWhereClause() {
+    $clauses = [
+      'entity_id' => CRM_Utils_SQL::mergeSubquery('Contact'),
+    ];
+    CRM_Utils_Hook::selectWhereClause($this, $clauses);
+    return $clauses;
+  }
+
 }
diff --git a/civicrm/CRM/Core/BAO/CustomValueTable.php b/civicrm/CRM/Core/BAO/CustomValueTable.php
index 1fa9a508a82df987e017fcb2d3b8e9bea2380330..0a724557b86e4a90eef27b879bf604b0fe3193df 100644
--- a/civicrm/CRM/Core/BAO/CustomValueTable.php
+++ b/civicrm/CRM/Core/BAO/CustomValueTable.php
@@ -153,7 +153,7 @@ class CRM_Core_BAO_CustomValueTable {
 
             case 'File':
               if (!$field['file_id']) {
-                CRM_Core_Error::fatal();
+                throw new CRM_Core_Exception('Missing parameter file_id');
               }
 
               // need to add/update civicrm_entity_file
@@ -318,7 +318,7 @@ class CRM_Core_BAO_CustomValueTable {
         return 'datetime';
 
       default:
-        CRM_Core_Error::fatal();
+        throw new CRM_Core_Exception('Invalid Field Type');
     }
   }
 
@@ -415,12 +415,13 @@ class CRM_Core_BAO_CustomValueTable {
    *   Array of custom values for the entity with key=>value
    *                                   pairs specified as civicrm_custom_field.id => custom value.
    *                                   Empty array if no custom values found.
+   * @throws CRM_Core_Exception
    */
   public static function &getEntityValues($entityID, $entityType = NULL, $fieldIDs = NULL, $formatMultiRecordField = FALSE, $DTparams = NULL) {
     if (!$entityID) {
       // adding this here since an empty contact id could have serious repurcussions
       // like looping forever
-      CRM_Core_Error::fatal('Please file an issue with the backtrace');
+      throw new CRM_Core_Exception('Please file an issue with the backtrace');
       return NULL;
     }
 
diff --git a/civicrm/CRM/Core/BAO/Discount.php b/civicrm/CRM/Core/BAO/Discount.php
index ce24c32e4e4888b3e9c601534f007a44dffa50b5..812747eb42466b68edfc2893c488e73535e688bd 100644
--- a/civicrm/CRM/Core/BAO/Discount.php
+++ b/civicrm/CRM/Core/BAO/Discount.php
@@ -98,11 +98,12 @@ class CRM_Core_BAO_Discount extends CRM_Core_DAO_Discount {
    * @return int
    *   $dao->id       discount id of the set which matches
    *                                 the date criteria
+   * @throws CRM_Core_Exception
    */
   public static function findSet($entityID, $entityTable) {
     if (empty($entityID) || empty($entityTable)) {
       // adding this here, to trap errors if values are not sent
-      CRM_Core_Error::fatal();
+      throw new CRM_Core_Exception('Invalid parameters passed to findSet function');
       return NULL;
     }
 
diff --git a/civicrm/CRM/Core/BAO/Domain.php b/civicrm/CRM/Core/BAO/Domain.php
index 5f1f508cbac38f362cbc4af0c6bac570708bf484..14e885a6e2eaa2da6d62d9e566182998b764bd7c 100644
--- a/civicrm/CRM/Core/BAO/Domain.php
+++ b/civicrm/CRM/Core/BAO/Domain.php
@@ -82,6 +82,29 @@ class CRM_Core_BAO_Domain extends CRM_Core_DAO_Domain {
     );
   }
 
+  /**
+   * Is a database update required to apply latest schema changes.
+   *
+   * @return bool
+   *
+   * @throws \CRM_Core_Exception
+   */
+  public static function isDBUpdateRequired() {
+    $dbVersion = CRM_Core_BAO_Domain::version();
+    $codeVersion = CRM_Utils_System::version();
+    return version_compare($dbVersion, $codeVersion) < 0;
+  }
+
+  /**
+   * Checks that the current DB schema is at least $min version
+   *
+   * @param string|number $min
+   * @return bool
+   */
+  public static function isDBVersionAtLeast($min) {
+    return version_compare(self::version(), $min, '>=');
+  }
+
   /**
    * Get the location values of a domain.
    *
diff --git a/civicrm/CRM/Core/BAO/File.php b/civicrm/CRM/Core/BAO/File.php
index 3e78e3ffeaa9a8be4466f13650fcf60269b068e1..c2de58619779db2462ef5b876a99e32327a24c20 100644
--- a/civicrm/CRM/Core/BAO/File.php
+++ b/civicrm/CRM/Core/BAO/File.php
@@ -677,7 +677,7 @@ AND       CEF.entity_id    = %2";
 
   /**
    * Delete a file attachment from an entity table / entity ID
-   *
+   * @throws CRM_Core_Exception
    */
   public static function deleteAttachment() {
     $params = [];
@@ -689,7 +689,7 @@ AND       CEF.entity_id    = %2";
 
     $signer = new CRM_Utils_Signer(CRM_Core_Key::privateKey(), self::$_signableFields);
     if (!$signer->validate($signature, $params)) {
-      CRM_Core_Error::fatal('Request signature is invalid');
+      throw new CRM_Core_Exception('Request signature is invalid');
     }
 
     self::deleteEntityFile($params['entityTable'], $params['entityID'], NULL, $params['fileID']);
@@ -726,17 +726,19 @@ AND       CEF.entity_id    = %2";
           $fileType == 'image/x-png' ||
           $fileType == 'image/png'
         ) {
-          $file_url[$fileID] = "
-              <a href='$url' class='crm-image-popup' title='$title'>
-                <i class='crm-i fa-file-image-o'></i>
-              </a>";
+          $file_url[$fileID] = <<<HEREDOC
+              <a href="$url" class="crm-image-popup" title="$title">
+                <i class="crm-i fa-file-image-o" aria-hidden="true"></i>
+              </a>
+HEREDOC;
         }
         // for non image files
         else {
-          $file_url[$fileID] = "
-              <a href='$url' title='$title'>
-                <i class='crm-i fa-paperclip'></i>
-              </a>";
+          $file_url[$fileID] = <<<HEREDOC
+              <a href="$url" title="$title">
+                <i class="crm-i fa-paperclip" aria-hidden="true"></i>
+              </a>
+HEREDOC;
         }
       }
     }
diff --git a/civicrm/CRM/Core/BAO/Job.php b/civicrm/CRM/Core/BAO/Job.php
index 5609b3075f0270f4133a8bcb1ccfc427782aad97..c4214e60b5263f2dac6cd91be14521c30dbe85b7 100644
--- a/civicrm/CRM/Core/BAO/Job.php
+++ b/civicrm/CRM/Core/BAO/Job.php
@@ -88,10 +88,11 @@ class CRM_Core_BAO_Job extends CRM_Core_DAO_Job {
    *   ID of the job to be deleted.
    *
    * @return bool|null
+   * @throws CRM_Core_Exception
    */
   public static function del($jobID) {
     if (!$jobID) {
-      CRM_Core_Error::fatal(ts('Invalid value passed to delete function.'));
+      throw new CRM_Core_Exception(ts('Invalid value passed to delete function.'));
     }
 
     $dao = new CRM_Core_DAO_Job();
@@ -117,15 +118,16 @@ class CRM_Core_BAO_Job extends CRM_Core_DAO_Job {
     // Prevent the job log from getting too big
     // For now, keep last minDays days and at least maxEntries records
     $query = 'SELECT COUNT(*) FROM civicrm_job_log';
-    $count = CRM_Core_DAO::singleValueQuery($query);
+    $count = (int) CRM_Core_DAO::singleValueQuery($query);
 
     if ($count <= $maxEntriesToKeep) {
       return;
     }
 
-    $count = $count - $maxEntriesToKeep;
+    $count = $count - (int) $maxEntriesToKeep;
 
-    $query = "DELETE FROM civicrm_job_log WHERE run_time < SUBDATE(NOW(), $minDaysToKeep) LIMIT $count";
+    $minDaysToKeep = (int) $minDaysToKeep;
+    $query = "DELETE FROM civicrm_job_log WHERE run_time < SUBDATE(NOW(), $minDaysToKeep) ORDER BY id LIMIT $count";
     CRM_Core_DAO::executeQuery($query);
   }
 
diff --git a/civicrm/CRM/Core/BAO/LabelFormat.php b/civicrm/CRM/Core/BAO/LabelFormat.php
index 175d599410bfe207560ab21264a09c271cd8fcf0..8678560c9cf30a693035e0d4ea34b5872fda3168 100644
--- a/civicrm/CRM/Core/BAO/LabelFormat.php
+++ b/civicrm/CRM/Core/BAO/LabelFormat.php
@@ -527,7 +527,7 @@ class CRM_Core_BAO_LabelFormat extends CRM_Core_DAO_OptionValue {
     // make sure serialized array will fit in the 'value' column
     $attribute = CRM_Core_DAO::getAttribute('CRM_Core_BAO_LabelFormat', 'value');
     if (strlen($this->value) > $attribute['maxlength']) {
-      CRM_Core_Error::fatal(ts('Label Format does not fit in database.'));
+      throw new CRM_Core_Exception(ts('Label Format does not fit in database.'));
     }
     $this->save();
 
diff --git a/civicrm/CRM/Core/BAO/Location.php b/civicrm/CRM/Core/BAO/Location.php
index ec50cd15f04c81a462d77632c6e0af0750030625..e2d393976cf114715d0b1406dcd6bf0edfcfda6e 100644
--- a/civicrm/CRM/Core/BAO/Location.php
+++ b/civicrm/CRM/Core/BAO/Location.php
@@ -260,13 +260,14 @@ WHERE e.id = %1";
    *   Contact id.
    * @param int $locationTypeId
    *   Id of the location to delete.
+   * @throws CRM_Core_Exception
    */
   public static function deleteLocationBlocks($contactId, $locationTypeId) {
     // ensure that contactId has a value
     if (empty($contactId) ||
       !CRM_Utils_Rule::positiveInteger($contactId)
     ) {
-      CRM_Core_Error::fatal();
+      throw new CRM_Core_Exception('Incorrect contact id parameter passed to deleteLocationBlocks');
     }
 
     if (empty($locationTypeId) ||
diff --git a/civicrm/CRM/Core/BAO/Mapping.php b/civicrm/CRM/Core/BAO/Mapping.php
index 19c6ec2290cd8cff27b851cdc0386c2522be4a1d..309972bb6aaf35c420a29862b5c77056d3506823 100644
--- a/civicrm/CRM/Core/BAO/Mapping.php
+++ b/civicrm/CRM/Core/BAO/Mapping.php
@@ -151,6 +151,7 @@ class CRM_Core_BAO_Mapping extends CRM_Core_DAO_Mapping {
         civicrm_api3('OptionValue', 'create', [
           'option_group_id' => 'mapping_type',
           'label' => $mappingType,
+          'name' => $mappingType,
           'value' => max(array_keys($mappingValues['values'])) + 1,
           'is_reserved' => 1,
         ]);
@@ -1002,6 +1003,7 @@ class CRM_Core_BAO_Mapping extends CRM_Core_DAO_Mapping {
    *
    * @return array
    *   formatted associated array of elements
+   * @throws CRM_Core_Exception
    */
   public static function formattedFields(&$params, $row = FALSE) {
     $fields = [];
@@ -1016,7 +1018,7 @@ class CRM_Core_BAO_Mapping extends CRM_Core_DAO_Mapping {
       foreach ($value as $k => $v) {
         if (in_array($v[0], $types)) {
           if ($contactType && $contactType != $v[0]) {
-            CRM_Core_Error::fatal(ts("Cannot have two clauses with different types: %1, %2",
+            throw new CRM_Core_Exception(ts("Cannot have two clauses with different types: %1, %2",
               [1 => $contactType, 2 => $v[0]]
             ));
           }
diff --git a/civicrm/CRM/Core/BAO/Note.php b/civicrm/CRM/Core/BAO/Note.php
index 86a95fd0800a7ae8ee9303afe0d314a66a6ca18a..e2ed3102e73b56e008715d5bf06de174bb41298d 100644
--- a/civicrm/CRM/Core/BAO/Note.php
+++ b/civicrm/CRM/Core/BAO/Note.php
@@ -126,7 +126,7 @@ class CRM_Core_BAO_Note extends CRM_Core_DAO_Note {
    *   $note CRM_Core_BAO_Note object
    * @throws \CRM_Core_Exception
    */
-  public static function add(&$params, $ids = array()) {
+  public static function add(&$params, $ids = []) {
     $dataExists = self::dataExists($params);
     if (!$dataExists) {
       return NULL;
@@ -350,7 +350,7 @@ class CRM_Core_BAO_Note extends CRM_Core_DAO_Note {
    *
    */
   public static function &getNote($id, $entityTable = 'civicrm_relationship') {
-    $viewNote = array();
+    $viewNote = [];
 
     $query = "
   SELECT  id,
@@ -453,7 +453,7 @@ ORDER BY  modified_date desc";
    * @return array
    *   Nested associative array beginning with direct children of given note.
    */
-  private static function buildNoteTree($parentId, $maxDepth = 0, $snippet = FALSE, &$tree = array(), $depth = 0) {
+  private static function buildNoteTree($parentId, $maxDepth = 0, $snippet = FALSE, &$tree = [], $depth = 0) {
     if ($maxDepth && $depth > $maxDepth) {
       return FALSE;
     }
@@ -517,7 +517,7 @@ ORDER BY  modified_date desc";
    * @return array
    *   One-dimensional array containing ids of all desendent notes
    */
-  public static function getDescendentIds($parentId, &$ids = array()) {
+  public static function getDescendentIds($parentId, &$ids = []) {
     // get direct children of given parentId note
     $note = new CRM_Core_DAO_Note();
     $note->entity_table = 'civicrm_note';
diff --git a/civicrm/CRM/Core/BAO/PaperSize.php b/civicrm/CRM/Core/BAO/PaperSize.php
index c14709e855258815f7aa39e74acf61e076ae5311..f7456bc50df0b5665ca5b919c8cb06137fdda573 100644
--- a/civicrm/CRM/Core/BAO/PaperSize.php
+++ b/civicrm/CRM/Core/BAO/PaperSize.php
@@ -72,12 +72,13 @@ class CRM_Core_BAO_PaperSize extends CRM_Core_DAO_OptionValue {
    *
    * @return int
    *   Group ID (null if Group ID doesn't exist)
+   * @throws CRM_Core_Exception
    */
   private static function _getGid() {
     if (!self::$_gid) {
       self::$_gid = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', 'paper_size', 'id', 'name');
       if (!self::$_gid) {
-        CRM_Core_Error::fatal(ts('Paper Size Option Group not found in database.'));
+        throw new CRM_Core_Exception(ts('Paper Size Option Group not found in database.'));
       }
     }
     return self::$_gid;
@@ -268,6 +269,7 @@ class CRM_Core_BAO_PaperSize extends CRM_Core_DAO_OptionValue {
    * @param array $values associative array of name/value pairs
    * @param int $id
    *   Id of the database record (null = new record).
+   * @throws CRM_Core_Exception
    */
   public function savePaperSize(&$values, $id) {
     // get the Option Group ID for Paper Sizes (create one if it doesn't exist)
@@ -311,7 +313,7 @@ class CRM_Core_BAO_PaperSize extends CRM_Core_DAO_OptionValue {
     // make sure serialized array will fit in the 'value' column
     $attribute = CRM_Core_DAO::getAttribute('CRM_Core_BAO_PaperSize', 'value');
     if (strlen($this->value) > $attribute['maxlength']) {
-      CRM_Core_Error::fatal(ts('Paper Size does not fit in database.'));
+      throw new CRM_Core_Exception(ts('Paper Size does not fit in database.'));
     }
     $this->save();
 
@@ -325,7 +327,7 @@ class CRM_Core_BAO_PaperSize extends CRM_Core_DAO_OptionValue {
    *
    * @param int $id
    *   ID of the Paper Size to be deleted.
-   *
+   * @throws CRM_Core_Exception
    */
   public static function del($id) {
     if ($id) {
@@ -340,7 +342,7 @@ class CRM_Core_BAO_PaperSize extends CRM_Core_DAO_OptionValue {
         }
       }
     }
-    CRM_Core_Error::fatal(ts('Invalid value passed to delete function.'));
+    throw new CRM_Core_Exception(ts('Invalid value passed to delete function.'));
   }
 
 }
diff --git a/civicrm/CRM/Core/BAO/PdfFormat.php b/civicrm/CRM/Core/BAO/PdfFormat.php
index 0703108a7f0a60326c3486966b10b3a1f578ba09..33783d7306c4a87a091f3e4bf5e8e229278d18ab 100644
--- a/civicrm/CRM/Core/BAO/PdfFormat.php
+++ b/civicrm/CRM/Core/BAO/PdfFormat.php
@@ -127,12 +127,13 @@ class CRM_Core_BAO_PdfFormat extends CRM_Core_DAO_OptionValue {
    *
    * @return int
    *   Group ID (null if Group ID doesn't exist)
+   * @throws CRM_Core_Exception
    */
   private static function _getGid() {
     if (!self::$_gid) {
       self::$_gid = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', 'pdf_format', 'id', 'name');
       if (!self::$_gid) {
-        CRM_Core_Error::fatal(ts('PDF Format Option Group not found in database.'));
+        throw new CRM_Core_Exception(ts('PDF Format Option Group not found in database.'));
       }
     }
     return self::$_gid;
@@ -325,6 +326,7 @@ class CRM_Core_BAO_PdfFormat extends CRM_Core_DAO_OptionValue {
    * @param array $values associative array of name/value pairs
    * @param int $id
    *   Id of the database record (null = new record).
+   * @throws CRM_Core_Exception
    */
   public function savePdfFormat(&$values, $id = NULL) {
     // get the Option Group ID for PDF Page Formats (create one if it doesn't exist)
@@ -364,7 +366,7 @@ class CRM_Core_BAO_PdfFormat extends CRM_Core_DAO_OptionValue {
     // make sure serialized array will fit in the 'value' column
     $attribute = CRM_Core_DAO::getAttribute('CRM_Core_BAO_PdfFormat', 'value');
     if (strlen($this->value) > $attribute['maxlength']) {
-      CRM_Core_Error::fatal(ts('PDF Page Format does not fit in database.'));
+      throw new CRM_Core_Exception(ts('PDF Page Format does not fit in database.'));
     }
     $this->save();
 
@@ -378,7 +380,7 @@ class CRM_Core_BAO_PdfFormat extends CRM_Core_DAO_OptionValue {
    *
    * @param int $id
    *   ID of the PDF Page Format to be deleted.
-   *
+   * @throws CRM_Core_Exception
    */
   public static function del($id) {
     if ($id) {
@@ -393,7 +395,7 @@ class CRM_Core_BAO_PdfFormat extends CRM_Core_DAO_OptionValue {
         }
       }
     }
-    CRM_Core_Error::fatal(ts('Invalid value passed to delete function.'));
+    throw new CRM_Core_Exception(ts('Invalid value passed to delete function.'));
   }
 
 }
diff --git a/civicrm/CRM/Core/BAO/Phone.php b/civicrm/CRM/Core/BAO/Phone.php
index bf4dd2c0431f581eee74bafab3867da722b5db86..f5851eee26cf4559c3cefdc87ecc1cc98a42e038 100644
--- a/civicrm/CRM/Core/BAO/Phone.php
+++ b/civicrm/CRM/Core/BAO/Phone.php
@@ -57,15 +57,7 @@ class CRM_Core_BAO_Phone extends CRM_Core_DAO_Phone {
     // Ensure mysql phone function exists
     CRM_Core_DAO::checkSqlFunctionsExist();
 
-    $hook = empty($params['id']) ? 'create' : 'edit';
-    CRM_Utils_Hook::pre($hook, 'Phone', $params['id'] ?? NULL, $params);
-
-    $phone = new CRM_Core_DAO_Phone();
-    $phone->copyValues($params);
-    $phone->save();
-
-    CRM_Utils_Hook::post($hook, 'Phone', $phone->id, $phone);
-    return $phone;
+    return self::writeRecord($params);
   }
 
   /**
diff --git a/civicrm/CRM/Core/BAO/PreferencesDate.php b/civicrm/CRM/Core/BAO/PreferencesDate.php
index 513bba5dd5174d8ded6b8f16cd1c454ddec12863..b05bbf4a15488ec5981828e45e496bda0868da3c 100644
--- a/civicrm/CRM/Core/BAO/PreferencesDate.php
+++ b/civicrm/CRM/Core/BAO/PreferencesDate.php
@@ -57,18 +57,20 @@ class CRM_Core_BAO_PreferencesDate extends CRM_Core_DAO_PreferencesDate {
    *   Id of the database record.
    * @param bool $is_active
    *   Value we want to set the is_active field.
+   * @throws CRM_Core_Exception
    */
   public static function setIsActive($id, $is_active) {
-    CRM_Core_Error::fatal();
+    throw new CRM_Core_Exception('Cannot call setIsActive function');
   }
 
   /**
    * Delete preference dates.
    *
    * @param int $id
+   * @throws CRM_Core_Exception
    */
   public static function del($id) {
-    CRM_Core_Error::fatal();
+    throw new CRM_Core_Exception('Cannot call del function');
   }
 
   /**
diff --git a/civicrm/CRM/Core/BAO/RecurringEntity.php b/civicrm/CRM/Core/BAO/RecurringEntity.php
index f4b1622de908feadb54a23f5d4a7937a15ad93d4..fceb1237b154f3e87b3518d2b1ff09c01cc7bf75 100644
--- a/civicrm/CRM/Core/BAO/RecurringEntity.php
+++ b/civicrm/CRM/Core/BAO/RecurringEntity.php
@@ -224,6 +224,7 @@ class CRM_Core_BAO_RecurringEntity extends CRM_Core_DAO_RecurringEntity {
    * Generate new DAOs and along with entries in civicrm_recurring_entity table.
    *
    * @return array
+   * @throws CRM_Core_Exception
    */
   public function generateEntities() {
     self::setStatus(self::RUNNING);
@@ -241,7 +242,7 @@ class CRM_Core_BAO_RecurringEntity extends CRM_Core_DAO_RecurringEntity {
         }
       }
       if (empty($findCriteria)) {
-        CRM_Core_Error::fatal("Find criteria missing to generate form. Make sure entity_id and table is set.");
+        throw new CRM_Core_Exception("Find criteria missing to generate form. Make sure entity_id and table is set.");
       }
 
       $count = 0;
@@ -547,11 +548,12 @@ class CRM_Core_BAO_RecurringEntity extends CRM_Core_DAO_RecurringEntity {
    *
    *
    * @return object
+   * @throws new CRM_Core_Exception
    */
   public static function copyCreateEntity($entityTable, $fromCriteria, $newParams, $createRecurringEntity = TRUE) {
     $daoName = self::$_tableDAOMapper[$entityTable];
     if (!$daoName) {
-      CRM_Core_Error::fatal("DAO Mapper missing for $entityTable.");
+      throw new CRM_Core_Exception("DAO Mapper missing for $entityTable.");
     }
     $newObject = CRM_Core_DAO::copyGeneric($daoName, $fromCriteria, $newParams);
 
@@ -631,7 +633,7 @@ class CRM_Core_BAO_RecurringEntity extends CRM_Core_DAO_RecurringEntity {
         $updateDAO = CRM_Core_DAO::cascadeUpdate($daoName, $obj->id, $entityID, $skipData);
       }
       else {
-        CRM_Core_Error::fatal("DAO Mapper missing for $entityTable.");
+        throw new CRM_Core_Exception("DAO Mapper missing for $entityTable.");
       }
     }
     // done with processing. lets unset static var.
@@ -811,7 +813,7 @@ class CRM_Core_BAO_RecurringEntity extends CRM_Core_DAO_RecurringEntity {
         foreach (self::$_linkedEntitiesInfo as $linkedTable => $linfo) {
           $daoName = self::$_tableDAOMapper[$linkedTable];
           if (!$daoName) {
-            CRM_Core_Error::fatal("DAO Mapper missing for $linkedTable.");
+            throw new CRM_Core_Exception("DAO Mapper missing for $linkedTable.");
           }
 
           $linkedDao = new $daoName();
diff --git a/civicrm/CRM/Core/BAO/SchemaHandler.php b/civicrm/CRM/Core/BAO/SchemaHandler.php
index c6757015f02e7bca13c0fd03be7cb58a25b60257..b9f3bad91f356e530f8dc01ec8a3331e7d168fb5 100644
--- a/civicrm/CRM/Core/BAO/SchemaHandler.php
+++ b/civicrm/CRM/Core/BAO/SchemaHandler.php
@@ -502,7 +502,7 @@ ADD UNIQUE INDEX `unique_entity_id` ( `entity_id` )";
    * @param string $columnName
    * @param $length
    *
-   * @throws Exception
+   * @throws CRM_Core_Exception
    */
   public static function alterFieldLength($customFieldID, $tableName, $columnName, $length) {
     // first update the custom field tables
@@ -543,7 +543,7 @@ MODIFY      {$columnName} varchar( $length )
       CRM_Core_DAO::executeQuery($sql);
     }
     else {
-      CRM_Core_Error::fatal(ts('Could Not Find Custom Field Details for %1, %2, %3',
+      throw new CRM_Core_Exception(ts('Could Not Find Custom Field Details for %1, %2, %3',
         [
           1 => $tableName,
           2 => $columnName,
@@ -866,7 +866,7 @@ MODIFY      {$columnName} varchar( $length )
       }
       $query .= " CHARACTER SET = $newCharSet COLLATE = $tableCollation";
       if ($param['Engine'] === 'InnoDB') {
-        $query .= ' ROW_FORMAT = Dynamic';
+        $query .= ' ROW_FORMAT = Dynamic KEY_BLOCK_SIZE = 0';
       }
       // Disable i18n rewrite.
       CRM_Core_DAO::executeQuery($query, $params, TRUE, NULL, FALSE, FALSE);
diff --git a/civicrm/CRM/Core/BAO/Setting.php b/civicrm/CRM/Core/BAO/Setting.php
index 7518e37dc2fdcc1d47f1e92999474776a7dc43f9..c6b18e74e9b2b77e1d22eab805b65de030fca4ee 100644
--- a/civicrm/CRM/Core/BAO/Setting.php
+++ b/civicrm/CRM/Core/BAO/Setting.php
@@ -515,7 +515,7 @@ class CRM_Core_BAO_Setting extends CRM_Core_DAO_Setting {
   public static function onChangeEnvironmentSetting($oldValue, $newValue, $metadata) {
     if ($newValue != 'Production') {
       $mailing = Civi::settings()->get('mailing_backend');
-      if ($mailing['outBound_option'] != 2) {
+      if ($mailing['outBound_option'] != CRM_Mailing_Config::OUTBOUND_OPTION_DISABLED) {
         Civi::settings()->set('mailing_backend_store', $mailing);
       }
       Civi::settings()->set('mailing_backend', ['outBound_option' => CRM_Mailing_Config::OUTBOUND_OPTION_DISABLED]);
diff --git a/civicrm/CRM/Core/BAO/StatusPreference.php b/civicrm/CRM/Core/BAO/StatusPreference.php
index 2f225ee9fe2e7241d00cfed80cf3d2ff337f0d65..d4e182eadc31ad5f76900919f303299451233e39 100644
--- a/civicrm/CRM/Core/BAO/StatusPreference.php
+++ b/civicrm/CRM/Core/BAO/StatusPreference.php
@@ -28,6 +28,7 @@ class CRM_Core_BAO_StatusPreference extends CRM_Core_DAO_StatusPreference {
    * @param array $params
    *
    * @return array
+   * @throws CRM_Core_Exception
    */
   public static function create($params) {
     $statusPreference = new CRM_Core_BAO_StatusPreference();
@@ -42,11 +43,11 @@ class CRM_Core_BAO_StatusPreference extends CRM_Core_DAO_StatusPreference {
       $params['ignore_severity'] = CRM_Utils_Check::severityMap($params['ignore_severity']);
     }
     if ($params['ignore_severity'] > 7) {
-      CRM_Core_Error::fatal(ts('You can not pass a severity level higher than 7.'));
+      throw new CRM_Core_Exception(ts('You can not pass a severity level higher than 7.'));
     }
     // If severity is now blank, you have an invalid severity string.
     if (is_null($params['ignore_severity'])) {
-      CRM_Core_Error::fatal(ts('Invalid string passed as severity level.'));
+      throw new CRM_Core_Exception(ts('Invalid string passed as severity level.'));
     }
 
     // Check if this StatusPreference already exists.
diff --git a/civicrm/CRM/Core/BAO/Tag.php b/civicrm/CRM/Core/BAO/Tag.php
index 2ef3afb41180a662e128aa831ee50eaeb7fef1b5..17c0bc47cceb05d1f8161a967cfa66c2ea293cff 100644
--- a/civicrm/CRM/Core/BAO/Tag.php
+++ b/civicrm/CRM/Core/BAO/Tag.php
@@ -412,8 +412,6 @@ class CRM_Core_BAO_Tag extends CRM_Core_DAO_Tag {
       }
     }
 
-    $tag = new CRM_Core_DAO_Tag();
-
     // if parent id is set then inherit used for and is hidden properties
     if (!empty($params['parent_id'])) {
       // get parent details
@@ -423,34 +421,28 @@ class CRM_Core_BAO_Tag extends CRM_Core_DAO_Tag {
       $params['used_for'] = implode(',', $params['used_for']);
     }
 
+    // Hack to make white null, because html5 color widget can't be empty
     if (isset($params['color']) && strtolower($params['color']) === '#ffffff') {
       $params['color'] = '';
     }
 
-    $tag->copyValues($params);
-    $tag->id = $id;
-    $hook = !$id ? 'create' : 'edit';
-    CRM_Utils_Hook::pre($hook, 'Tag', $tag->id, $params);
-
     // save creator id and time
-    if (!$tag->id) {
-      $session = CRM_Core_Session::singleton();
-      $tag->created_id = $session->get('userID');
-      $tag->created_date = date('YmdHis');
+    if (!$id) {
+      $params['created_id'] = $params['created_id'] ?? CRM_Core_Session::getLoggedInContactID();
+      $params['created_date'] = $params['created_date'] ?? date('YmdHis');
     }
 
-    $tag->save();
-    CRM_Utils_Hook::post($hook, 'Tag', $tag->id, $tag);
+    $tag = self::writeRecord($params);
 
     // if we modify parent tag, then we need to update all children
-    $tag->find(TRUE);
-    if (!$tag->parent_id && $tag->used_for) {
-      CRM_Core_DAO::executeQuery("UPDATE civicrm_tag SET used_for=%1 WHERE parent_id = %2",
-        [
+    if ($id) {
+      $tag->find(TRUE);
+      if (!$tag->parent_id && $tag->used_for) {
+        CRM_Core_DAO::executeQuery("UPDATE civicrm_tag SET used_for=%1 WHERE parent_id = %2", [
           1 => [$tag->used_for, 'String'],
           2 => [$tag->id, 'Integer'],
-        ]
-      );
+        ]);
+      }
     }
 
     CRM_Core_PseudoConstant::flush();
@@ -462,11 +454,10 @@ class CRM_Core_BAO_Tag extends CRM_Core_DAO_Tag {
    * Check if there is data to create the object.
    *
    * @param array $params
-   *   (reference ) an assoc array of name/value pairs.
    *
    * @return bool
    */
-  public static function dataExists(&$params) {
+  public static function dataExists($params) {
     // Disallow empty values except for the number zero.
     // TODO: create a utility for this since it's needed in many places
     if (!empty($params['name']) || (string) $params['name'] === '0') {
diff --git a/civicrm/CRM/Core/BAO/UFGroup.php b/civicrm/CRM/Core/BAO/UFGroup.php
index d8a3d6c8ed43845c45a255a897d31fe59492baa9..5841ab0d72451a4c9b3585fb0e582a30fc7ce960 100644
--- a/civicrm/CRM/Core/BAO/UFGroup.php
+++ b/civicrm/CRM/Core/BAO/UFGroup.php
@@ -516,6 +516,7 @@ class CRM_Core_BAO_UFGroup extends CRM_Core_DAO_UFGroup {
       // if field is not present in customFields, that means the user
       // DOES NOT HAVE permission to access that field
       if (array_key_exists($field->field_name, $customFields)) {
+        $formattedField['serialize'] = !empty($customFields[$field->field_name]['serialize']);
         $formattedField['is_search_range'] = $customFields[$field->field_name]['is_search_range'];
         // fix for CRM-1994
         $formattedField['options_per_line'] = $customFields[$field->field_name]['options_per_line'];
@@ -2699,7 +2700,7 @@ AND    ( entity_id IS NULL OR entity_id <= 0 )
 
     if (!$domainEmailAddress || $domainEmailAddress == 'info@EXAMPLE.ORG') {
       $fixUrl = CRM_Utils_System::url('civicrm/admin/domain', 'action=update&reset=1');
-      CRM_Core_Error::fatal(ts('The site administrator needs to enter a valid \'FROM Email Address\' in <a href="%1">Administer CiviCRM &raquo; Communications &raquo; FROM Email Addresses</a>. The email address used may need to be a valid mail account with your email service provider.', [1 => $fixUrl]));
+      CRM_Core_Error::statusBounce(ts('The site administrator needs to enter a valid \'FROM Email Address\' in <a href="%1">Administer CiviCRM &raquo; Communications &raquo; FROM Email Addresses</a>. The email address used may need to be a valid mail account with your email service provider.', [1 => $fixUrl]));
     }
 
     foreach ($emailList as $emailTo) {
diff --git a/civicrm/CRM/Core/BAO/UFMatch.php b/civicrm/CRM/Core/BAO/UFMatch.php
index 116ebd141c874a9139e35fb7d86b6390d7457fc9..b7dd9b2c80187275e9022d123fe3c6f4d9038b07 100644
--- a/civicrm/CRM/Core/BAO/UFMatch.php
+++ b/civicrm/CRM/Core/BAO/UFMatch.php
@@ -57,12 +57,14 @@ class CRM_Core_BAO_UFMatch extends CRM_Core_DAO_UFMatch {
    *
    * @param $ctype
    * @param bool $isLogin
+   *
+   * @throws CRM_Core_Exception
    */
   public static function synchronize(&$user, $update, $uf, $ctype, $isLogin = FALSE) {
     $userSystem = CRM_Core_Config::singleton()->userSystem;
     $session = CRM_Core_Session::singleton();
     if (!is_object($session)) {
-      CRM_Core_Error::fatal('wow, session is not an object?');
+      throw new CRM_Core_Exception('wow, session is not an object?');
       return;
     }
 
diff --git a/civicrm/CRM/Core/BAO/WordReplacement.php b/civicrm/CRM/Core/BAO/WordReplacement.php
index d8fe9df6b359fa05c4dcaa8629eaae7d5a35f058..ba4f09d7fa43d4d8dc3a3807728a8305eb4fb87d 100644
--- a/civicrm/CRM/Core/BAO/WordReplacement.php
+++ b/civicrm/CRM/Core/BAO/WordReplacement.php
@@ -51,6 +51,7 @@ class CRM_Core_BAO_WordReplacement extends CRM_Core_DAO_WordReplacement {
    * @param null $reset
    *
    * @return null|CRM_Core_BAO_WordReplacement
+   * @throws CRM_Core_Exception
    */
   public static function getWordReplacement($reset = NULL) {
     static $wordReplacement = NULL;
@@ -58,7 +59,7 @@ class CRM_Core_BAO_WordReplacement extends CRM_Core_DAO_WordReplacement {
       $wordReplacement = new CRM_Core_BAO_WordReplacement();
       $wordReplacement->id = CRM_Core_Config::wordReplacementID();
       if (!$wordReplacement->find(TRUE)) {
-        CRM_Core_Error::fatal();
+        throw new CRM_Core_Exception('Unable to find word replacement');
       }
     }
     return $wordReplacement;
diff --git a/civicrm/CRM/Core/Block.php b/civicrm/CRM/Core/Block.php
index 2194e468827fd6b4661280a16695ca9ec7bbc97f..bbd3b427f83f8878ebcc09c2ee25333fbb421747 100644
--- a/civicrm/CRM/Core/Block.php
+++ b/civicrm/CRM/Core/Block.php
@@ -120,7 +120,7 @@ class CRM_Core_Block {
           'template' => 'LangSwitch.tpl',
           'info' => ts('CiviCRM Language Switcher'),
           'subject' => '',
-          'templateValues' => array(),
+          'templateValues' => [],
           'active' => TRUE,
           'cache' => BLOCK_NO_CACHE,
           'visibility' => 1,
@@ -133,7 +133,7 @@ class CRM_Core_Block {
           'template' => 'Event.tpl',
           'info' => ts('CiviCRM Upcoming Events'),
           'subject' => ts('Upcoming Events'),
-          'templateValues' => array(),
+          'templateValues' => [],
           'active' => TRUE,
           'cache' => BLOCK_NO_CACHE,
           'visibility' => 1,
@@ -215,7 +215,7 @@ class CRM_Core_Block {
    */
   public static function getInfo() {
 
-    $block = array();
+    $block = [];
     foreach (self::properties() as $id => $value) {
       if ($value['active']) {
         if (in_array($id, array(
@@ -328,7 +328,7 @@ class CRM_Core_Block {
   private static function setTemplateShortcutValues() {
     $config = CRM_Core_Config::singleton();
 
-    static $shortCuts = array();
+    static $shortCuts = [];
 
     if (!($shortCuts)) {
       if (CRM_Core_Permission::check('add contacts')) {
@@ -397,7 +397,7 @@ class CRM_Core_Block {
       }
     }
 
-    $values = array();
+    $values = [];
     foreach ($shortCuts as $key => $short) {
       $values[$key] = self::setShortCutValues($short);
     }
@@ -431,7 +431,7 @@ class CRM_Core_Block {
    * @return array
    */
   private static function setShortcutValues($short) {
-    $value = array();
+    $value = [];
     if (isset($short['url'])) {
       $value['url'] = $short['url'];
     }
@@ -452,7 +452,7 @@ class CRM_Core_Block {
    * Create the list of dashboard links.
    */
   private static function setTemplateDashboardValues() {
-    static $dashboardLinks = array();
+    static $dashboardLinks = [];
     if (CRM_Core_Permission::check('access Contact Dashboard')) {
       $dashboardLinks = array(
         array(
@@ -467,9 +467,9 @@ class CRM_Core_Block {
       return NULL;
     }
 
-    $values = array();
+    $values = [];
     foreach ($dashboardLinks as $dash) {
-      $value = array();
+      $value = [];
       if (isset($dash['url'])) {
         $value['url'] = $dash['url'];
       }
@@ -504,9 +504,9 @@ class CRM_Core_Block {
       );
     }
 
-    $values = array();
+    $values = [];
     foreach ($shortCuts as $short) {
-      $value = array();
+      $value = [];
       $value['url'] = CRM_Utils_System::url($short['path'], $short['query']);
       $value['title'] = $short['title'];
       $values[] = $value;
@@ -601,7 +601,7 @@ class CRM_Core_Block {
       return NULL;
     }
 
-    $block = array();
+    $block = [];
     $block['name'] = 'block-civicrm';
     $block['id'] = $block['name'] . '_' . $id;
     $block['subject'] = self::fetch($id, 'Subject.tpl',
diff --git a/civicrm/CRM/Core/CommunityMessages.php b/civicrm/CRM/Core/CommunityMessages.php
index ff76489da41ce7bebb105e8c6f4d0ba887270026..081457351cd1a218a839a847965f4fe6c0e8cc01 100644
--- a/civicrm/CRM/Core/CommunityMessages.php
+++ b/civicrm/CRM/Core/CommunityMessages.php
@@ -95,15 +95,16 @@ class CRM_Core_CommunityMessages {
       $isChanged = TRUE;
     }
 
-    if ($document['expires'] <= CRM_Utils_Time::getTimeRaw()) {
+    $refTime = CRM_Utils_Time::getTimeRaw();
+    if ($document['expires'] <= $refTime) {
       $newDocument = $this->fetchDocument();
       if ($newDocument && $this->validateDocument($newDocument)) {
         $document = $newDocument;
-        $document['expires'] = CRM_Utils_Time::getTimeRaw() + $document['ttl'];
+        $document['expires'] = $refTime + $document['ttl'];
       }
       else {
         // keep the old messages for now, try again later
-        $document['expires'] = CRM_Utils_Time::getTimeRaw() + $document['retry'];
+        $document['expires'] = $refTime + $document['retry'];
       }
       $isChanged = TRUE;
     }
diff --git a/civicrm/CRM/Core/Component.php b/civicrm/CRM/Core/Component.php
index b96694a43d5a38c4f6cc74d24d9a085b99163b45..c594c3b6d8ab8edc725eaccd1e2da35193ccdf81 100644
--- a/civicrm/CRM/Core/Component.php
+++ b/civicrm/CRM/Core/Component.php
@@ -70,7 +70,7 @@ class CRM_Core_Component {
    * @param bool $force
    *
    * @return CRM_Core_Component_Info[]
-   * @throws Exception
+   * @throws CRM_Core_Exception
    */
   public static function &getComponents($force = FALSE) {
     if (!isset(Civi::$statics[__CLASS__]['all']) || $force) {
@@ -87,7 +87,7 @@ class CRM_Core_Component {
         require_once $infoClassFile;
         $infoObject = new $infoClass($cr->name, $cr->namespace, $cr->id);
         if ($infoObject->info['name'] !== $cr->name) {
-          CRM_Core_Error::fatal("There is a discrepancy between name in component registry and in info file ({$cr->name}).");
+          throw new CRM_Core_Exception("There is a discrepancy between name in component registry and in info file ({$cr->name}).");
         }
         Civi::$statics[__CLASS__]['all'][$cr->name] = $infoObject;
         unset($infoObject);
diff --git a/civicrm/CRM/Core/Controller.php b/civicrm/CRM/Core/Controller.php
index 26a4eff05402605b19aa6ba94480fffc08dd0a59..0550fb39f372e3d9a2c14033cb1d030f16e37e1b 100644
--- a/civicrm/CRM/Core/Controller.php
+++ b/civicrm/CRM/Core/Controller.php
@@ -556,8 +556,8 @@ class CRM_Core_Controller extends HTML_QuickForm_Controller {
   public function addWizardStyle(&$wizard) {
     $wizard['style'] = [
       'barClass' => '',
-      'stepPrefixCurrent' => '&raquo;',
-      'stepPrefixPast' => '&#x2714;',
+      'stepPrefixCurrent' => '<i class="crm-i fa-chevron-right" aria-hidden="true"></i>&nbsp;',
+      'stepPrefixPast' => '<i class="crm-i fa-check" aria-hidden="true"></i>&nbsp;',
       'stepPrefixFuture' => ' ',
       'subStepPrefixCurrent' => '&nbsp;&nbsp;',
       'subStepPrefixPast' => '&nbsp;&nbsp;',
diff --git a/civicrm/CRM/Core/DAO.php b/civicrm/CRM/Core/DAO.php
index 2b670ae91e729902507df4b2666215d7c0f2b95a..1e245457ec8e92da922153f68c6dcae120907ef6 100644
--- a/civicrm/CRM/Core/DAO.php
+++ b/civicrm/CRM/Core/DAO.php
@@ -1481,12 +1481,6 @@ FROM   civicrm_domain
       return $result;
     }
 
-    if ($freeDAO ||
-      preg_match('/^(insert|update|delete|create|drop|replace)/i', $queryStr)
-    ) {
-      // we typically do this for insert/update/delete statements OR if explicitly asked to
-      // free the dao
-    }
     return $dao;
   }
 
@@ -1736,7 +1730,7 @@ FROM   civicrm_domain
       if (!$blockCopyofCustomValues) {
         $newObject->copyCustomFields($object->id, $newObject->id);
       }
-      CRM_Utils_Hook::post('create', CRM_Core_DAO_AllCoreTables::getBriefName(str_replace('_BAO_', '_DAO_', $daoName)), $newObject->id, $newObject);
+      CRM_Utils_Hook::post('create', CRM_Core_DAO_AllCoreTables::getBriefName($daoName), $newObject->id, $newObject);
     }
 
     return $newObject;
@@ -2459,7 +2453,7 @@ SELECT contact_id
       $coreReferences = CRM_Core_DAO::getReferencesToTable($tableName);
       foreach ($coreReferences as $coreReference) {
         if ($coreReference instanceof \CRM_Core_Reference_Dynamic) {
-          \Civi::$statics[__CLASS__]['contact_references_dynamic'][$tableName][$coreReference->getReferenceTable()][] = $coreReference->getReferenceKey();
+          \Civi::$statics[__CLASS__]['contact_references_dynamic'][$tableName][$coreReference->getReferenceTable()][] = [$coreReference->getReferenceKey(), $coreReference->getTypeColumn()];
         }
       }
     }
@@ -2857,11 +2851,11 @@ SELECT contact_id
    * Generates acl clauses suitable for adding to WHERE or ON when doing an api.get for this entity
    *
    * Return format is in the form of fieldname => clauses starting with an operator. e.g.:
-   * @code
+   * ```
    *   array(
    *     'location_type_id' => array('IS NOT NULL', 'IN (1,2,3)')
    *   )
-   * @endcode
+   * ```
    *
    * Note that all array keys must be actual field names in this entity. Use subqueries to filter on other tables e.g. custom values.
    *
diff --git a/civicrm/CRM/Core/DAO/AllCoreTables.php b/civicrm/CRM/Core/DAO/AllCoreTables.php
index 0fee62521ce1d713bb8dbc8b3ab5feac8fa8029d..9e292fdb5a4f6e9b372d183253527344c46c7a9d 100644
--- a/civicrm/CRM/Core/DAO/AllCoreTables.php
+++ b/civicrm/CRM/Core/DAO/AllCoreTables.php
@@ -186,14 +186,86 @@ class CRM_Core_DAO_AllCoreTables {
   }
 
   /**
-   * Get the DAO for the class.
+   * Get the DAO for a BAO class.
    *
-   * @param string $className
+   * @param string $baoName
+   *
+   * @return string|CRM_Core_DAO
+   */
+  public static function getCanonicalClassName($baoName) {
+    return str_replace('_BAO_', '_DAO_', $baoName);
+  }
+
+  /**
+   * Get the BAO for a DAO class.
+   *
+   * @param string $daoName
    *
+   * @return string|CRM_Core_DAO
+   */
+  public static function getBAOClassName($daoName) {
+    $baoName = str_replace('_DAO_', '_BAO_', $daoName);
+    return class_exists($baoName) ? $baoName : $daoName;
+  }
+
+  /**
+   * Convert possibly underscore separated words to camel case with special handling for 'UF'
+   * e.g membership_payment returns MembershipPayment
+   *
+   * @param string $name
+   * @param bool $legacyV3
    * @return string
    */
-  public static function getCanonicalClassName($className) {
-    return str_replace('_BAO_', '_DAO_', $className);
+  public static function convertEntityNameToCamel(string $name, $legacyV3 = FALSE): string {
+    // This map only applies to APIv3
+    $map = [
+      'acl' => 'Acl',
+      'ACL' => 'Acl',
+      'im' => 'Im',
+      'IM' => 'Im',
+    ];
+    if ($legacyV3 && isset($map[$name])) {
+      return $map[$name];
+    }
+
+    $fragments = explode('_', $name);
+    foreach ($fragments as & $fragment) {
+      $fragment = ucfirst($fragment);
+      // Special case: UFGroup, UFJoin, UFMatch, UFField (if passed in without underscores)
+      if (strpos($fragment, 'Uf') === 0 && strlen($name) > 2) {
+        $fragment = 'UF' . ucfirst(substr($fragment, 2));
+      }
+    }
+    // Special case: UFGroup, UFJoin, UFMatch, UFField (if passed in underscore-separated)
+    if ($fragments[0] === 'Uf') {
+      $fragments[0] = 'UF';
+    }
+    return implode('', $fragments);
+  }
+
+  /**
+   * Convert CamelCase to snake_case, with special handling for some entity names.
+   *
+   * Eg. Activity returns activity
+   *     UFGroup returns uf_group
+   *     OptionValue returns option_value
+   *
+   * @param string $name
+   *
+   * @return string
+   */
+  public static function convertEntityNameToLower(string $name): string {
+    if ($name === strtolower($name)) {
+      return $name;
+    }
+    if ($name === 'PCP' || $name === 'IM' || $name === 'ACL') {
+      return strtolower($name);
+    }
+    return strtolower(ltrim(str_replace('U_F',
+      'uf',
+      // That's CamelCase, beside an odd UFCamel that is expected as uf_camel
+      preg_replace('/(?=[A-Z])/', '_$0', $name)
+    ), '_'));
   }
 
   /**
@@ -242,7 +314,8 @@ class CRM_Core_DAO_AllCoreTables {
    *   Ex: 'Contact'.
    */
   public static function getBriefName($className) {
-    return CRM_Utils_Array::value($className, array_flip(self::daoToClass()));
+    $className = self::getCanonicalClassName($className);
+    return array_search($className, self::daoToClass(), TRUE) ?: NULL;
   }
 
   /**
diff --git a/civicrm/CRM/Core/DAO/CustomField.php b/civicrm/CRM/Core/DAO/CustomField.php
index 7b8ded42abbfa91cb479cb2d422d345a29f5f985..5c5e9a1036ed3234c76b5fdda16b04d637d937e7 100644
--- a/civicrm/CRM/Core/DAO/CustomField.php
+++ b/civicrm/CRM/Core/DAO/CustomField.php
@@ -6,7 +6,7 @@
  *
  * Generated from xml/schema/CRM/Core/CustomField.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:492b1be45dc41c15b35371410a074393)
+ * (GenCodeChecksum:0b21a2a1f1cba7a76fd8830db1626513)
  */
 
 /**
@@ -224,6 +224,13 @@ class CRM_Core_DAO_CustomField extends CRM_Core_DAO {
    */
   public $option_group_id;
 
+  /**
+   * Serialization method - a non-null value indicates a multi-valued field.
+   *
+   * @var int
+   */
+  public $serialize;
+
   /**
    * Stores Contact Get API params contact reference custom fields. May be used for other filters in the future.
    *
@@ -640,6 +647,20 @@ class CRM_Core_DAO_CustomField extends CRM_Core_DAO {
             'labelColumn' => 'title',
           ],
         ],
+        'serialize' => [
+          'name' => 'serialize',
+          'type' => CRM_Utils_Type::T_INT,
+          'title' => ts('Serialize'),
+          'description' => ts('Serialization method - a non-null value indicates a multi-valued field.'),
+          'where' => 'civicrm_custom_field.serialize',
+          'table_name' => 'civicrm_custom_field',
+          'entity' => 'CustomField',
+          'bao' => 'CRM_Core_BAO_CustomField',
+          'localizable' => 0,
+          'pseudoconstant' => [
+            'callback' => 'CRM_Core_SelectValues::fieldSerialization',
+          ],
+        ],
         'filter' => [
           'name' => 'filter',
           'type' => CRM_Utils_Type::T_STRING,
diff --git a/civicrm/CRM/Core/DAO/Factory.php b/civicrm/CRM/Core/DAO/Factory.php
index 66dd5a9d7495c7fc040beaf803c053c399160e91..1401d2ba1da9cce62416b9df818cf69aa02a7f9f 100644
--- a/civicrm/CRM/Core/DAO/Factory.php
+++ b/civicrm/CRM/Core/DAO/Factory.php
@@ -24,12 +24,12 @@ class CRM_Core_DAO_Factory {
    * @param string $className
    *
    * @return mixed
-   * @throws Exception
+   * @throws CRM_Core_Exception
    */
   public static function create($className) {
     $type = self::$_classes[$className] ?? NULL;
     if (!$type) {
-      CRM_Core_Error::fatal("class $className not found");
+      throw new CRM_Core_Exception("class $className not found");
     }
 
     $class = self::$_prefix[$type] . $className;
diff --git a/civicrm/CRM/Core/DAO/OptionValue.php b/civicrm/CRM/Core/DAO/OptionValue.php
index 279f18540ea572fc26463fd8b27dfc3b67801183..c2418be597769541da34b196f800477ac193c243 100644
--- a/civicrm/CRM/Core/DAO/OptionValue.php
+++ b/civicrm/CRM/Core/DAO/OptionValue.php
@@ -6,7 +6,7 @@
  *
  * Generated from xml/schema/CRM/Core/OptionValue.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:7aa11987c26800632c3798b4cfce95c1)
+ * (GenCodeChecksum:5997192c77d86867e8d13c353dd02351)
  */
 
 /**
@@ -281,7 +281,7 @@ class CRM_Core_DAO_OptionValue extends CRM_Core_DAO {
           'title' => ts('Filter'),
           'description' => ts('Bitwise logic can be used to create subsets of options within an option_group for different uses.'),
           'where' => 'civicrm_option_value.filter',
-          'default' => 'NULL',
+          'default' => '0',
           'table_name' => 'civicrm_option_value',
           'entity' => 'OptionValue',
           'bao' => 'CRM_Core_BAO_OptionValue',
diff --git a/civicrm/CRM/Core/Error.php b/civicrm/CRM/Core/Error.php
index 206570e51b93bce6f8201de15dee7fef28432a32..573cd4739ca8c013eddc07696b3b35a671c03057 100644
--- a/civicrm/CRM/Core/Error.php
+++ b/civicrm/CRM/Core/Error.php
@@ -184,6 +184,19 @@ class CRM_Core_Error extends PEAR_ErrorStack {
       }
     }
 
+    // Use the custom fatalErrorHandler if defined
+    if ($config->fatalErrorHandler && function_exists($config->fatalErrorHandler)) {
+      $name = $config->fatalErrorHandler;
+      $vars = [
+        'pearError' => $pearError,
+      ];
+      $ret = $name($vars);
+      if ($ret) {
+        // the call has been successfully handled so we just exit
+        self::abend(CRM_Core_Error::FATAL_ERROR);
+      }
+    }
+
     $template->assign_by_ref('error', $error);
     $errorDetails = CRM_Core_Error::debug('', $error, FALSE);
     $template->assign_by_ref('errorDetails', $errorDetails);
@@ -204,7 +217,7 @@ class CRM_Core_Error extends PEAR_ErrorStack {
       exit;
     }
     $runOnce = TRUE;
-    self::abend(1);
+    self::abend(CRM_Core_Error::FATAL_ERROR);
   }
 
   /**
@@ -559,6 +572,18 @@ class CRM_Core_Error extends PEAR_ErrorStack {
     }
     $file_log->close();
 
+    // Use the custom fatalErrorHandler if defined
+    if (in_array($priority, [PEAR_LOG_EMERG, PEAR_LOG_ALERT, PEAR_LOG_CRIT, PEAR_LOG_ERR])) {
+      if ($config->fatalErrorHandler && function_exists($config->fatalErrorHandler)) {
+        $name = $config->fatalErrorHandler;
+        $vars = [
+          'debugLogMessage' => $message,
+          'priority' => $priority,
+        ];
+        $name($vars);
+      }
+    }
+
     if (!isset(\Civi::$statics[__CLASS__]['userFrameworkLogging'])) {
       // Set it to FALSE first & then try to set it. This is to prevent a loop as calling
       // $config->userFrameworkLogging can trigger DB queries & under log mode this
@@ -651,22 +676,30 @@ class CRM_Core_Error extends PEAR_ErrorStack {
 
       $prefixString = $prefix ? ($prefix . '.') : '';
 
-      $hash = self::generateLogFileHash($config);
-      $fileName = $config->configAndLogDir . 'CiviCRM.' . $prefixString . $hash . '.log';
+      if (CRM_Utils_Constant::value('CIVICRM_LOG_HASH', TRUE)) {
+        $hash = self::generateLogFileHash($config) . '.';
+      }
+      else {
+        $hash = '';
+      }
+      $fileName = $config->configAndLogDir . 'CiviCRM.' . $prefixString . $hash . 'log';
 
-      // Roll log file monthly or if greater than 256M.
+      // Roll log file monthly or if greater than our threshold.
       // Size-based rotation introduced in response to filesize limits on
       // certain OS/PHP combos.
-      if (file_exists($fileName)) {
-        $fileTime = date("Ym", filemtime($fileName));
-        $fileSize = filesize($fileName);
-        if (($fileTime < date('Ym')) ||
-          ($fileSize > 256 * 1024 * 1024) ||
-          ($fileSize < 0)
-        ) {
-          rename($fileName,
-            $fileName . '.' . date('YmdHi')
-          );
+      $maxBytes = CRM_Utils_Constant::value('CIVICRM_LOG_ROTATESIZE', 256 * 1024 * 1024);
+      if ($maxBytes) {
+        if (file_exists($fileName)) {
+          $fileTime = date("Ym", filemtime($fileName));
+          $fileSize = filesize($fileName);
+          if (($fileTime < date('Ym')) ||
+            ($fileSize > $maxBytes) ||
+            ($fileSize < 0)
+          ) {
+            rename($fileName,
+              $fileName . '.' . date('YmdHi')
+            );
+          }
         }
       }
       \Civi::$statics[__CLASS__]['logger_file' . $prefix] = $fileName;
diff --git a/civicrm/CRM/Core/Form.php b/civicrm/CRM/Core/Form.php
index 416085c733a52fb2fb08a4557d3b9eaeb7505575..a3c4023c0edb89e82936b4486cf82de9151d5042 100644
--- a/civicrm/CRM/Core/Form.php
+++ b/civicrm/CRM/Core/Form.php
@@ -421,7 +421,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
 
     $element = $this->addElement($type, $name, CRM_Utils_String::purifyHTML($label), $attributes, $extra);
     if (HTML_QuickForm::isError($element)) {
-      CRM_Core_Error::fatal(HTML_QuickForm::errorMessage($element));
+      CRM_Core_Error::statusBounce(HTML_QuickForm::errorMessage($element));
     }
 
     if ($inputType == 'color') {
@@ -436,7 +436,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
         $error = $this->addRule($name, ts('%1 is a required field.', [1 => $label]), 'required');
       }
       if (HTML_QuickForm::isError($error)) {
-        CRM_Core_Error::fatal(HTML_QuickForm::errorMessage($element));
+        CRM_Core_Error::statusBounce(HTML_QuickForm::errorMessage($element));
       }
     }
 
@@ -2015,7 +2015,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   public function addEntityRef($name, $label = '', $props = [], $required = FALSE) {
     // Default properties
     $props['api'] = CRM_Utils_Array::value('api', $props, []);
-    $props['entity'] = CRM_Utils_String::convertStringToCamel(CRM_Utils_Array::value('entity', $props, 'Contact'));
+    $props['entity'] = CRM_Core_DAO_AllCoreTables::convertEntityNameToCamel(CRM_Utils_Array::value('entity', $props, 'Contact'));
     $props['class'] = ltrim(CRM_Utils_Array::value('class', $props, '') . ' crm-form-entityref');
 
     if (array_key_exists('create', $props) && empty($props['create'])) {
diff --git a/civicrm/CRM/Core/Form/Renderer.php b/civicrm/CRM/Core/Form/Renderer.php
index 8450a9baa23f1704a3ed655fdee11b4d64514f96..8b224a98c20ec29f6ea803566941d86a2b5c7154 100644
--- a/civicrm/CRM/Core/Form/Renderer.php
+++ b/civicrm/CRM/Core/Form/Renderer.php
@@ -370,7 +370,10 @@ class CRM_Core_Form_Renderer extends HTML_QuickForm_Renderer_ArraySmarty {
       $path = $field->getAttribute('data-option-edit-path');
       // NOTE: If we ever needed to support arguments in this link other than reset=1 we could split $path here if it contains a ?
       $url = CRM_Utils_System::url($path, 'reset=1');
-      $el['html'] .= ' <a href="' . $url . '" class="crm-option-edit-link medium-popup crm-hover-button" target="_blank" title="' . ts('Edit Options') . '" data-option-edit-path="' . $path . '"><i class="crm-i fa-wrench"></i></a>';
+      $icon = CRM_Core_Page::crmIcon('fa-wrench', ts('Edit %1 Options', [1 => $field->getLabel() ?: ts('Field')]));
+      $el['html'] .= <<<HEREDOC
+ <a href="$url" class="crm-option-edit-link medium-popup crm-hover-button" target="_blank" data-option-edit-path="$path">$icon</a>
+HEREDOC;
     }
   }
 
@@ -382,7 +385,7 @@ class CRM_Core_Form_Renderer extends HTML_QuickForm_Renderer_ArraySmarty {
     // Initially hide if not needed
     // Note: visibility:hidden prevents layout jumping around unlike display:none
     $display = $field->getValue() !== NULL ? '' : ' style="visibility:hidden;"';
-    $el['html'] .= ' <a href="#" class="crm-hover-button crm-clear-link"' . $display . ' title="' . ts('Clear') . '"><i class="crm-i fa-times"></i></a>';
+    $el['html'] .= ' <a href="#" class="crm-hover-button crm-clear-link"' . $display . ' title="' . ts('Clear') . '"><i class="crm-i fa-times" aria-hidden="true"></i></a>';
   }
 
 }
diff --git a/civicrm/CRM/Core/I18n.php b/civicrm/CRM/Core/I18n.php
index 2f0e3207f3525a6cb6deb99754375e236a99e8e2..357dc2f936715ea8d00db74362c962431a52e0a3 100644
--- a/civicrm/CRM/Core/I18n.php
+++ b/civicrm/CRM/Core/I18n.php
@@ -38,18 +38,26 @@ class CRM_Core_I18n {
   public static $SQL_ESCAPER = NULL;
 
   /**
-   * Encode a string for use in SQL.
+   * Escape a string if a mode is specified, otherwise return string unmodified.
    *
    * @param string $text
+   * @param string $mode
    * @return string
    */
-  protected static function escapeSql($text) {
-    if (self::$SQL_ESCAPER == NULL) {
-      return CRM_Core_DAO::escapeString($text);
-    }
-    else {
-      return call_user_func(self::$SQL_ESCAPER, $text);
+  protected static function escape($text, $mode) {
+    switch ($mode) {
+      case 'sql':
+        if (self::$SQL_ESCAPER == NULL) {
+          return CRM_Core_DAO::escapeString($text);
+        }
+        else {
+          return call_user_func(self::$SQL_ESCAPER, $text);
+        }
+
+      case 'js':
+        return substr(json_encode($text, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT), 1, -1);
     }
+    return $text;
   }
 
   /**
@@ -312,23 +320,15 @@ class CRM_Core_I18n {
    *   the translated string
    */
   public function crm_translate($text, $params = []) {
-    if (isset($params['escape'])) {
-      $escape = $params['escape'];
-      unset($params['escape']);
-    }
+    $escape = $params['escape'] ?? NULL;
+    unset($params['escape']);
 
     // sometimes we need to {ts}-tag a string, but don’t want to
     // translate it in the template (like civicrm_navigation.tpl),
     // because we handle the translation in a different way (CRM-6998)
     // in such cases we return early, only doing SQL/JS escaping
     if (isset($params['skip']) and $params['skip']) {
-      if (isset($escape) and ($escape == 'sql')) {
-        $text = self::escapeSql($text);
-      }
-      if (isset($escape) and ($escape == 'js')) {
-        $text = addcslashes($text, "'");
-      }
-      return $text;
+      return self::escape($text, $escape);
     }
 
     $plural = $count = NULL;
@@ -385,17 +385,7 @@ class CRM_Core_I18n {
       $text = $this->strarg($text, $params);
     }
 
-    // escape SQL if we were asked for it
-    if (isset($escape) and ($escape == 'sql')) {
-      $text = self::escapeSql($text);
-    }
-
-    // escape for JavaScript (if requested)
-    if (isset($escape) and ($escape == 'js')) {
-      $text = addcslashes($text, "'");
-    }
-
-    return $text;
+    return self::escape($text, $escape);
   }
 
   /**
diff --git a/civicrm/CRM/Core/I18n/Form.php b/civicrm/CRM/Core/I18n/Form.php
index 2c36e246397c53381cfb3da7e3cda940b2b6d534..6beb1d669dafc6cf9c783d1e31944a3558206060 100644
--- a/civicrm/CRM/Core/I18n/Form.php
+++ b/civicrm/CRM/Core/I18n/Form.php
@@ -27,7 +27,7 @@ class CRM_Core_I18n_Form extends CRM_Core_Form {
     $id = CRM_Utils_Request::retrieve('id', 'Int', $this);
     $this->_structure = CRM_Core_I18n_SchemaStructure::columns();
     if (!isset($this->_structure[$table][$field])) {
-      CRM_Core_Error::fatal("$table.$field is not internationalized.");
+      CRM_Core_Error::statusBounce("$table.$field is not internationalized.");
     }
 
     $this->addElement('hidden', 'table', $table);
@@ -106,7 +106,7 @@ class CRM_Core_I18n_Form extends CRM_Core_Form {
 
     // validate table and field
     if (!isset($this->_structure[$table][$field])) {
-      CRM_Core_Error::fatal("$table.$field is not internationalized.");
+      CRM_Core_Error::statusBounce("$table.$field is not internationalized.");
     }
 
     $cols = [];
diff --git a/civicrm/CRM/Core/I18n/SchemaStructure.php b/civicrm/CRM/Core/I18n/SchemaStructure.php
index c76ece17542459e28258d615a081d1eef63e89f6..ecadf83929c92afe8751d3ef86a9ba1f984827ae 100644
--- a/civicrm/CRM/Core/I18n/SchemaStructure.php
+++ b/civicrm/CRM/Core/I18n/SchemaStructure.php
@@ -118,7 +118,7 @@ class CRM_Core_I18n_SchemaStructure {
           'title' => "varchar(127) COMMENT 'Payment Processor Descriptive Name.'",
         ],
         'civicrm_membership_type' => [
-          'name' => "varchar(128) COMMENT 'Name of Membership Type'",
+          'name' => "varchar(128) NOT NULL COMMENT 'Name of Membership Type'",
           'description' => "varchar(255) COMMENT 'Description of Membership Type'",
         ],
         'civicrm_membership_block' => [
@@ -154,7 +154,7 @@ class CRM_Core_I18n_SchemaStructure {
           'help_post' => "text COMMENT 'Description and/or help text to display after this field.'",
         ],
         'civicrm_price_field_value' => [
-          'label' => "varchar(255) COMMENT 'Price field option label'",
+          'label' => "varchar(255) NOT NULL COMMENT 'Price field option label'",
           'description' => "text DEFAULT NULL COMMENT 'Price field option description.'",
           'help_pre' => "text DEFAULT NULL COMMENT 'Price field option pre help text.'",
           'help_post' => "text DEFAULT NULL COMMENT 'Price field option post field help.'",
@@ -486,6 +486,7 @@ class CRM_Core_I18n_SchemaStructure {
           'name' => [
             'type' => "Text",
             'label' => "Name",
+            'required' => "true",
           ],
           'description' => [
             'type' => "TextArea",
@@ -585,6 +586,7 @@ class CRM_Core_I18n_SchemaStructure {
         'civicrm_price_field_value' => [
           'label' => [
             'type' => "Text",
+            'required' => "true",
           ],
           'description' => [
             'type' => "TextArea",
diff --git a/civicrm/CRM/Core/Invoke.php b/civicrm/CRM/Core/Invoke.php
index b0262dfda7f7bc9a0f86344dcd769e6f506a8689..780ae182fa8933155968e5feb193f2e11e5cc8af 100644
--- a/civicrm/CRM/Core/Invoke.php
+++ b/civicrm/CRM/Core/Invoke.php
@@ -102,7 +102,7 @@ class CRM_Core_Invoke {
         return CRM_Utils_System::redirect();
       }
       else {
-        CRM_Core_Error::fatal('You do not have permission to execute this url');
+        CRM_Core_Error::statusBounce('You do not have permission to execute this url');
       }
     }
   }
@@ -224,7 +224,7 @@ class CRM_Core_Invoke {
 
       if (!array_key_exists('page_callback', $item)) {
         CRM_Core_Error::debug('Bad item', $item);
-        CRM_Core_Error::fatal(ts('Bad menu record in database'));
+        CRM_Core_Error::statusBounce(ts('Bad menu record in database'));
       }
 
       // check that we are permissioned to access this page
@@ -307,7 +307,7 @@ class CRM_Core_Invoke {
           $object = new $item['page_callback']($title, TRUE, $mode, NULL, $addSequence);
         }
         else {
-          CRM_Core_Error::fatal();
+          throw new CRM_Core_Exception('Execute supplied menu action');
         }
         $result = $object->run($newArgs, $pageArgs);
       }
diff --git a/civicrm/CRM/Core/Menu.php b/civicrm/CRM/Core/Menu.php
index 5ec868771e888e9ab01e9676d45a411a37b01450..390d6a2a7c1b6e113c33836d49f3ca31d8917745 100644
--- a/civicrm/CRM/Core/Menu.php
+++ b/civicrm/CRM/Core/Menu.php
@@ -74,7 +74,7 @@ class CRM_Core_Menu {
       // lets call a hook and get any additional files if needed
       CRM_Utils_Hook::xmlMenu($files);
 
-      self::$_items = array();
+      self::$_items = [];
       foreach ($files as $file) {
         self::read($file, self::$_items);
       }
@@ -105,22 +105,24 @@ class CRM_Core_Menu {
    *   An XML document defining a list of menu items.
    * @param array $menu
    *   An alterable list of menu items.
+   *
+   * @throws CRM_Core_Exception
    */
   public static function readXML($xml, &$menu) {
     $config = CRM_Core_Config::singleton();
     foreach ($xml->item as $item) {
       if (!(string ) $item->path) {
         CRM_Core_Error::debug('i', $item);
-        CRM_Core_Error::fatal();
+        throw new CRM_Core_Exception('Unable to read XML file');
       }
       $path = (string ) $item->path;
-      $menu[$path] = array();
+      $menu[$path] = [];
       unset($item->path);
 
       if ($item->ids_arguments) {
-        $ids = array();
+        $ids = [];
         foreach (array('json' => 'json', 'html' => 'html', 'exception' => 'exceptions') as $tag => $attr) {
-          $ids[$attr] = array();
+          $ids[$attr] = [];
           foreach ($item->ids_arguments->{$tag} as $value) {
             $ids[$attr][] = (string) $value;
           }
@@ -152,7 +154,7 @@ class CRM_Core_Menu {
               $elements = explode(';', $value);
               $op = 'or';
             }
-            $items = array();
+            $items = [];
             foreach ($elements as $element) {
               $items[] = $element;
             }
@@ -204,7 +206,7 @@ class CRM_Core_Menu {
    * @param array $menu
    * @param string $path
    *
-   * @throws Exception
+   * @throws CRM_Core_Exception
    */
   public static function fillMenuValues(&$menu, $path) {
     $fieldsToPropagate = array(
@@ -214,7 +216,7 @@ class CRM_Core_Menu {
       'page_arguments',
       'is_ssl',
     );
-    $fieldsPresent = array();
+    $fieldsPresent = [];
     foreach ($fieldsToPropagate as $field) {
       $fieldsPresent[$field] = isset($menu[$path][$field]);
     }
@@ -240,15 +242,15 @@ class CRM_Core_Menu {
       return;
     }
 
-    $messages = array();
+    $messages = [];
     foreach ($fieldsToPropagate as $field) {
       if (!$fieldsPresent[$field]) {
         $messages[] = ts("Could not find %1 in path tree",
-          array(1 => $field)
+          [1 => $field]
         );
       }
     }
-    CRM_Core_Error::fatal("'$path': " . implode(', ', $messages));
+    throw new CRM_Core_Exception("'$path': " . implode(', ', $messages));
   }
 
   /**
@@ -305,7 +307,7 @@ class CRM_Core_Menu {
         CRM_Core_BAO_SchemaHandler::checkIfFieldExists('civicrm_menu', 'module_data', FALSE)
       ) {
         // Move unrecognized fields to $module_data.
-        $module_data = array();
+        $module_data = [];
         foreach (array_keys($item) as $key) {
           if (!isset($daoFields[$key])) {
             $module_data[$key] = $item[$key];
@@ -339,7 +341,7 @@ class CRM_Core_Menu {
    * @param array $menu
    */
   public static function buildAdminLinks(&$menu) {
-    $values = array();
+    $values = [];
 
     foreach ($menu as $path => $item) {
       if (empty($item['adminGroup'])) {
@@ -370,15 +372,14 @@ class CRM_Core_Menu {
         'extra' => $item['extra'] ?? NULL,
       );
       if (!array_key_exists($item['adminGroup'], $values)) {
-        $values[$item['adminGroup']] = array();
-        $values[$item['adminGroup']]['fields'] = array();
+        $values[$item['adminGroup']] = [];
+        $values[$item['adminGroup']]['fields'] = [];
       }
       $values[$item['adminGroup']]['fields']["{weight}.{$item['title']}"] = $value;
       $values[$item['adminGroup']]['component_id'] = $item['component_id'];
     }
 
     foreach ($values as $group => $dontCare) {
-      $values[$group]['perColumn'] = round(count($values[$group]['fields']) / 2);
       ksort($values[$group]);
     }
 
@@ -415,7 +416,7 @@ class CRM_Core_Menu {
    *   The breadcrumb for this path
    */
   public static function buildBreadcrumb(&$menu, $path) {
-    $crumbs = array();
+    $crumbs = [];
 
     $pathElements = explode('/', $path);
     array_pop($pathElements);
@@ -505,7 +506,7 @@ class CRM_Core_Menu {
    * @throws \CRM_Core_Exception
    */
   public static function fillComponentIds(&$menu, $path) {
-    static $cache = array();
+    static $cache = [];
 
     if (array_key_exists('component_id', $menu[$path])) {
       return;
@@ -550,7 +551,7 @@ class CRM_Core_Menu {
 
     $args = explode('/', $path);
 
-    $elements = array();
+    $elements = [];
     while (!empty($args)) {
       $string = implode('/', $args);
       $string = CRM_Core_DAO::escapeString($string);
@@ -586,10 +587,10 @@ UNION (
     $menu = new CRM_Core_DAO_Menu();
     $menu->query($query);
 
-    self::$_menuCache = array();
+    self::$_menuCache = [];
     $menuPath = NULL;
     while ($menu->fetch()) {
-      self::$_menuCache[$menu->path] = array();
+      self::$_menuCache[$menu->path] = [];
       CRM_Core_DAO::storeValues($menu, self::$_menuCache[$menu->path]);
 
       // Move module_data into main item.
@@ -646,7 +647,7 @@ UNION (
     if (!is_string($pathArgs)) {
       return;
     }
-    $args = array();
+    $args = [];
 
     $elements = explode(',', $pathArgs);
     foreach ($elements as $keyVal) {
@@ -655,7 +656,7 @@ UNION (
     }
 
     if (array_key_exists('urlToSession', $arr)) {
-      $urlToSession = array();
+      $urlToSession = [];
 
       $params = explode(';', $arr['urlToSession']);
       $count = 0;
diff --git a/civicrm/CRM/Core/OptionGroup.php b/civicrm/CRM/Core/OptionGroup.php
index 2540c1547138416768af14bcc47309ea0dbf6e2c..89018c1a21cf987980be1c6d255c55fc97aa13e9 100644
--- a/civicrm/CRM/Core/OptionGroup.php
+++ b/civicrm/CRM/Core/OptionGroup.php
@@ -439,8 +439,6 @@ WHERE  v.option_group_id = g.id
 
   /**
    * Creates a new option group with the passed in values.
-   * @TODO: Should update the group if it already exists intelligently, so multi-lingual is
-   * not messed up. Currently deletes the old group
    *
    * @param string $groupName
    *   The name of the option group - make sure there is no conflict.
@@ -464,10 +462,10 @@ WHERE  v.option_group_id = g.id
    *   the option group ID
    */
   public static function createAssoc($groupName, &$values, &$defaultID, $groupTitle = NULL) {
-    self::deleteAssoc($groupName);
     if (!empty($values)) {
       $group = new CRM_Core_DAO_OptionGroup();
       $group->name = $groupName;
+      $group->find(TRUE);
       $group->title = empty($groupTitle) ? $groupName : $groupTitle;
       $group->is_reserved = 1;
       $group->is_active = 1;
@@ -476,8 +474,9 @@ WHERE  v.option_group_id = g.id
       foreach ($values as $v) {
         $value = new CRM_Core_DAO_OptionValue();
         $value->option_group_id = $group->id;
-        $value->label = $v['label'];
         $value->value = $v['value'];
+        $value->find(TRUE);
+        $value->label = $v['label'];
         $value->name = $v['name'] ?? NULL;
         $value->description = $v['description'] ?? NULL;
         $value->weight = $v['weight'] ?? NULL;
@@ -502,8 +501,11 @@ WHERE  v.option_group_id = g.id
    * @param $values
    * @param bool $flip
    * @param string $field
+   *
+   * @deprecated
    */
   public static function getAssoc($groupName, &$values, $flip = FALSE, $field = 'name') {
+    CRM_Core_Error::deprecatedFunctionWarning('unused function');
     $query = "
 SELECT v.id as amount_id, v.value, v.label, v.name, v.description, v.weight
   FROM civicrm_option_group g,
@@ -546,8 +548,11 @@ ORDER BY v.weight
   /**
    * @param string $groupName
    * @param string $operator
+   *
+   * @deprecated
    */
   public static function deleteAssoc($groupName, $operator = "=") {
+    CRM_Core_Error::deprecatedFunctionWarning('unused function');
     $query = "
 DELETE g, v
   FROM civicrm_option_group g,
diff --git a/civicrm/CRM/Core/Page.php b/civicrm/CRM/Core/Page.php
index 251c642d7530cc706eb34c69af356fc92cdc4e3e..f83b170f931f309f924b69f530665899213e4355 100644
--- a/civicrm/CRM/Core/Page.php
+++ b/civicrm/CRM/Core/Page.php
@@ -418,4 +418,58 @@ class CRM_Core_Page {
     $this->assign('fields', $dateFields);
   }
 
+  /**
+   * Handy helper to produce the standard markup for an icon with alternative
+   * text for a title and screen readers.
+   *
+   * See also the smarty block function `icon`
+   *
+   * @param string $icon
+   *   The class name of the icon to display.
+   * @param string $text
+   *   The translated text to display.
+   * @param bool $condition
+   *   Whether to display anything at all. This helps simplify code when a
+   *   checkmark should appear if something is true.
+   * @param array $attribs
+   *   Attributes to set or override on the icon element.  Any standard
+   *   attribute can be unset by setting the value to an empty string.
+   *
+   * @return string
+   *   The whole bit to drop in.
+   */
+  public static function crmIcon($icon, $text = NULL, $condition = TRUE, $attribs = []) {
+    if (!$condition) {
+      return '';
+    }
+
+    // Add icon classes to any that might exist in $attribs
+    $classes = array_key_exists('class', $attribs) ? explode(' ', $attribs['class']) : [];
+    $classes[] = 'crm-i';
+    $classes[] = $icon;
+    $attribs['class'] = implode(' ', array_unique($classes));
+
+    $standardAttribs = ['aria-hidden' => 'true'];
+    if ($text === NULL || $text === '') {
+      $title = $sr = '';
+    }
+    else {
+      $standardAttribs['title'] = $text;
+      $sr = "<span class=\"sr-only\">$text</span>";
+    }
+
+    // Assemble attribs
+    $attribString = '';
+    // Strip out title if $attribs specifies a blank title
+    $attribs = array_merge($standardAttribs, $attribs);
+    foreach ($attribs as $attrib => $val) {
+      if (strlen($val)) {
+        $val = htmlspecialchars($val);
+        $attribString .= " $attrib=\"$val\"";
+      }
+    }
+
+    return "<i$attribString></i>$sr";
+  }
+
 }
diff --git a/civicrm/CRM/Core/Page/Redirect.php b/civicrm/CRM/Core/Page/Redirect.php
index 90be62a20bc28528352935618d5850b7d299e0e2..15141ae4f021258de5af189e17af8310f6fc662c 100644
--- a/civicrm/CRM/Core/Page/Redirect.php
+++ b/civicrm/CRM/Core/Page/Redirect.php
@@ -3,13 +3,13 @@
 /**
  * Placeholder page which generates a redirect
  *
- * @code
+ * ```
  * <item>
  *   <path>civicrm/admin/options/case_type</path>
  *   <page_callback>CRM_Core_Page_Redirect</page_callback>
  *   <page_arguments>url=civicrm/foo/bar?whiz=bang&amp;passthru=%%passthru%%</page_arguments>
  * </item>
- * @endcoe
+ * ```
  */
 class CRM_Core_Page_Redirect extends CRM_Core_Page {
 
diff --git a/civicrm/CRM/Core/Payment.php b/civicrm/CRM/Core/Payment.php
index 288359d4e5e5e710820932c2c676b8bbe3eae040..c4e4deb2c583126aa1ee2ce6abef3f4d142de747 100644
--- a/civicrm/CRM/Core/Payment.php
+++ b/civicrm/CRM/Core/Payment.php
@@ -94,7 +94,7 @@ abstract class CRM_Core_Payment {
   /**
    * Processor type label.
    *
-   * (Deprecated parameter but used in some messages).
+   * (Deprecated unused parameter).
    *
    * @var string
    * @deprecated
@@ -399,6 +399,19 @@ abstract class CRM_Core_Payment {
     return method_exists(CRM_Utils_System::getClassName($this), 'cancelSubscription');
   }
 
+  /**
+   * Does the processor support the user having a choice as to whether to cancel the recurring with the processor?
+   *
+   * If this returns TRUE then there will be an option to send a cancellation request in the cancellation form.
+   *
+   * This would normally be false for processors where CiviCRM maintains the schedule.
+   *
+   * @return bool
+   */
+  protected function supportsCancelRecurringNotifyOptional() {
+    return $this->supportsCancelRecurring();
+  }
+
   /**
    * Does this processor support pre-approval.
    *
@@ -615,7 +628,10 @@ abstract class CRM_Core_Payment {
         }
 
       case 'cancelRecurNotSupportedText':
-        return ts('Automatic cancellation is not supported for this payment processor. You or the contributor will need to manually cancel this recurring contribution using the payment processor website.');
+        if (!$this->supportsCancelRecurring()) {
+          return ts('Automatic cancellation is not supported for this payment processor. You or the contributor will need to manually cancel this recurring contribution using the payment processor website.');
+        }
+        return '';
 
     }
     CRM_Core_Error::deprecatedFunctionWarning('Calls to getText must use a supported method');
@@ -1375,6 +1391,11 @@ abstract class CRM_Core_Payment {
    * proceed. Note the form layer will only call this after calling
    * $processor->supports('cancelRecurring');
    *
+   * A payment processor can control whether to notify the actual payment provider or just
+   * cancel in CiviCRM by setting the `isNotifyProcessorOnCancelRecur` property on PropertyBag.
+   * If supportsCancelRecurringNotifyOptional() is TRUE this will be automatically set based on
+   * the user selection on the form. If FALSE you need to set it yourself.
+   *
    * @param \Civi\Payment\PropertyBag $propertyBag
    *
    * @return array
@@ -1382,7 +1403,8 @@ abstract class CRM_Core_Payment {
    * @throws \Civi\Payment\Exception\PaymentProcessorException
    */
   public function doCancelRecurring(PropertyBag $propertyBag) {
-    if (method_exists($this, 'cancelSubscription')) {
+    if (method_exists($this, 'cancelSubscription')
+    && ($propertyBag->has('isNotifyProcessorOnCancelRecur') && $propertyBag->getIsNotifyProcessorOnCancelRecur())) {
       $message = NULL;
       if ($this->cancelSubscription($message, $propertyBag)) {
         return ['message' => $message];
diff --git a/civicrm/CRM/Core/Payment/AuthorizeNet.php b/civicrm/CRM/Core/Payment/AuthorizeNet.php
index 52657cf22117d8a59c44579100f28bfaa5f54bcf..abf5e4aaa7ccf9f3858039f41ec10c135c48286f 100644
--- a/civicrm/CRM/Core/Payment/AuthorizeNet.php
+++ b/civicrm/CRM/Core/Payment/AuthorizeNet.php
@@ -33,12 +33,23 @@ class CRM_Core_Payment_AuthorizeNet extends CRM_Core_Payment {
   protected $_params = [];
 
   /**
-   * We only need one instance of this object. So we use the singleton
-   * pattern and cache the instance in this variable
-   *
-   * @var object
+   * @var GuzzleHttp\Client
    */
-  static private $_singleton = NULL;
+  protected $guzzleClient;
+
+  /**
+   * @return \GuzzleHttp\Client
+   */
+  public function getGuzzleClient(): \GuzzleHttp\Client {
+    return $this->guzzleClient ?? new \GuzzleHttp\Client();
+  }
+
+  /**
+   * @param \GuzzleHttp\Client $guzzleClient
+   */
+  public function setGuzzleClient(\GuzzleHttp\Client $guzzleClient) {
+    $this->guzzleClient = $guzzleClient;
+  }
 
   /**
    * Constructor.
@@ -53,7 +64,6 @@ class CRM_Core_Payment_AuthorizeNet extends CRM_Core_Payment {
   public function __construct($mode, &$paymentProcessor) {
     $this->_mode = $mode;
     $this->_paymentProcessor = $paymentProcessor;
-    $this->_processorName = ts('Authorize.net');
 
     $this->_setParam('apiLogin', $paymentProcessor['user_name']);
     $this->_setParam('paymentKey', $paymentProcessor['password']);
@@ -98,6 +108,8 @@ class CRM_Core_Payment_AuthorizeNet extends CRM_Core_Payment {
    *
    * @return array
    *   the result in a nice formatted array (or an error object)
+   *
+   * @throws \Civi\Payment\Exception\PaymentProcessorException
    */
   public function doDirectPayment(&$params) {
     if (!defined('CURLOPT_SSLCERT')) {
@@ -233,7 +245,7 @@ class CRM_Core_Payment_AuthorizeNet extends CRM_Core_Payment {
       $intervalLength *= 12;
       $intervalUnit = 'months';
     }
-    elseif ($intervalUnit == 'day') {
+    elseif ($intervalUnit === 'day') {
       $intervalUnit = 'days';
     }
     elseif ($intervalUnit == 'month') {
@@ -316,30 +328,22 @@ class CRM_Core_Payment_AuthorizeNet extends CRM_Core_Payment {
     $template->assign('billingCountry', $this->_getParam('country'));
 
     $arbXML = $template->fetch('CRM/Contribute/Form/Contribution/AuthorizeNetARB.tpl');
-    // submit to authorize.net
 
-    $submit = curl_init($this->_paymentProcessor['url_recur']);
-    if (!$submit) {
-      return self::error(9002, 'Could not initiate connection to payment gateway');
-    }
-    curl_setopt($submit, CURLOPT_RETURNTRANSFER, 1);
-    curl_setopt($submit, CURLOPT_HTTPHEADER, ["Content-Type: text/xml"]);
-    curl_setopt($submit, CURLOPT_HEADER, 1);
-    curl_setopt($submit, CURLOPT_POSTFIELDS, $arbXML);
-    curl_setopt($submit, CURLOPT_POST, 1);
-    curl_setopt($submit, CURLOPT_SSL_VERIFYPEER, Civi::settings()->get('verifySSL'));
-
-    $response = curl_exec($submit);
-
-    if (!$response) {
-      return self::error(curl_errno($submit), curl_error($submit));
-    }
-
-    curl_close($submit);
-    $responseFields = $this->_ParseArbReturn($response);
-
-    if ($responseFields['resultCode'] == 'Error') {
-      return self::error($responseFields['code'], $responseFields['text']);
+    // Submit to authorize.net
+    $response = $this->getGuzzleClient()->post($this->_paymentProcessor['url_recur'], [
+      'headers' => [
+        'Content-Type' => 'text/xml; charset=UTF8',
+      ],
+      'body' => $arbXML,
+      'curl' => [
+        CURLOPT_RETURNTRANSFER => TRUE,
+        CURLOPT_SSL_VERIFYPEER => Civi::settings()->get('verifySSL'),
+      ],
+    ]);
+    $responseFields = $this->_ParseArbReturn((string) $response->getBody());
+
+    if ($responseFields['resultCode'] === 'Error') {
+      throw new PaymentProcessorException($responseFields['code'], $responseFields['text']);
     }
 
     // update recur processor_id with subscriptionId
diff --git a/civicrm/CRM/Core/Payment/BaseIPN.php b/civicrm/CRM/Core/Payment/BaseIPN.php
index 2da96d6cc4926b0f150a3590afac2bc54af9cd9e..8f5dd3786fe98c4503f6cb4d57c79591f1b88a60 100644
--- a/civicrm/CRM/Core/Payment/BaseIPN.php
+++ b/civicrm/CRM/Core/Payment/BaseIPN.php
@@ -85,7 +85,7 @@ class CRM_Core_Payment_BaseIPN {
    *
    * @return bool
    */
-  public function validateData(&$input, &$ids, &$objects, $required = TRUE, $paymentProcessorID = NULL) {
+  public function validateData($input, &$ids, &$objects, $required = TRUE, $paymentProcessorID = NULL) {
 
     // Check if the contribution exists
     // make sure contribution exists and is valid
@@ -158,7 +158,7 @@ class CRM_Core_Payment_BaseIPN {
    *
    * @return bool|array
    */
-  public function loadObjects(&$input, &$ids, &$objects, $required, $paymentProcessorID, $error_handling = NULL) {
+  public function loadObjects($input, &$ids, &$objects, $required, $paymentProcessorID, $error_handling = NULL) {
     if (empty($error_handling)) {
       // default options are that we log an error & echo it out
       // note that we should refactor this error handling into error code @ some point
@@ -274,15 +274,18 @@ class CRM_Core_Payment_BaseIPN {
   /**
    * Handled pending contribution status.
    *
+   * @deprecated
+   *
    * @param array $objects
    * @param object $transaction
    *
    * @return bool
    */
   public function pending(&$objects, &$transaction) {
+    CRM_Core_Error::deprecatedFunctionWarning('This function will be removed at some point');
     $transaction->commit();
-    Civi::log()->debug("Returning since contribution status is Pending");
-    echo "Success: Returning since contribution status is pending<p>";
+    Civi::log()->debug('Returning since contribution status is Pending');
+    echo 'Success: Returning since contribution status is pending<p>';
     return TRUE;
   }
 
diff --git a/civicrm/CRM/Core/Payment/Dummy.php b/civicrm/CRM/Core/Payment/Dummy.php
index e0a079a8f1b8b48218c9811bdc6e9354531e7d66..1f3d9336cd62ba7b2a599be1f4712e9f2438ba18 100644
--- a/civicrm/CRM/Core/Payment/Dummy.php
+++ b/civicrm/CRM/Core/Payment/Dummy.php
@@ -11,18 +11,16 @@
  *
  * @package CRM
  * @author Marshal Newrock <marshal@idealso.com>
- * $Id: Dummy.php 45429 2013-02-06 22:11:18Z lobo $
  */
 
 use Civi\Payment\Exception\PaymentProcessorException;
+use Civi\Payment\PropertyBag;
 
 /**
  * Dummy payment processor
  */
 class CRM_Core_Payment_Dummy extends CRM_Core_Payment {
-  const CHARSET = 'iso-8859-1';
-
-  protected $_mode = NULL;
+  protected $_mode;
 
   protected $_params = [];
   protected $_doDirectPaymentResult = [];
@@ -43,28 +41,17 @@ class CRM_Core_Payment_Dummy extends CRM_Core_Payment {
     }
   }
 
-  /**
-   * We only need one instance of this object. So we use the singleton
-   * pattern and cache the instance in this variable
-   *
-   * @var object
-   */
-  static private $_singleton = NULL;
-
   /**
    * Constructor.
    *
    * @param string $mode
    *   The mode of operation: live or test.
    *
-   * @param $paymentProcessor
-   *
-   * @return \CRM_Core_Payment_Dummy
+   * @param array $paymentProcessor
    */
   public function __construct($mode, &$paymentProcessor) {
     $this->_mode = $mode;
     $this->_paymentProcessor = $paymentProcessor;
-    $this->_processorName = ts('Dummy Processor');
   }
 
   /**
@@ -78,19 +65,19 @@ class CRM_Core_Payment_Dummy extends CRM_Core_Payment {
    * @throws \Civi\Payment\Exception\PaymentProcessorException
    */
   public function doDirectPayment(&$params) {
+    $propertyBag = PropertyBag::cast($params);
     // Invoke hook_civicrm_paymentProcessor
     // In Dummy's case, there is no translation of parameters into
     // the back-end's canonical set of parameters.  But if a processor
     // does this, it needs to invoke this hook after it has done translation,
     // but before it actually starts talking to its proprietary back-end.
-    if (!empty($params['is_recur'])) {
-      $throwAnENoticeIfNotSetAsTheseAreRequired = $params['frequency_interval'] . $params['frequency_unit'];
+    if ($propertyBag->getIsRecur()) {
+      $throwAnENoticeIfNotSetAsTheseAreRequired = $propertyBag->getRecurFrequencyInterval() . $propertyBag->getRecurFrequencyUnit();
     }
     // no translation in Dummy processor
-    $cookedParams = $params;
     CRM_Utils_Hook::alterPaymentProcessorParams($this,
       $params,
-      $cookedParams
+      $propertyBag
     );
     // This means we can test failing transactions by setting a past year in expiry. A full expiry check would
     // be more complete.
@@ -107,26 +94,25 @@ class CRM_Core_Payment_Dummy extends CRM_Core_Payment {
       $result['trxn_id'] = array_shift($this->_doDirectPaymentResult['trxn_id']);
       return $result;
     }
-    if ($this->_mode == 'test') {
+    if ($this->_mode === 'test') {
       $query = "SELECT MAX(trxn_id) FROM civicrm_contribution WHERE trxn_id LIKE 'test\\_%'";
       $p = [];
-      $trxn_id = strval(CRM_Core_DAO::singleValueQuery($query, $p));
+      $trxn_id = (string) CRM_Core_DAO::singleValueQuery($query, $p);
       $trxn_id = str_replace('test_', '', $trxn_id);
-      $trxn_id = intval($trxn_id) + 1;
+      $trxn_id = (int) $trxn_id + 1;
       $params['trxn_id'] = 'test_' . $trxn_id . '_' . uniqid();
     }
     else {
       $query = "SELECT MAX(trxn_id) FROM civicrm_contribution WHERE trxn_id LIKE 'live_%'";
       $p = [];
-      $trxn_id = strval(CRM_Core_DAO::singleValueQuery($query, $p));
+      $trxn_id = (string) CRM_Core_DAO::singleValueQuery($query, $p);
       $trxn_id = str_replace('live_', '', $trxn_id);
-      $trxn_id = intval($trxn_id) + 1;
+      $trxn_id = (int) $trxn_id + 1;
       $params['trxn_id'] = 'live_' . $trxn_id . '_' . uniqid();
     }
-    $params['gross_amount'] = $params['amount'];
+    $params['gross_amount'] = $propertyBag->getAmount();
     // Add a fee_amount so we can make sure fees are handled properly in underlying classes.
     $params['fee_amount'] = 1.50;
-    $params['net_amount'] = $params['gross_amount'] - $params['fee_amount'];
     $params['description'] = $this->getPaymentDescription($params);
 
     return $params;
@@ -142,7 +128,8 @@ class CRM_Core_Payment_Dummy extends CRM_Core_Payment {
   }
 
   /**
-   * Supports altering future start dates
+   * Supports altering future start dates.
+   *
    * @return bool
    */
   public function supportsFutureRecurStartDate() {
@@ -159,28 +146,6 @@ class CRM_Core_Payment_Dummy extends CRM_Core_Payment {
    */
   public function doRefund(&$params) {}
 
-  /**
-   * Generate error object.
-   *
-   * Throwing exceptions is preferred over this.
-   *
-   * @param string $errorCode
-   * @param string $errorMessage
-   *
-   * @return CRM_Core_Error
-   *   Error object.
-   */
-  public function &error($errorCode = NULL, $errorMessage = NULL) {
-    $e = CRM_Core_Error::singleton();
-    if ($errorCode) {
-      $e->push($errorCode, 0, NULL, $errorMessage);
-    }
-    else {
-      $e->push(9001, 0, NULL, 'Unknown System Error.');
-    }
-    return $e;
-  }
-
   /**
    * This function checks to see if we have the right config values.
    *
@@ -221,13 +186,37 @@ class CRM_Core_Payment_Dummy extends CRM_Core_Payment {
   }
 
   /**
-   * @param string $message
-   * @param array $params
+   * Does this processor support cancelling recurring contributions through code.
    *
-   * @return bool|object
+   * If the processor returns true it must be possible to take action from within CiviCRM
+   * that will result in no further payments being processed. In the case of token processors (e.g
+   * IATS, eWay) updating the contribution_recur table is probably sufficient.
+   *
+   * @return bool
    */
-  public function cancelSubscription(&$message = '', $params = []) {
+  protected function supportsCancelRecurring() {
     return TRUE;
   }
 
+  /**
+   * Cancel a recurring subscription.
+   *
+   * Payment processor classes should override this rather than implementing cancelSubscription.
+   *
+   * A PaymentProcessorException should be thrown if the update of the contribution_recur
+   * record should not proceed (in many cases this function does nothing
+   * as the payment processor does not need to take any action & this should silently
+   * proceed. Note the form layer will only call this after calling
+   * $processor->supports('cancelRecurring');
+   *
+   * @param \Civi\Payment\PropertyBag $propertyBag
+   *
+   * @return array
+   *
+   * @throws \Civi\Payment\Exception\PaymentProcessorException
+   */
+  public function doCancelRecurring(PropertyBag $propertyBag) {
+    return ['message' => ts('Recurring contribution cancelled')];
+  }
+
 }
diff --git a/civicrm/CRM/Core/Payment/Elavon.php b/civicrm/CRM/Core/Payment/Elavon.php
index 71ca4d5af1dd2065894cccd68aaa9c2f6b3a94e9..35a93702e1ce371588f909e1be1f924895f1f8ed 100644
--- a/civicrm/CRM/Core/Payment/Elavon.php
+++ b/civicrm/CRM/Core/Payment/Elavon.php
@@ -28,14 +28,6 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment {
   const
     CHARSET = 'UFT-8';
 
-  /**
-   * We only need one instance of this object. So we use the singleton
-   * pattern and cache the instance in this variable
-   *
-   * @var CRM_Core_Payment_Elavon
-   */
-  static private $_singleton = NULL;
-
   /**
    * Constructor.
    *
@@ -50,7 +42,6 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment {
     // live or test
     $this->_mode = $mode;
     $this->_paymentProcessor = $paymentProcessor;
-    $this->_processorName = ts('Elavon');
   }
 
   /**
@@ -120,11 +111,11 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment {
    */
   public function doDirectPayment(&$params) {
     if (isset($params['is_recur']) && $params['is_recur'] == TRUE) {
-      CRM_Core_Error::fatal(ts('Elavon - recurring payments not implemented'));
+      throw new CRM_Core_Exception(ts('Elavon - recurring payments not implemented'));
     }
 
     if (!defined('CURLOPT_SSLCERT')) {
-      CRM_Core_Error::fatal(ts('Elavon / Nova Virtual Merchant Gateway requires curl with SSL support'));
+      throw new CRM_Core_Exception(ts('Elavon / Nova Virtual Merchant Gateway requires curl with SSL support'));
     }
 
     //Create the array of variables to be sent to the processor from the $params array
diff --git a/civicrm/CRM/Core/Payment/FirstData.php b/civicrm/CRM/Core/Payment/FirstData.php
index a8f5b4f7ff42d2239ce9865195ba2e250dbd93e7..5e520cf7e452608cfbdef142c41ae7660079a544 100644
--- a/civicrm/CRM/Core/Payment/FirstData.php
+++ b/civicrm/CRM/Core/Payment/FirstData.php
@@ -55,14 +55,6 @@ class CRM_Core_Payment_FirstData extends CRM_Core_Payment {
   // (not used, implicit in the API, might need to convert?)
   const CHARSET = 'UFT-8';
 
-  /**
-   * We only need one instance of this object. So we use the singleton
-   * pattern and cache the instance in this variable
-   *
-   * @var object
-   */
-  static private $_singleton = NULL;
-
   /**
    * Constructor.
    *
@@ -156,11 +148,11 @@ class CRM_Core_Payment_FirstData extends CRM_Core_Payment {
    */
   public function doDirectPayment(&$params) {
     if ($params['is_recur'] == TRUE) {
-      CRM_Core_Error::fatal(ts('First Data - recurring payments not implemented'));
+      throw new CRM_Core_Exception(ts('First Data - recurring payments not implemented'));
     }
 
     if (!defined('CURLOPT_SSLCERT')) {
-      CRM_Core_Error::fatal(ts('%1 - Gateway requires curl with SSL support', [1 => $paymentProcessor]));
+      throw new CRM_Core_Exception(ts('%1 - Gateway requires curl with SSL support', [1 => $paymentProcessor]));
     }
 
     /**********************************************************
diff --git a/civicrm/CRM/Core/Payment/Manual.php b/civicrm/CRM/Core/Payment/Manual.php
index 302756419984b8573571d58a2806f67496ccba16..0a311b48da916fbfd04420dec9c98312f15e220f 100644
--- a/civicrm/CRM/Core/Payment/Manual.php
+++ b/civicrm/CRM/Core/Payment/Manual.php
@@ -196,6 +196,19 @@ class CRM_Core_Payment_Manual extends CRM_Core_Payment {
     return TRUE;
   }
 
+  /**
+   * Does the processor support the user having a choice as to whether to cancel the recurring with the processor?
+   *
+   * If this returns TRUE then there will be an option to send a cancellation request in the cancellation form.
+   *
+   * This would normally be false for processors where CiviCRM maintains the schedule.
+   *
+   * @return bool
+   */
+  protected function supportsCancelRecurringNotifyOptional() {
+    return FALSE;
+  }
+
   /**
    * Are back office payments supported.
    *
diff --git a/civicrm/CRM/Core/Payment/PayJunction.php b/civicrm/CRM/Core/Payment/PayJunction.php
index 646421ee03f1409bda82a19d4fd89fa7976b92a8..f544ee10c03da276a6bb2e56ebcce493ceded1ca 100644
--- a/civicrm/CRM/Core/Payment/PayJunction.php
+++ b/civicrm/CRM/Core/Payment/PayJunction.php
@@ -22,14 +22,6 @@ class CRM_Core_Payment_PayJunction extends CRM_Core_Payment {
   // (not used, implicit in the API, might need to convert?)
   const CHARSET = 'UFT-8';
 
-  /**
-   * We only need one instance of this object. So we use the singleton
-   * pattern and cache the instance in this variable
-   *
-   * @var object
-   */
-  static private $_singleton = NULL;
-
   /**
    * Constructor.
    *
@@ -43,7 +35,6 @@ class CRM_Core_Payment_PayJunction extends CRM_Core_Payment {
   public function __construct($mode, &$paymentProcessor) {
     $this->_mode = $mode;
     $this->_paymentProcessor = $paymentProcessor;
-    $this->_processorName = ts('PayJunction');
   }
 
   /**
diff --git a/civicrm/CRM/Core/Payment/PayPalIPN.php b/civicrm/CRM/Core/Payment/PayPalIPN.php
index a506ab3f29714c9e737bfb09833608f1164b38f9..0d3eae538678fbbaf91030744070c93bd411cde5 100644
--- a/civicrm/CRM/Core/Payment/PayPalIPN.php
+++ b/civicrm/CRM/Core/Payment/PayPalIPN.php
@@ -263,8 +263,9 @@ class CRM_Core_Payment_PayPalIPN extends CRM_Core_Payment_BaseIPN {
     if ($status == 'Denied' || $status == 'Failed' || $status == 'Voided') {
       return $this->failed($objects, $transaction);
     }
-    elseif ($status == 'Pending') {
-      return $this->pending($objects, $transaction);
+    if ($status === 'Pending') {
+      Civi::log()->debug('Returning since contribution status is Pending');
+      return;
     }
     elseif ($status == 'Refunded' || $status == 'Reversed') {
       return $this->cancelled($objects, $transaction);
diff --git a/civicrm/CRM/Core/Payment/PayPalImpl.php b/civicrm/CRM/Core/Payment/PayPalImpl.php
index 42fdfa2f8052d8588edaedeaddde121995fc3f12..022067bd98b568294c53c5d23e5763aa600fa93a 100644
--- a/civicrm/CRM/Core/Payment/PayPalImpl.php
+++ b/civicrm/CRM/Core/Payment/PayPalImpl.php
@@ -43,19 +43,6 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
   public function __construct($mode, &$paymentProcessor) {
     $this->_mode = $mode;
     $this->_paymentProcessor = $paymentProcessor;
-
-    if ($this->isPayPalType($this::PAYPAL_STANDARD)) {
-      $this->_processorName = ts('PayPal Standard');
-    }
-    elseif ($this->isPayPalType($this::PAYPAL_EXPRESS)) {
-      $this->_processorName = ts('PayPal Express');
-    }
-    elseif ($this->isPayPalType($this::PAYPAL_PRO)) {
-      $this->_processorName = ts('PayPal Pro');
-    }
-    else {
-      throw new PaymentProcessorException('CRM_Core_Payment_PayPalImpl: Payment processor type is not defined!');
-    }
   }
 
   /**
@@ -929,7 +916,7 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
     // if recurring donations, add a few more items
     if (!empty($params['is_recur'])) {
       if (!$params['contributionRecurID']) {
-        CRM_Core_Error::fatal(ts('Recurring contribution, but no database id'));
+        throw new CRM_Core_Exception(ts('Recurring contribution, but no database id'));
       }
 
       $paypalParams += [
diff --git a/civicrm/CRM/Core/Payment/PayPalProIPN.php b/civicrm/CRM/Core/Payment/PayPalProIPN.php
index 885fac970f2edc8897597746bb0d21c16fbfc369..a2155527240bd05529736c1e7e676e0f25b969b2 100644
--- a/civicrm/CRM/Core/Payment/PayPalProIPN.php
+++ b/civicrm/CRM/Core/Payment/PayPalProIPN.php
@@ -351,8 +351,8 @@ class CRM_Core_Payment_PayPalProIPN extends CRM_Core_Payment_BaseIPN {
       $this->failed($objects, $transaction);
       return;
     }
-    elseif ($status == 'Pending') {
-      $this->pending($objects, $transaction);
+    if ($status === 'Pending') {
+      Civi::log()->debug('Returning since contribution status is Pending');
       return;
     }
     elseif ($status == 'Refunded' || $status == 'Reversed') {
@@ -588,7 +588,7 @@ INNER JOIN civicrm_membership_payment mp ON m.id = mp.membership_id AND mp.contr
     // & suspec main function may be a victom of copy & paste
     // membership would be an easy add - but not relevant to my customer...
     $this->_component = $input['component'] = 'contribute';
-    $input['trxn_date'] = date('Y-m-d-H-i-s', strtotime(self::retrieve('time_created', 'String')));
+    $input['trxn_date'] = date('Y-m-d H:i:s', strtotime(self::retrieve('time_created', 'String')));
     $paymentProcessorID = $contributionRecur['payment_processor_id'];
 
     if (!$this->validateData($input, $ids, $objects, TRUE, $paymentProcessorID)) {
diff --git a/civicrm/CRM/Core/Payment/PayflowPro.php b/civicrm/CRM/Core/Payment/PayflowPro.php
index 1d4b92c5e96ce64fb9fad1c3c35985c721dcc6b7..1f5b76933676c06eb4b92ee553f6f7be2623bb41 100644
--- a/civicrm/CRM/Core/Payment/PayflowPro.php
+++ b/civicrm/CRM/Core/Payment/PayflowPro.php
@@ -17,14 +17,6 @@ class CRM_Core_Payment_PayflowPro extends CRM_Core_Payment {
   const
     CHARSET = 'UFT-8';
 
-  /**
-   * We only need one instance of this object. So we use the singleton
-   * pattern and cache the instance in this variable
-   *
-   * @var object
-   */
-  static private $_singleton = NULL;
-
   /**
    * Constructor
    *
@@ -36,7 +28,6 @@ class CRM_Core_Payment_PayflowPro extends CRM_Core_Payment {
     // live or test
     $this->_mode = $mode;
     $this->_paymentProcessor = $paymentProcessor;
-    $this->_processorName = ts('Payflow Pro');
   }
 
   /*
@@ -58,7 +49,7 @@ class CRM_Core_Payment_PayflowPro extends CRM_Core_Payment {
    */
   public function doDirectPayment(&$params) {
     if (!defined('CURLOPT_SSLCERT')) {
-      CRM_Core_Error::fatal(ts('Payflow Pro requires curl with SSL support'));
+      throw new CRM_Core_Exception(ts('Payflow Pro requires curl with SSL support'));
     }
 
     /*
@@ -373,7 +364,7 @@ class CRM_Core_Payment_PayflowPro extends CRM_Core_Payment {
    * @throws Exception
    */
   public function doTransferCheckout(&$params, $component) {
-    CRM_Core_Error::fatal(ts('This function is not implemented'));
+    throw new CRM_Core_Exception(ts('This function is not implemented'));
   }
 
   /**
diff --git a/civicrm/CRM/Core/Payment/PaymentExpress.php b/civicrm/CRM/Core/Payment/PaymentExpress.php
index 2f40ea054f080877e1e85a12e1339eab56f9f179..54ddfa21c6363a4ecd74b204bd5fccdf7367fabd 100644
--- a/civicrm/CRM/Core/Payment/PaymentExpress.php
+++ b/civicrm/CRM/Core/Payment/PaymentExpress.php
@@ -41,14 +41,6 @@ class CRM_Core_Payment_PaymentExpress extends CRM_Core_Payment {
 
   protected $_mode = NULL;
 
-  /**
-   * We only need one instance of this object. So we use the singleton
-   * pattern and cache the instance in this variable
-   *
-   * @var object
-   */
-  static private $_singleton = NULL;
-
   /**
    * Constructor.
    *
@@ -63,7 +55,6 @@ class CRM_Core_Payment_PaymentExpress extends CRM_Core_Payment {
 
     $this->_mode = $mode;
     $this->_paymentProcessor = $paymentProcessor;
-    $this->_processorName = ts('DPS Payment Express');
   }
 
   /**
@@ -103,7 +94,7 @@ class CRM_Core_Payment_PaymentExpress extends CRM_Core_Payment {
    *   Assoc array of input parameters for this transaction.
    */
   public function doDirectPayment(&$params) {
-    CRM_Core_Error::fatal(ts('This function is not implemented'));
+    throw new CRM_Core_Exception(ts('This function is not implemented'));
   }
 
   /**
@@ -118,7 +109,7 @@ class CRM_Core_Payment_PaymentExpress extends CRM_Core_Payment {
     $component = strtolower($component);
     $config = CRM_Core_Config::singleton();
     if ($component != 'contribute' && $component != 'event') {
-      CRM_Core_Error::fatal(ts('Component is invalid'));
+      throw new CRM_Core_Exception(ts('Component is invalid'));
     }
 
     $url = CRM_Utils_System::externUrl('extern/pxIPN');
@@ -211,7 +202,7 @@ class CRM_Core_Payment_PaymentExpress extends CRM_Core_Payment {
       }
       else {
         // calling DPS failed
-        CRM_Core_Error::fatal(ts('Unable to establish connection to the payment gateway.'));
+        throw new CRM_Core_Exception(ts('Unable to establish connection to the payment gateway.'));
       }
     }
     else {
diff --git a/civicrm/CRM/Core/Payment/PaymentExpressIPN.php b/civicrm/CRM/Core/Payment/PaymentExpressIPN.php
index ab34ea13f1e536ba40766cefe6e135b0340e9a01..878b948f96cbca6b164700726f893ebd0c7a68ca 100644
--- a/civicrm/CRM/Core/Payment/PaymentExpressIPN.php
+++ b/civicrm/CRM/Core/Payment/PaymentExpressIPN.php
@@ -38,14 +38,6 @@
  */
 class CRM_Core_Payment_PaymentExpressIPN extends CRM_Core_Payment_BaseIPN {
 
-  /**
-   * We only need one instance of this object. So we use the singleton
-   * pattern and cache the instance in this variable
-   *
-   * @var object
-   */
-  static private $_singleton = NULL;
-
   /**
    * Mode of operation: live or test
    *
@@ -109,6 +101,8 @@ class CRM_Core_Payment_PaymentExpressIPN extends CRM_Core_Payment_BaseIPN {
    * @param $transactionReference
    *
    * @return bool
+   * @throws \CRM_Core_Exception
+   * @throws \CiviCRM_API3_Exception
    */
   public function newOrderNotify($success, $privateData, $component, $amount, $transactionReference) {
     $ids = $input = $params = [];
@@ -160,8 +154,6 @@ class CRM_Core_Payment_PaymentExpressIPN extends CRM_Core_Payment_BaseIPN {
       return FALSE;
     }
 
-    $transaction = new CRM_Core_Transaction();
-
     // check if contribution is already completed, if so we ignore this ipn
 
     if ($contribution->contribution_status_id == 1) {
@@ -181,7 +173,7 @@ class CRM_Core_Payment_PaymentExpressIPN extends CRM_Core_Payment_BaseIPN {
         $contribution->trxn_id = $ids['membership'];
       }
     }
-    $this->completeTransaction($input, $ids, $objects, $transaction);
+    CRM_Contribute_BAO_Contribution::completeOrder($input, $ids, $objects);
     return TRUE;
   }
 
@@ -308,7 +300,7 @@ class CRM_Core_Payment_PaymentExpressIPN extends CRM_Core_Payment_BaseIPN {
         $info = curl_getinfo($curl);
         if ($info['http_code'] < 200 || $info['http_code'] > 299) {
           $log_message = "DPS error: HTTP {$info['http_code']} retrieving {$info['url']}.";
-          CRM_Core_Error::fatal($log_message);
+          throw new CRM_Core_Exception($log_message);
         }
         else {
           fwrite($message_log, sprintf("\n\r%s:- %s\n", date("D M j G:i:s T Y"), $response));
@@ -318,7 +310,7 @@ class CRM_Core_Payment_PaymentExpressIPN extends CRM_Core_Payment_BaseIPN {
           $valid = CRM_Core_Payment_PaymentExpressUtils::_xmlAttribute($response, 'valid');
           // CRM_Core_Payment_PaymentExpressUtils::_xmlAttribute() returns NULL if preg fails.
           if (is_null($valid)) {
-            CRM_Core_Error::fatal(ts("DPS error: Unable to parse XML response from DPS.", [1 => $valid]));
+            throw new CRM_Core_Exception(ts("DPS error: Unable to parse XML response from DPS.", [1 => $valid]));
           }
           $success = CRM_Core_Payment_PaymentExpressUtils::_xmlElement($response, 'Success');
           $txnId = CRM_Core_Payment_PaymentExpressUtils::_xmlElement($response, 'TxnId');
@@ -334,7 +326,7 @@ class CRM_Core_Payment_PaymentExpressIPN extends CRM_Core_Payment_BaseIPN {
       }
       else {
         // calling DPS failed
-        CRM_Core_Error::fatal(ts('Unable to establish connection to the payment gateway to verify transaction response.'));
+        throw new CRM_Core_Exception(ts('Unable to establish connection to the payment gateway to verify transaction response.'));
         exit;
       }
     }
diff --git a/civicrm/CRM/Core/Payment/ProcessorForm.php b/civicrm/CRM/Core/Payment/ProcessorForm.php
index 322f07cfd2f3ba908cc1f8311240d1bde485de47..3f927557b0bd819fb2d4570d931dfd7f1bd21710 100644
--- a/civicrm/CRM/Core/Payment/ProcessorForm.php
+++ b/civicrm/CRM/Core/Payment/ProcessorForm.php
@@ -106,7 +106,7 @@ class CRM_Core_Payment_ProcessorForm {
     if (!empty($form->_values['is_monetary']) &&
       !$form->_paymentProcessor['class_name'] && empty($form->_values['is_pay_later'])
     ) {
-      CRM_Core_Error::fatal(ts('Payment processor is not set for this page'));
+      CRM_Core_Error::statusBounce(ts('Payment processor is not set for this page'));
     }
 
     if (!empty($form->_membershipBlock) && !empty($form->_membershipBlock['is_separate_payment']) &&
@@ -115,7 +115,7 @@ class CRM_Core_Payment_ProcessorForm {
       )
     ) {
 
-      CRM_Core_Error::fatal(ts('This contribution page is configured to support separate contribution and membership payments. This %1 plugin does not currently support multiple simultaneous payments, or the option to "Execute real-time monetary transactions" is disabled. Please contact the site administrator and notify them of this error',
+      CRM_Core_Error::statusBounce(ts('This contribution page is configured to support separate contribution and membership payments. This %1 plugin does not currently support multiple simultaneous payments, or the option to "Execute real-time monetary transactions" is disabled. Please contact the site administrator and notify them of this error',
           [1 => $form->_paymentProcessor['payment_processor_type']]
         )
       );
diff --git a/civicrm/CRM/Core/Payment/Realex.php b/civicrm/CRM/Core/Payment/Realex.php
index 8100aa95319546b764e21f63e5318ed6d543ccc5..610b101cbdb6e251f8c353bbced38b378c7e1ba1 100644
--- a/civicrm/CRM/Core/Payment/Realex.php
+++ b/civicrm/CRM/Core/Payment/Realex.php
@@ -44,14 +44,6 @@ class CRM_Core_Payment_Realex extends CRM_Core_Payment {
 
   protected $_params = [];
 
-  /**
-   * We only need one instance of this object. So we use the singleton
-   * pattern and cache the instance in this variable
-   *
-   * @var object
-   */
-  static private $_singleton = NULL;
-
   /**
    * Constructor.
    *
@@ -65,7 +57,6 @@ class CRM_Core_Payment_Realex extends CRM_Core_Payment {
   public function __construct($mode, &$paymentProcessor) {
     $this->_mode = $mode;
     $this->_paymentProcessor = $paymentProcessor;
-    $this->_processorName = ts('Realex');
 
     $this->_setParam('merchant_ref', $paymentProcessor['user_name']);
     $this->_setParam('secret', $paymentProcessor['password']);
diff --git a/civicrm/CRM/Core/Payment/eWAY.php b/civicrm/CRM/Core/Payment/eWAY.php
index 8f198b46c82483316aab9c4c28be5fcbbd42e14c..bc6710ae8d5f3207f3e2e1deb16965c3edb12bae 100644
--- a/civicrm/CRM/Core/Payment/eWAY.php
+++ b/civicrm/CRM/Core/Payment/eWAY.php
@@ -102,14 +102,6 @@ class CRM_Core_Payment_eWAY extends CRM_Core_Payment {
   // (not used, implicit in the API, might need to convert?)
   const CHARSET = 'UTF-8';
 
-  /**
-   * We only need one instance of this object. So we use the singleton
-   * pattern and cache the instance in this variable
-   *
-   * @var object
-   */
-  static private $_singleton = NULL;
-
   /**
    * *******************************************************
    * Constructor
@@ -126,7 +118,6 @@ class CRM_Core_Payment_eWAY extends CRM_Core_Payment {
     // live or test
     $this->_mode = $mode;
     $this->_paymentProcessor = $paymentProcessor;
-    $this->_processorName = ts('eWay');
   }
 
   /**
@@ -139,11 +130,11 @@ class CRM_Core_Payment_eWAY extends CRM_Core_Payment {
    */
   public function doDirectPayment(&$params) {
     if (CRM_Utils_Array::value('is_recur', $params) == TRUE) {
-      CRM_Core_Error::fatal(ts('eWAY - recurring payments not implemented'));
+      throw new CRM_Core_Exception(ts('eWAY - recurring payments not implemented'));
     }
 
     if (!defined('CURLOPT_SSLCERT')) {
-      CRM_Core_Error::fatal(ts('eWAY - Gateway requires curl with SSL support'));
+      throw new CRM_Core_Exception(ts('eWAY - Gateway requires curl with SSL support'));
     }
 
     // eWAY Client ID
diff --git a/civicrm/CRM/Core/Permission/Base.php b/civicrm/CRM/Core/Permission/Base.php
index a91c126b8c113271110d79e4bbbb0aea66ab7808..b3f5a066f17114eeb6382021a20882e145dda3ea 100644
--- a/civicrm/CRM/Core/Permission/Base.php
+++ b/civicrm/CRM/Core/Permission/Base.php
@@ -170,9 +170,10 @@ class CRM_Core_Permission_Base {
    * @param string $permissionName
    *   Name of the permission we are interested in.
    *
+   * @throws CRM_Core_Exception.
    */
   public function permissionEmails($permissionName) {
-    CRM_Core_Error::fatal("this function only works in Drupal 6 at the moment");
+    throw new CRM_Core_Exception("this function only works in Drupal 6 at the moment");
   }
 
   /**
@@ -181,9 +182,10 @@ class CRM_Core_Permission_Base {
    * @param string $roleName
    *   Name of the role we are interested in.
    *
+   * @throws CRM_Core_Exception.
    */
   public function roleEmails($roleName) {
-    CRM_Core_Error::fatal("this function only works in Drupal 6 at the moment");
+    throw new CRM_Core_Exception("this function only works in Drupal 6 at the moment");
   }
 
   /**
diff --git a/civicrm/CRM/Core/PseudoConstant.php b/civicrm/CRM/Core/PseudoConstant.php
index afbb0b5fbb4120b2515db85bd44c6f5d191809dd..e158ee21f0a8ab0086ffecd1e897c1fb84a79f0e 100644
--- a/civicrm/CRM/Core/PseudoConstant.php
+++ b/civicrm/CRM/Core/PseudoConstant.php
@@ -194,7 +194,7 @@ class CRM_Core_PseudoConstant {
       'fresh' => FALSE,
       'context' => $context,
     ];
-    $entity = CRM_Core_DAO_AllCoreTables::getBriefName(CRM_Core_DAO_AllCoreTables::getCanonicalClassName($daoName));
+    $entity = CRM_Core_DAO_AllCoreTables::getBriefName($daoName);
 
     // Custom fields are not in the schema
     if (strpos($fieldName, 'custom_') === 0 && is_numeric($fieldName[7])) {
@@ -1527,7 +1527,7 @@ WHERE  id = %1
     }
     // Filter domain specific options
     if (in_array('domain_id', $availableFields)) {
-      $wheres[] = 'domain_id = ' . CRM_Core_Config::domainID();
+      $wheres[] = 'domain_id = ' . CRM_Core_Config::domainID() . ' OR  domain_id is NULL';
     }
     $queryParams = [
       1 => [$params['keyColumn'], 'String', CRM_Core_DAO::QUERY_FORMAT_NO_QUOTES],
diff --git a/civicrm/CRM/Core/Region.php b/civicrm/CRM/Core/Region.php
index b1809af63629f209e129148ec10e6e2f5659782d..5d101d1d70ff0cc9e682fe5044c6a951cb2dd743 100644
--- a/civicrm/CRM/Core/Region.php
+++ b/civicrm/CRM/Core/Region.php
@@ -68,7 +68,7 @@ class CRM_Core_Region {
   /**
    * Add a snippet of content to a region.
    *
-   * @code
+   * ```
    * CRM_Core_Region::instance('page-header')->add(array(
    *   'markup' => '<div style="color:red">Hello!</div>',
    * ));
@@ -81,7 +81,7 @@ class CRM_Core_Region {
    * CRM_Core_Region::instance('page-header')->add(array(
    *   'callback' => 'myextension_callback_function',
    * ));
-   * @endcode
+   * ```
    *
    * Note: This function does not perform any extra encoding of markup, script code, or etc. If
    * you're passing in user-data, you must clean it yourself.
diff --git a/civicrm/CRM/Core/SelectValues.php b/civicrm/CRM/Core/SelectValues.php
index 6b451af1ebde9bf00baeec118eeb34d13ae187a8..b2c515867e89ed7db981754c3165b46a974f454f 100644
--- a/civicrm/CRM/Core/SelectValues.php
+++ b/civicrm/CRM/Core/SelectValues.php
@@ -68,11 +68,7 @@ class CRM_Core_SelectValues {
    * @return array
    */
   public static function contactType() {
-    static $contactType = NULL;
-    if (!$contactType) {
-      $contactType = CRM_Contact_BAO_ContactType::basicTypePairs();
-    }
-    return $contactType;
+    return CRM_Contact_BAO_ContactType::basicTypePairs();
   }
 
   /**
@@ -181,12 +177,9 @@ class CRM_Core_SelectValues {
       'Select Date' => ts('Select Date'),
       'File' => ts('File'),
       'Select State/Province' => ts('Select State/Province'),
-      'Multi-Select State/Province' => ts('Multi-Select State/Province'),
       'Select Country' => ts('Select Country'),
-      'Multi-Select Country' => ts('Multi-Select Country'),
       'RichTextEditor' => ts('Rich Text Editor'),
       'Autocomplete-Select' => ts('Autocomplete-Select'),
-      'Multi-Select' => ts('Multi-Select'),
       'Link' => ts('Link'),
       'ContactReference' => ts('Autocomplete-Select'),
     ];
@@ -1179,4 +1172,14 @@ class CRM_Core_SelectValues {
     return $ret;
   }
 
+  public static function fieldSerialization() {
+    return [
+      CRM_Core_DAO::SERIALIZE_SEPARATOR_BOOKEND => 'separator_bookend',
+      CRM_Core_DAO::SERIALIZE_SEPARATOR_TRIMMED => 'separator_trimmed',
+      CRM_Core_DAO::SERIALIZE_JSON => 'json',
+      CRM_Core_DAO::SERIALIZE_PHP => 'php',
+      CRM_Core_DAO::SERIALIZE_COMMA => 'comma',
+    ];
+  }
+
 }
diff --git a/civicrm/CRM/Core/ShowHideBlocks.php b/civicrm/CRM/Core/ShowHideBlocks.php
index 934eb317a3aa8f3a7e38f90ccb56f36512237ab6..1b4786987c947d969f8f3878f3104d76f22e727c 100644
--- a/civicrm/CRM/Core/ShowHideBlocks.php
+++ b/civicrm/CRM/Core/ShowHideBlocks.php
@@ -16,14 +16,6 @@
  */
 class CRM_Core_ShowHideBlocks {
 
-  /**
-   * The icons prefixed to block show and hide links.
-   *
-   * @var string
-   */
-  public static $_showIcon;
-  public static $_hideIcon;
-
   /**
    * The array of ids of blocks that will be shown.
    *
@@ -64,17 +56,6 @@ class CRM_Core_ShowHideBlocks {
     }
   }
 
-  /**
-   * Load icon vars used in hide and show links.
-   */
-  public static function setIcons() {
-    if (!isset(self::$_showIcon)) {
-      $config = CRM_Core_Config::singleton();
-      self::$_showIcon = '<img src="' . $config->resourceBase . 'i/TreePlus.gif" class="action-icon" alt="' . ts('show field or section') . '"/>';
-      self::$_hideIcon = '<img src="' . $config->resourceBase . 'i/TreeMinus.gif" class="action-icon" alt="' . ts('hide field or section') . '"/>';
-    }
-  }
-
   /**
    * Add the values from this class to the template.
    */
@@ -130,115 +111,4 @@ class CRM_Core_ShowHideBlocks {
     }
   }
 
-  /**
-   * Create a well formatted html link from the smaller pieces.
-   *
-   * @param string $name
-   *   Name of the link.
-   * @param string $href
-   * @param string $text
-   * @param string $js
-   *
-   * @return string
-   *   the formatted html link
-   */
-  public static function linkHtml($name, $href, $text, $js) {
-    return '<a name="' . $name . '" id="' . $name . '" href="' . $href . '" ' . $js . ">$text</a>";
-  }
-
-  /**
-   * Create links that we can use in the form.
-   *
-   * @param CRM_Core_Form $form
-   *   The form object.
-   * @param string $prefix
-   *   The attribute that we are referencing.
-   * @param string $showLinkText
-   *   The text to be shown for the show link.
-   * @param string $hideLinkText
-   *   The text to be shown for the hide link.
-   *
-   * @param bool $assign
-   *
-   * @return array
-   */
-  public static function links(&$form, $prefix, $showLinkText, $hideLinkText, $assign = TRUE) {
-    $showCode = "if(event.preventDefault) event.preventDefault(); else event.returnValue = false; cj('#id_{$prefix}').show(); cj('#id_{$prefix}_show').hide();";
-    $hideCode = "if(event.preventDefault) event.preventDefault(); else event.returnValue = false; cj('#id_{$prefix}').hide(); cj('#id_{$prefix}_show').show();";
-
-    self::setIcons();
-    $values = [];
-    $values['show'] = self::linkHtml("${prefix}_show", "#${prefix}_hide", self::$_showIcon . $showLinkText, "onclick=\"$showCode\"");
-    $values['hide'] = self::linkHtml("${prefix}_hide", "#${prefix}", self::$_hideIcon . $hideLinkText, "onclick=\"$hideCode\"");
-
-    if ($assign) {
-      $form->assign($prefix, $values);
-    }
-    else {
-      return $values;
-    }
-  }
-
-  /**
-   * Create html link elements that we can use in the form.
-   *
-   * @param CRM_Core_Form $form
-   *   The form object.
-   * @param int $index
-   *   The current index of the element being processed.
-   * @param int $maxIndex
-   *   The max number of elements that will be processed.
-   * @param string $prefix
-   *   The attribute that we are referencing.
-   * @param string $showLinkText
-   *   The text to be shown for the show link.
-   * @param string $hideLinkText
-   *   The text to be shown for the hide link.
-   * @param string $elementType
-   *   The set the class.
-   * @param string $hideLink
-   *   The hide block string.
-   */
-  public function linksForArray(&$form, $index, $maxIndex, $prefix, $showLinkText, $hideLinkText, $elementType = NULL, $hideLink = NULL) {
-    $showHidePrefix = str_replace(["]", "["], ["", "_"], $prefix);
-    $showHidePrefix = "id_" . $showHidePrefix;
-    if ($index == $maxIndex) {
-      $showCode = $hideCode = "return false;";
-    }
-    else {
-      $next = $index + 1;
-      if ($elementType) {
-        $showCode = "cj('#${prefix}_${next}_show').show(); return false;";
-        if ($hideLink) {
-          $hideCode = $hideLink;
-        }
-        else {
-          $hideCode = "cj('#${prefix}_${next}_show, #${prefix}_${next}').hide(); return false;";
-        }
-      }
-      else {
-        $showCode = "cj('#{$showHidePrefix}_{$next}_show').show(); return false;";
-        $hideCode = "cj('#{$showHidePrefix}_{$next}_show, #{$showHidePrefix}_{$next}').hide(); return false;";
-      }
-    }
-
-    self::setIcons();
-    if ($elementType) {
-      $form->addElement('link', "${prefix}[${index}][show]", NULL, "#${prefix}_${index}", self::$_showIcon . $showLinkText,
-        ['onclick' => "cj('#${prefix}_${index}_show').hide(); cj('#${prefix}_${index}').show();" . $showCode]
-      );
-      $form->addElement('link', "${prefix}[${index}][hide]", NULL, "#${prefix}_${index}", self::$_hideIcon . $hideLinkText,
-        ['onclick' => "cj('#${prefix}_${index}').hide(); cj('#${prefix}_${index}_show').show();" . $hideCode]
-      );
-    }
-    else {
-      $form->addElement('link', "${prefix}[${index}][show]", NULL, "#${prefix}_${index}", self::$_showIcon . $showLinkText,
-        ['onclick' => "cj('#{$showHidePrefix}_{$index}_show').hide(); cj('#{$showHidePrefix}_{$index}').show();" . $showCode]
-      );
-      $form->addElement('link', "${prefix}[${index}][hide]", NULL, "#${prefix}_${index}", self::$_hideIcon . $hideLinkText,
-        ['onclick' => "cj('#{$showHidePrefix}_{$index}').hide(); cj('#{$showHidePrefix}_{$index}_show').show();" . $hideCode]
-      );
-    }
-  }
-
 }
diff --git a/civicrm/CRM/Core/Smarty.php b/civicrm/CRM/Core/Smarty.php
index e45873d4dbbf55e53919283b75428c7e1e78ee72..86fcab419aa1565c46634f7cdddcc6ab71caabb0 100644
--- a/civicrm/CRM/Core/Smarty.php
+++ b/civicrm/CRM/Core/Smarty.php
@@ -258,14 +258,14 @@ class CRM_Core_Smarty extends Smarty {
   /**
    * Temporarily assign a list of variables.
    *
-   * @code
+   * ```
    * $smarty->pushScope(array(
    *   'first_name' => 'Alice',
    *   'last_name' => 'roberts',
    * ));
    * $html = $smarty->fetch('view-contact.tpl');
    * $smarty->popScope();
-   * @endcode
+   * ```
    *
    * @param array $vars
    *   (string $name => mixed $value).
diff --git a/civicrm/CRM/Core/Smarty/plugins/block.crmButton.php b/civicrm/CRM/Core/Smarty/plugins/block.crmButton.php
index b9869e20d312782210e547da09915394bc709e95..8c5f573a863020bea1eab8177f6697424ea28fef 100644
--- a/civicrm/CRM/Core/Smarty/plugins/block.crmButton.php
+++ b/civicrm/CRM/Core/Smarty/plugins/block.crmButton.php
@@ -38,9 +38,21 @@ function smarty_block_crmButton($params, $text, &$smarty) {
   // Always add class 'button' - fixme probably should be crm-button
   $params['class'] = empty($params['class']) ? 'button' : 'button ' . $params['class'];
   // Any FA icon works
-  $icon = CRM_Utils_Array::value('icon', $params, 'pencil');
+  if (array_key_exists('icon', $params) && !$params['icon']) {
+    // icon=0 should produce a button with no icon
+    $iconMarkup = '';
+  }
+  else {
+    $icon = $params['icon'] ?? 'fa-pencil';
+    // Assume for now that all icons are Font Awesome v4.x but handle if it's
+    // specified
+    if (strpos($icon, 'fa-') !== 0) {
+      $icon = "fa-$icon";
+    }
+    $iconMarkup = "<i class='crm-i $icon' aria-hidden=\"true\"></i>&nbsp; ";
+  }
   // All other params are treated as html attributes
   CRM_Utils_Array::remove($params, 'icon', 'p', 'q', 'a', 'f', 'h', 'fb', 'fe');
   $attributes = CRM_Utils_String::htmlAttributes($params);
-  return "<a $attributes><span><i class='crm-i fa-$icon'></i>&nbsp; $text</span></a>";
+  return "<a $attributes><span>$iconMarkup$text</span></a>";
 }
diff --git a/civicrm/CRM/Core/Smarty/plugins/block.crmScope.php b/civicrm/CRM/Core/Smarty/plugins/block.crmScope.php
index 66bb8bcb4fdac8098422820dd4d87ae1d74c588a..3e0e0280038625d4fbed68b8edbee3b8340e52df 100644
--- a/civicrm/CRM/Core/Smarty/plugins/block.crmScope.php
+++ b/civicrm/CRM/Core/Smarty/plugins/block.crmScope.php
@@ -5,7 +5,7 @@
  *
  * Example:
  *
- * @code
+ * ```
  * {tsScope x=1}
  *   Expect {$x}==1
  *   {tsScope x=2}
@@ -13,7 +13,7 @@
  *   {/tsScope}
  *   Expect {$x}==1
  * {/tsScope}
- * @endcode
+ * ```
  *
  * @param array $params
  *   Must define 'name'.
diff --git a/civicrm/CRM/Core/Smarty/plugins/block.icon.php b/civicrm/CRM/Core/Smarty/plugins/block.icon.php
new file mode 100644
index 0000000000000000000000000000000000000000..ac68cfe027ead11a8e1610a6f3f815dc760a520f
--- /dev/null
+++ b/civicrm/CRM/Core/Smarty/plugins/block.icon.php
@@ -0,0 +1,43 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @author Andrew Hunt, AGH Strategies
+ */
+
+/**
+ * Display an icon with some alternative text.
+ *
+ * This is a wrapper around CRM_Core_Page::icon().
+ *
+ * @param $params
+ *   - condition: if present and falsey, return empty
+ *   - icon: the icon class to display instead of fa-check
+ *   - anything else is passed along as attributes for the icon
+ *
+ * @param $text
+ *   The translated text to include in the icon's title and screen-reader text.
+ *
+ * @param $smarty
+ *
+ * @return string
+ */
+function smarty_block_icon($params, $text, &$smarty) {
+  $condition = array_key_exists('condition', $params) ? $params['condition'] : 1;
+  $icon = $params['icon'] ?? 'fa-check';
+  $dontPass = [
+    'condition' => 1,
+    'icon' => 1,
+  ];
+  return CRM_Core_Page::crmIcon($icon, $text, $condition, array_diff_key($params, $dontPass));
+}
diff --git a/civicrm/CRM/Core/Smarty/plugins/function.copyIcon.php b/civicrm/CRM/Core/Smarty/plugins/function.copyIcon.php
new file mode 100644
index 0000000000000000000000000000000000000000..19f4a369912ec018b171cf4a17dca1bda3f17295
--- /dev/null
+++ b/civicrm/CRM/Core/Smarty/plugins/function.copyIcon.php
@@ -0,0 +1,34 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @author Andrew Hunt, AGH Strategies
+ */
+
+/**
+ * Display a copy icon that copies the first row's values down.
+ *
+ * @param $params
+ *   - name: the field name
+ *   - title: the field title
+ *
+ * @param $smarty
+ *
+ * @return string
+ */
+function smarty_function_copyIcon($params, &$smarty) {
+  $text = ts('Click to copy %1 from row one to all rows.', [1 => $params['title']]);
+  return <<<HEREDOC
+<i class="crm-i fa-clone action-icon" fname="{$params['name']}" title="$text"><span class="sr-only">$text</span></i>
+HEREDOC;
+}
diff --git a/civicrm/CRM/Core/Smarty/plugins/function.crmVersion.php b/civicrm/CRM/Core/Smarty/plugins/function.crmVersion.php
index e25693c93a56fec4216f073dde485613e4057036..6f9ab7c617055fffd29104b4418b12a8548098a2 100644
--- a/civicrm/CRM/Core/Smarty/plugins/function.crmVersion.php
+++ b/civicrm/CRM/Core/Smarty/plugins/function.crmVersion.php
@@ -20,11 +20,11 @@
 /**
  * Display the CiviCRM version
  *
- * @code
+ * ```
  * The version is {crmVersion}.
  *
  * {crmVersion redact=auto assign=ver}The version is {$ver}.
- * @endcode
+ * ```
  *
  * @param $params
  * @param $smarty
diff --git a/civicrm/CRM/Core/Smarty/plugins/function.privacyFlag.php b/civicrm/CRM/Core/Smarty/plugins/function.privacyFlag.php
new file mode 100644
index 0000000000000000000000000000000000000000..050f7a19d6a7db8db342892a4142a5d92106f78b
--- /dev/null
+++ b/civicrm/CRM/Core/Smarty/plugins/function.privacyFlag.php
@@ -0,0 +1,56 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @author Andrew Hunt, AGH Strategies
+ *
+ */
+
+/**
+ * Display a banned icon to flag privacy preferences
+ *
+ * @param $params
+ *   - field: the applicable privacy field
+ *     (one of CRM_Core_SelectValues::privacy() or `on_hold`)
+ *   - condition: if present and falsey, return empty
+ *
+ * @param $smarty
+ *
+ * @return string
+ */
+function smarty_function_privacyFlag($params, &$smarty) {
+  if (array_key_exists('condition', $params) && !$params['condition']) {
+    return '';
+  }
+  $icons = [
+    'do_not_phone' => 'fa-phone',
+    'do_not_email' => 'fa-paper-plane',
+    'do_not_mail' => 'fa-envelope',
+    'do_not_sms' => 'fa-mobile',
+    'do_not_trade' => 'fa-exchange',
+    'is_opt_out' => 'fa-paper-plane-o',
+  ];
+  $titles = CRM_Core_SelectValues::privacy();
+  $field = $params['field'] ?? 'do_not_mail';
+  if ($field == 'on_hold') {
+    $text = ts('Email on hold - generally due to bouncing.');
+    return <<<HEREDOC
+<span class="privacy-flag email-hold" title="$text"><i class="crm-i fa-exclamation-triangle fa-lg font-red" aria-hidden="true"></i></span><span class="sr-only">$text</span>
+HEREDOC;
+  }
+  $class = str_replace('_', '-', $field);
+  $text = ts('Privacy flag: %1', [1 => $titles[$field]]);
+  return <<<HEREDOC
+<span class="fa-stack privacy-flag $class" title="$text" aria-hidden="true"><i class="crm-i {$icons[$field]} fa-stack-1x"></i><i class="crm-i fa-ban fa-stack-2x font-red"></i></span><span class="sr-only">$text</span>
+HEREDOC;
+}
diff --git a/civicrm/CRM/Core/TemporaryErrorScope.php b/civicrm/CRM/Core/TemporaryErrorScope.php
index d8853b8ef8cdff4b6ea0802a32e832b9d2d630d9..ab0cf4b3f800fed1bcbab1e5a1d246283c277cdd 100644
--- a/civicrm/CRM/Core/TemporaryErrorScope.php
+++ b/civicrm/CRM/Core/TemporaryErrorScope.php
@@ -14,9 +14,9 @@
  *
  * To ensure that they throw exceptions, use:
  *
- * @code
+ * ```
  * $errorScope = CRM_Core_TemporaryErrorScope::useException();
- * @endcode
+ * ```
  *
  * Note that relying on this is a code-smell: it can be
  * safe to temporarily switch to exception
diff --git a/civicrm/CRM/Core/Transaction.php b/civicrm/CRM/Core/Transaction.php
index 04be629fb36f7ce17b522544cc93b79f8f1cf7fd..a2297bee1f2db42c7164fe64ffc046bc5da6d340 100644
--- a/civicrm/CRM/Core/Transaction.php
+++ b/civicrm/CRM/Core/Transaction.php
@@ -28,7 +28,7 @@
  *
  * Examples:
  *
- * @code
+ * ```
  * // Some business logic using the helper functions
  * function my_business_logic() {
  *   CRM_Core_Transaction::create()->run(function($tx) {
@@ -60,7 +60,7 @@
  *   }
  * }
  *
- * @endcode
+ * ```
  *
  * Note: As of 4.6, the transaction manager supports both reference-counting and nested
  * transactions (SAVEPOINTs). In the past, it only supported reference-counting. The two cases
diff --git a/civicrm/CRM/Core/xml/Menu/Admin.xml b/civicrm/CRM/Core/xml/Menu/Admin.xml
index c68150360683c54a604ea2ac806aee24a32c69d6..acf56251f115fccd4a34b6bf21dc4f5129d96b32 100644
--- a/civicrm/CRM/Core/xml/Menu/Admin.xml
+++ b/civicrm/CRM/Core/xml/Menu/Admin.xml
@@ -7,7 +7,6 @@
      <desc>Configure custom fields to collect and store custom data which is not included in the standard CiviCRM forms.</desc>
      <page_callback>CRM_Custom_Page_Group</page_callback>
      <adminGroup>Customize Data and Screens</adminGroup>
-     <icon>admin/small/custm_data.png</icon>
      <weight>10</weight>
   </item>
   <item>
@@ -48,7 +47,6 @@
      <desc>Profiles allow you to aggregate groups of fields and include them in your site as input forms, contact display pages, and search and listings features.</desc>
      <page_callback>CRM_UF_Page_Group</page_callback>
      <adminGroup>Customize Data and Screens</adminGroup>
-     <icon>admin/small/Profile.png</icon>
      <weight>20</weight>
   </item>
   <item>
@@ -94,7 +92,6 @@
      <desc>CiviCRM has several built-in activity types (meetings, phone calls, emails sent). Track other types of interactions by creating custom activity types here.</desc>
      <page_callback>CRM_Admin_Page_Options</page_callback>
      <adminGroup>Customize Data and Screens</adminGroup>
-     <icon>admin/small/05.png</icon>
      <weight>30</weight>
   </item>
   <item>
@@ -103,7 +100,6 @@
      <desc>Contacts can be linked to each other through Relationships (e.g. Spouse, Employer, etc.). Define the types of relationships you want to record here.</desc>
      <page_callback>CRM_Admin_Page_RelationshipType</page_callback>
      <adminGroup>Customize Data and Screens</adminGroup>
-     <icon>admin/small/rela_type.png</icon>
      <weight>35</weight>
   </item>
   <item>
@@ -111,7 +107,6 @@
      <title>Contact Types</title>
      <page_callback>CRM_Admin_Page_ContactType</page_callback>
      <adminGroup>Customize Data and Screens</adminGroup>
-     <icon>admin/small/09.png</icon>
      <weight>40</weight>
   </item>
   <item>
@@ -120,7 +115,6 @@
      <desc>Options for assigning gender to individual contacts (e.g. Male, Female, Other).</desc>
      <page_callback>CRM_Admin_Page_Options</page_callback>
      <adminGroup>Customize Data and Screens</adminGroup>
-     <icon>admin/small/01.png</icon>
      <weight>45</weight>
   </item>
   <item>
@@ -129,7 +123,6 @@
      <desc>Options for individual contact prefixes (e.g. Ms., Mr., Dr. etc.).</desc>
      <page_callback>CRM_Admin_Page_Options</page_callback>
      <adminGroup>Customize Data and Screens</adminGroup>
-     <icon>admin/small/title.png</icon>
      <weight>50</weight>
   </item>
   <item>
@@ -138,7 +131,6 @@
      <desc>Options for individual contact suffixes (e.g. Jr., Sr. etc.).</desc>
      <page_callback>CRM_Admin_Page_Options</page_callback>
      <adminGroup>Customize Data and Screens</adminGroup>
-     <icon>admin/small/10.png</icon>
      <weight>55</weight>
   </item>
   <item>
@@ -147,7 +139,6 @@
      <desc>Options for categorizing contact addresses and phone numbers (e.g. Home, Work, Billing, etc.).</desc>
      <page_callback>CRM_Admin_Page_LocationType</page_callback>
      <adminGroup>Customize Data and Screens</adminGroup>
-     <icon>admin/small/13.png</icon>
      <weight>60</weight>
   </item>
   <item>
@@ -164,7 +155,6 @@
      <desc>List of IM services which can be used when recording screen-names for contacts.</desc>
      <page_callback>CRM_Admin_Page_Options</page_callback>
      <adminGroup>Customize Data and Screens</adminGroup>
-     <icon>admin/small/07.png</icon>
      <weight>70</weight>
   </item>
   <item>
@@ -173,7 +163,6 @@
      <desc>List of mobile phone providers which can be assigned when recording contact phone numbers.</desc>
      <page_callback>CRM_Admin_Page_Options</page_callback>
      <adminGroup>Customize Data and Screens</adminGroup>
-     <icon>admin/small/08.png</icon>
      <weight>75</weight>
   </item>
   <item>
@@ -183,7 +172,6 @@
     Mobile, Fax, Pager)</desc>
     <page_callback>CRM_Admin_Page_Options</page_callback>
     <adminGroup>Customize Data and Screens</adminGroup>
-    <icon>tel.gif</icon>
     <weight>80</weight>
   </item>
   <item>
@@ -191,7 +179,6 @@
      <title>Display Preferences</title>
      <page_callback>CRM_Admin_Form_Preferences_Display</page_callback>
      <adminGroup>Customize Data and Screens</adminGroup>
-     <icon>admin/small/36.png</icon>
      <weight>90</weight>
   </item>
   <item>
@@ -199,7 +186,6 @@
      <title>Search Preferences</title>
      <page_callback>CRM_Admin_Form_Setting_Search</page_callback>
      <adminGroup>Customize Data and Screens</adminGroup>
-     <icon>admin/small/36.png</icon>
      <weight>95</weight>
   </item>
   <item>
@@ -207,7 +193,6 @@
      <title>Date Preferences</title>
      <page_callback>CRM_Admin_Form_PreferencesDate</page_callback>
      <adminGroup>Customize Data and Screens</adminGroup>
-     <icon>admin/small/36.png</icon>
      <weight>97</weight>
   </item>
   <item>
@@ -216,7 +201,6 @@
      <desc>Add or remove menu items, and modify the order of items on the navigation menu.</desc>
      <page_callback>CRM_Admin_Page_Navigation</page_callback>
      <adminGroup>Customize Data and Screens</adminGroup>
-     <icon>admin/small/template.png</icon>
      <weight>100</weight>
   </item>
   <item>
@@ -233,7 +217,6 @@
      <desc>Developers and accidental techies with a bit of PHP and SQL knowledge can create new search forms to handle specific search and reporting needs which aren't covered by the built-in Advanced Search and Search Builder features.</desc>
      <page_callback>CRM_Admin_Page_Options</page_callback>
      <adminGroup>Customize Data and Screens</adminGroup>
-     <icon>admin/small/template.png</icon>
      <weight>110</weight>
   </item>
   <item>
@@ -243,7 +226,6 @@
      <path_arguments>action=update</path_arguments>
      <page_callback>CRM_Contact_Form_Domain</page_callback>
      <adminGroup>Communications</adminGroup>
-     <icon>admin/small/domain.png</icon>
      <weight>10</weight>
   </item>
   <item>
@@ -252,7 +234,6 @@
      <desc>List of Email Addresses which can be used when sending emails to contacts.</desc>
      <page_callback>CRM_Admin_Page_Options</page_callback>
      <adminGroup>Communications</adminGroup>
-     <icon>admin/small/title.png</icon>
      <weight>20</weight>
   </item>
   <item>
@@ -261,7 +242,6 @@
      <desc>Message templates allow you to save and re-use messages with layouts which you can use when sending email to one or more contacts.</desc>
      <page_callback>CRM_Admin_Page_MessageTemplates</page_callback>
      <adminGroup>Communications</adminGroup>
-     <icon>admin/small/template.png</icon>
      <access_arguments>edit message templates;edit user-driven message templates;edit system workflow message templates</access_arguments>
      <weight>30</weight>
   </item>
@@ -281,7 +261,6 @@
      <access_callback>1</access_callback>
      <access_arguments>administer CiviCRM;edit all events</access_arguments>
      <adminGroup>Communications</adminGroup>
-     <icon>admin/small/template.png</icon>
      <weight>40</weight>
   </item>
   <item>
@@ -294,7 +273,6 @@
      <desc>One or more preferred methods of communication can be assigned to each contact. Customize the available options here.</desc>
      <page_callback>CRM_Admin_Page_Options</page_callback>
      <adminGroup>Communications</adminGroup>
-     <icon>admin/small/communication.png</icon>
      <weight>50</weight>
   </item>
   <item>
@@ -303,7 +281,6 @@
      <desc>Configure Label Formats that are used when creating mailing labels.</desc>
      <page_callback>CRM_Admin_Page_LabelFormats</page_callback>
      <adminGroup>Communications</adminGroup>
-     <icon>admin/small/template.png</icon>
      <weight>60</weight>
   </item>
   <item>
@@ -312,7 +289,6 @@
      <desc>Configure PDF Page Formats that can be assigned to Message Templates when creating PDF letters.</desc>
      <page_callback>CRM_Admin_Page_PdfFormats</page_callback>
      <adminGroup>Communications</adminGroup>
-     <icon>admin/small/template.png</icon>
      <weight>70</weight>
   </item>
   <item>
@@ -321,7 +297,6 @@
      <desc>Options for Communication Style selection.</desc>
      <page_callback>CRM_Admin_Page_Options</page_callback>
      <adminGroup>Communications</adminGroup>
-     <icon>admin/small/01.png</icon>
      <weight>75</weight>
   </item>
   <item>
@@ -330,7 +305,6 @@
      <desc>Options for assigning email greetings to individual and household contacts.</desc>
      <page_callback>CRM_Admin_Page_Options</page_callback>
      <adminGroup>Communications</adminGroup>
-     <icon>admin/small/01.png</icon>
      <weight>80</weight>
   </item>
   <item>
@@ -339,7 +313,6 @@
      <desc>Options for assigning postal greetings to individual and household contacts.</desc>
      <page_callback>CRM_Admin_Page_Options</page_callback>
      <adminGroup>Communications</adminGroup>
-     <icon>admin/small/01.png</icon>
      <weight>90</weight>
   </item>
   <item>
@@ -348,7 +321,6 @@
      <desc>Options for assigning addressee to individual, household and organization contacts.</desc>
      <page_callback>CRM_Admin_Page_Options</page_callback>
      <adminGroup>Communications</adminGroup>
-     <icon>admin/small/01.png</icon>
      <weight>100</weight>
   </item>
   <item>
@@ -356,7 +328,6 @@
      <title>Languages, Currency, Locations</title>
      <page_callback>CRM_Admin_Form_Setting_Localization</page_callback>
      <adminGroup>Localization</adminGroup>
-     <icon>admin/small/36.png</icon>
      <weight>10</weight>
   </item>
   <item>
@@ -364,7 +335,6 @@
      <title>Address Settings</title>
      <page_callback>CRM_Admin_Form_Preferences_Address</page_callback>
      <adminGroup>Localization</adminGroup>
-     <icon>admin/small/36.png</icon>
      <weight>20</weight>
   </item>
   <item>
@@ -372,7 +342,6 @@
      <title>Date Formats</title>
      <page_callback>CRM_Admin_Form_Setting_Date</page_callback>
      <adminGroup>Localization</adminGroup>
-     <icon>admin/small/36.png</icon>
      <weight>30</weight>
   </item>
   <item>
@@ -381,7 +350,6 @@
      <desc>Options for contact languages.</desc>
      <page_callback>CRM_Admin_Page_Options</page_callback>
      <adminGroup>Localization</adminGroup>
-     <icon>admin/small/36.png</icon>
      <weight>40</weight>
   </item>
   <item>
@@ -390,7 +358,6 @@
      <desc>Grant or deny access to actions (view, edit...), features and components.</desc>
      <page_callback>CRM_Admin_Page_Access</page_callback>
      <adminGroup>Users and Permissions</adminGroup>
-     <icon>admin/small/03.png</icon>
      <weight>10</weight>
   </item>
     <item>
@@ -406,7 +373,6 @@
      <desc>Automatically create a CiviCRM contact record for each CMS user record.</desc>
      <page_callback>CRM_Admin_Form_CMSUser</page_callback>
      <adminGroup>Users and Permissions</adminGroup>
-     <icon>admin/small/Synch_user.png</icon>
      <weight>20</weight>
   </item>
   <item>
@@ -416,7 +382,6 @@
      <page_callback>CRM_Admin_Page_ConfigTaskList</page_callback>
      <return_url>civicrm/admin/configtask</return_url>
      <adminGroup>System Settings</adminGroup>
-     <icon>check.gif</icon>
      <weight>1</weight>
   </item>
   <item>
@@ -425,7 +390,6 @@
    <desc>Enable or disable components (e.g. CiviEvent, CiviMember, etc.) for your site based on the features you need. We recommend disabling any components not being used in order to simplify the user interface. You can easily re-enable components at any time from this screen.</desc>
      <page_callback>CRM_Admin_Form_Setting_Component</page_callback>
      <adminGroup>System Settings</adminGroup>
-     <icon>admin/small/36.png</icon>
      <weight>10</weight>
   </item>
   <item>
@@ -435,7 +399,6 @@
      <desc></desc>
      <access_arguments>administer CiviCRM</access_arguments>
      <adminGroup>System Settings</adminGroup>
-     <icon>admin/small/price_sets.png</icon>
      <weight>120</weight>
   </item>
   <item>
@@ -449,7 +412,6 @@
      <title>Outbound Email Settings</title>
      <page_callback>CRM_Admin_Form_Setting_Smtp</page_callback>
      <adminGroup>System Settings</adminGroup>
-     <icon>admin/small/07.png</icon>
      <weight>20</weight>
   </item>
   <item>
@@ -458,7 +420,6 @@
      <desc>Payment Processor setup for CiviCRM transactions</desc>
      <page_callback>CRM_Admin_Page_PaymentProcessor</page_callback>
      <adminGroup>System Settings</adminGroup>
-     <icon>admin/small/online_contribution_pages.png</icon>
      <weight>30</weight>
      <access_arguments>administer payment processors</access_arguments>
   </item>
@@ -467,7 +428,6 @@
      <title>Mapping and Geocoding</title>
      <page_callback>CRM_Admin_Form_Setting_Mapping</page_callback>
      <adminGroup>System Settings</adminGroup>
-     <icon>admin/small/36.png</icon>
      <weight>40</weight>
   </item>
   <item>
@@ -476,7 +436,6 @@
      <desc>Enable undelete/move to trash feature, detailed change logging, ReCAPTCHA to protect forms.</desc>
      <page_callback>CRM_Admin_Form_Setting_Miscellaneous</page_callback>
      <adminGroup>System Settings</adminGroup>
-     <icon>admin/small/36.png</icon>
      <weight>50</weight>
   </item>
   <item>
@@ -484,7 +443,6 @@
      <title>Directories</title>
      <page_callback>CRM_Admin_Form_Setting_Path</page_callback>
      <adminGroup>System Settings</adminGroup>
-     <icon>admin/small/36.png</icon>
      <weight>60</weight>
   </item>
   <item>
@@ -492,7 +450,6 @@
      <title>Resource URLs</title>
      <page_callback>CRM_Admin_Form_Setting_Url</page_callback>
      <adminGroup>System Settings</adminGroup>
-     <icon>admin/small/36.png</icon>
      <weight>70</weight>
   </item>
   <item>
@@ -501,7 +458,6 @@
      <desc>Reset the Base Directory Path and Base URL settings - generally when a CiviCRM site is moved to another location in the file system and/or to another domain.</desc>
      <page_callback>CRM_Admin_Form_Setting_UpdateConfigBackend</page_callback>
      <adminGroup>System Settings</adminGroup>
-     <icon>admin/small/updatepath.png</icon>
      <weight>80</weight>
   </item>
   <item>
@@ -509,7 +465,6 @@
      <title>CMS Database Integration</title>
      <page_callback>CRM_Admin_Form_Setting_UF</page_callback>
      <adminGroup>System Settings</adminGroup>
-     <icon>admin/small/36.png</icon>
      <weight>90</weight>
   </item>
   <item>
@@ -518,7 +473,6 @@
      <desc>File Extensions that can be considered safe.</desc>
      <page_callback>CRM_Admin_Page_Options</page_callback>
      <adminGroup>System Settings</adminGroup>
-     <icon>admin/small/01.png</icon>
      <weight>100</weight>
   </item>
   <item>
@@ -527,7 +481,6 @@
      <desc>Access all meta-data option groups.</desc>
      <page_callback>CRM_Admin_Page_Options</page_callback>
      <adminGroup>System Settings</adminGroup>
-     <icon>admin/small/01.png</icon>
      <weight>105</weight>
   </item>
   <item>
@@ -536,7 +489,6 @@
      <desc>Import and Export mappings allow you to easily run the same job multiple times. This option allows you to rename or delete existing mappings.</desc>
      <page_callback>CRM_Admin_Page_Mapping</page_callback>
      <adminGroup>System Settings</adminGroup>
-     <icon>admin/small/import_export_map.png</icon>
      <weight>110</weight>
   </item>
   <item>
@@ -544,7 +496,6 @@
      <title>Debugging</title>
      <page_callback>CRM_Admin_Form_Setting_Debugging</page_callback>
      <adminGroup>System Settings</adminGroup>
-     <icon>admin/small/36.png</icon>
      <weight>120</weight>
   </item>
   <item>
@@ -552,7 +503,6 @@
      <title>Multi Site Settings</title>
      <page_callback>CRM_Admin_Form_Generic</page_callback>
      <adminGroup>System Settings</adminGroup>
-     <icon>admin/small/36.png</icon>
      <weight>130</weight>
   </item>
   <item>
@@ -594,7 +544,6 @@
      <page_callback>CRM_Admin_Page_Job</page_callback>
      <access_arguments>access CiviCRM,administer CiviCRM</access_arguments>
      <adminGroup>System Settings</adminGroup>
-     <icon>admin/small/13.png</icon>
      <weight>1370</weight>
   </item>
   <item>
@@ -604,7 +553,6 @@
      <page_callback>CRM_Admin_Page_JobLog</page_callback>
      <access_arguments>access CiviCRM,administer CiviCRM</access_arguments>
      <adminGroup>Manage</adminGroup>
-     <icon>admin/small/13.png</icon>
      <weight>1380</weight>
   </item>
   <item>
@@ -613,7 +561,6 @@
      <desc>List of types which can be assigned to Grants. (Enable CiviGrant from Administer > Systme Settings > Enable Components if you want to track grants.)</desc>
      <page_callback>CRM_Admin_Page_Options</page_callback>
      <adminGroup>Option Lists</adminGroup>
-     <icon>admin/small/grant_type.png</icon>
      <weight>385</weight>
   </item>
   <item>
@@ -655,7 +602,6 @@
      <desc>Price sets allow you to offer multiple options with associated fees (e.g. pre-conference workshops, additional meals, etc.). Configure Price Sets for events which need more than a single set of fee levels.</desc>
      <access_arguments>access CiviCRM,access CiviEvent</access_arguments>
      <adminGroup>Customize</adminGroup>
-     <icon>admin/small/price_sets.png</icon>
      <weight>380</weight>
   </item>
   <item>
@@ -694,7 +640,6 @@
      <page_callback>CRM_SMS_Page_Provider</page_callback>
      <access_arguments>administer CiviCRM</access_arguments>
      <adminGroup>System Settings</adminGroup>
-     <icon>admin/small/36.png</icon>
      <weight>500</weight>
   </item>
     <item>
diff --git a/civicrm/CRM/Core/xml/Menu/Contact.xml b/civicrm/CRM/Core/xml/Menu/Contact.xml
index 26bf51d30c0fa5c619ef270daa1c44e91a70a689..e49b57236dda52787551c60eb90b626bdb330f2f 100644
--- a/civicrm/CRM/Core/xml/Menu/Contact.xml
+++ b/civicrm/CRM/Core/xml/Menu/Contact.xml
@@ -343,7 +343,6 @@
    <page_callback>CRM_Contact_Page_DedupeRules</page_callback>
    <access_arguments>administer dedupe rules;merge duplicate contacts</access_arguments>
    <adminGroup>Manage</adminGroup>
-   <icon>admin/small/duplicate_matching.png</icon>
    <weight>105</weight>
 </item>
 <item>
diff --git a/civicrm/CRM/Core/xml/Menu/Tag.xml b/civicrm/CRM/Core/xml/Menu/Tag.xml
index e05e6eed8b4a21a517d62f2b51656e9b9bb2e46d..aa975c3aba446c4bd2712385d5880d6dafb1abaa 100644
--- a/civicrm/CRM/Core/xml/Menu/Tag.xml
+++ b/civicrm/CRM/Core/xml/Menu/Tag.xml
@@ -8,7 +8,6 @@
     <page_callback>CRM_Tag_Page_Tag</page_callback>
     <access_arguments>administer CiviCRM;manage tags</access_arguments>
     <adminGroup>Customize Data and Screens</adminGroup>
-    <icon>admin/small/11.png</icon>
     <weight>25</weight>
   </item>
   <item>
diff --git a/civicrm/CRM/Custom/Form/Field.php b/civicrm/CRM/Custom/Form/Field.php
index 4f5625d81a9252f3aade4ab2a7013113d82b0381..e61d23ed803ce27ae2468052c1eb234dfd82add6 100644
--- a/civicrm/CRM/Custom/Form/Field.php
+++ b/civicrm/CRM/Custom/Form/Field.php
@@ -76,7 +76,6 @@ class CRM_Custom_Form_Field extends CRM_Core_Form {
       'Select' => 'Select',
       'Radio' => 'Radio',
       'CheckBox' => 'CheckBox',
-      'Multi-Select' => 'Multi-Select',
       'Autocomplete-Select' => 'Autocomplete-Select',
     ],
     ['Text' => 'Text', 'Select' => 'Select', 'Radio' => 'Radio'],
@@ -85,8 +84,8 @@ class CRM_Custom_Form_Field extends CRM_Core_Form {
     ['TextArea' => 'TextArea', 'RichTextEditor' => 'RichTextEditor'],
     ['Date' => 'Select Date'],
     ['Radio' => 'Radio'],
-    ['StateProvince' => 'Select State/Province', 'Multi-Select' => 'Multi-Select State/Province'],
-    ['Country' => 'Select Country', 'Multi-Select' => 'Multi-Select Country'],
+    ['StateProvince' => 'Select State/Province'],
+    ['Country' => 'Select Country'],
     ['File' => 'File'],
     ['Link' => 'Link'],
     ['ContactReference' => 'Autocomplete-Select'],
@@ -141,7 +140,6 @@ class CRM_Custom_Form_Field extends CRM_Core_Form {
           'Select' => ts('Select'),
           'Radio' => ts('Radio'),
           'CheckBox' => ts('CheckBox'),
-          'Multi-Select' => ts('Multi-Select'),
           'Autocomplete-Select' => ts('Autocomplete-Select'),
         ],
         [
@@ -162,8 +160,8 @@ class CRM_Custom_Form_Field extends CRM_Core_Form {
         ['TextArea' => ts('TextArea'), 'RichTextEditor' => ts('Rich Text Editor')],
         ['Date' => ts('Select Date')],
         ['Radio' => ts('Radio')],
-        ['StateProvince' => ts('Select State/Province'), 'Multi-Select' => ts('Multi-Select State/Province')],
-        ['Country' => ts('Select Country'), 'Multi-Select' => ts('Multi-Select Country')],
+        ['StateProvince' => ts('Select State/Province')],
+        ['Country' => ts('Select Country')],
         ['File' => ts('Select File')],
         ['Link' => ts('Link')],
         ['ContactReference' => ts('Autocomplete-Select')],
@@ -329,6 +327,8 @@ class CRM_Custom_Form_Field extends CRM_Core_Form {
       'return' => ['title'],
     ];
 
+    $this->add('checkbox', 'serialize', ts('Multi-Select'));
+
     if ($this->_action == CRM_Core_Action::UPDATE) {
       $this->freeze('data_type');
       if (!empty($this->_values['option_group_id'])) {
@@ -727,7 +727,7 @@ SELECT count(*)
     if (isset($fields['data_type'][1])) {
       $dataField = $fields['data_type'][1];
     }
-    $optionFields = ['Select', 'Multi-Select', 'CheckBox', 'Radio'];
+    $optionFields = ['Select', 'CheckBox', 'Radio'];
 
     if (isset($fields['option_type']) && $fields['option_type'] == 1) {
       //capture duplicate Custom option values
@@ -950,6 +950,17 @@ AND    option_group_id = %2";
       $params['is_search_range'] = 0;
     }
 
+    // Serialization cannot be changed on update
+    if ($this->_id) {
+      unset($params['serialize']);
+    }
+    elseif (strpos($params['html_type'], 'Select') === 0) {
+      $params['serialize'] = $params['serialize'] ? CRM_Core_DAO::SERIALIZE_SEPARATOR_BOOKEND : 'null';
+    }
+    else {
+      $params['serialize'] = $params['html_type'] == 'CheckBox' ? CRM_Core_DAO::SERIALIZE_SEPARATOR_BOOKEND : 'null';
+    }
+
     $filter = 'null';
     if ($dataTypeKey == 11 && !empty($params['filter_selected'])) {
       if ($params['filter_selected'] == 'Advance' && trim(CRM_Utils_Array::value('filter', $params))) {
diff --git a/civicrm/CRM/Dedupe/MergeHandler.php b/civicrm/CRM/Dedupe/MergeHandler.php
index db454c0e70fc1a31aa2ad0e8a02f952b0dea5619..e48352cfd7e27eacfdfc2cf1db87aa3ea885b545 100644
--- a/civicrm/CRM/Dedupe/MergeHandler.php
+++ b/civicrm/CRM/Dedupe/MergeHandler.php
@@ -123,7 +123,7 @@ class CRM_Dedupe_MergeHandler {
   public function getTablesDynamicallyRelatedToContactTable() {
     if (!isset(\Civi::$statics[__CLASS__]['dynamic'])) {
       \Civi::$statics[__CLASS__]['dynamic'] = [];
-      foreach (CRM_Core_DAO::getDynamicReferencesToTable('civicrm_contact') as $tableName => $field) {
+      foreach (CRM_Core_DAO::getDynamicReferencesToTable('civicrm_contact') as $tableName => $fields) {
         if ($tableName === 'civicrm_financial_item') {
           // It turns out that civicrm_financial_item does not have an index on entity_table (only as the latter
           // part of a entity_id/entity_table index which probably is not adding any value over & above entity_id
@@ -131,7 +131,9 @@ class CRM_Dedupe_MergeHandler {
           // values for entity_table in the schema.
           continue;
         }
-        $sql[] = "(SELECT '$tableName' as civicrm_table, '{$field[0]}' as field_name FROM $tableName WHERE entity_table = 'civicrm_contact' LIMIT 1)";
+        foreach ($fields as $field) {
+          $sql[] = "(SELECT '$tableName' as civicrm_table, '{$field[0]}' as field_name FROM $tableName WHERE {$field[1]} = 'civicrm_contact' LIMIT 1)";
+        }
       }
       $sqlString = implode(' UNION ', $sql);
       if ($sqlString) {
diff --git a/civicrm/CRM/Dedupe/Merger.php b/civicrm/CRM/Dedupe/Merger.php
index c19ed52f2add10bc34337bcd4e8a4172f38347e6..9f161af164195a45cd59c646e6a252c18115d756 100644
--- a/civicrm/CRM/Dedupe/Merger.php
+++ b/civicrm/CRM/Dedupe/Merger.php
@@ -492,6 +492,7 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
     $otherId = $mergeHandler->getToRemoveID();
     $cidRefs = self::cidRefs();
     $eidRefs = $mergeHandler->getTablesDynamicallyRelatedToContactTable();
+    $dynamicRefs = CRM_Core_DAO::getDynamicReferencesToTable('civicrm_contact');
     $cpTables = self::cpTables();
     $paymentTables = self::paymentTables();
     self::filterRowBasedCustomDataFromCustomTables($cidRefs);
@@ -543,14 +544,15 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
 
           $preOperationSqls = self::operationSql($mainId, $otherId, $table, $tableOperations);
           $sqls = array_merge($sqls, $preOperationSqls);
-          $sqls[] = "UPDATE IGNORE $table SET $field = $mainId WHERE $field = $otherId";
-          $sqls[] = "DELETE FROM $table WHERE $field = $otherId";
+          $sqls[] = "UPDATE $table SET $field = $mainId WHERE $field = $otherId";
         }
       }
 
       if (isset($eidRefs[$table])) {
-        $sqls[] = "UPDATE IGNORE $table SET {$eidRefs[$table]}= $mainId WHERE {$eidRefs[$table]} = $otherId AND entity_table = 'civicrm_contact'";
-        $sqls[] = "DELETE FROM $table WHERE {$eidRefs[$table]} = $otherId AND entity_table = 'civicrm_contact'";
+        foreach ($dynamicRefs[$table] as $dynamicRef) {
+          $sqls[] = "UPDATE IGNORE $table SET {$dynamicRef[0]}= $mainId WHERE {$dynamicRef[0]} = $otherId AND {$dynamicRef[1]} = 'civicrm_contact'";
+          $sqls[] = "DELETE FROM $table WHERE {$dynamicRef[0]} = $otherId AND {$dynamicRef[1]} = 'civicrm_contact'";
+        }
       }
     }
 
@@ -1918,90 +1920,72 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
         return [$cFields, $submitted];
       }
       $htmlType = $cFields[$fid]['attributes']['html_type'];
-      switch ($htmlType) {
-        case 'File':
-          // Handled in CustomField->move(). Tested in testMergeCustomFields.
-          unset($submitted["custom_$fid"]);
-          break;
-
-        case 'Select Country':
-          // @todo Test in testMergeCustomFields disabled as this does not work, Handle in CustomField->move().
-        case 'Select State/Province':
-          $submitted[$key] = CRM_Core_BAO_CustomField::displayValue($value, $fid);
-          break;
-
-        case 'Select Date':
-          if ($cFields[$fid]['attributes']['is_view']) {
-            $submitted[$key] = date('YmdHis', strtotime($submitted[$key]));
-          }
-          break;
+      $isSerialized = CRM_Core_BAO_CustomField::isSerialized($cFields[$fid]['attributes']);
 
-        case 'CheckBox':
-        case 'Multi-Select':
-        case 'Multi-Select Country':
-        case 'Multi-Select State/Province':
-          // Merge values from both contacts for multivalue fields, CRM-4385
-          // get the existing custom values from db.
-          $customParams = ['entityID' => $mainId, $key => TRUE];
-          $customfieldValues = CRM_Core_BAO_CustomValueTable::getValues($customParams);
-          if (!empty($customfieldValues[$key])) {
-            $existingValue = explode(CRM_Core_DAO::VALUE_SEPARATOR, $customfieldValues[$key]);
-            if (is_array($existingValue) && !empty($existingValue)) {
-              $mergeValue = $submittedCustomFields = [];
-              if ($value == 'null') {
-                // CRM-19074 if someone has deliberately chosen to overwrite with 'null', respect it.
-                $submitted[$key] = $value;
+      if ($htmlType === 'File') {
+        // Handled in CustomField->move(). Tested in testMergeCustomFields.
+        unset($submitted["custom_$fid"]);
+      }
+      elseif (!$isSerialized && ($htmlType === 'Select Country' || $htmlType === 'Select State/Province')) {
+        // @todo Test in testMergeCustomFields disabled as this does not work, Handle in CustomField->move().
+        $submitted[$key] = CRM_Core_BAO_CustomField::displayValue($value, $fid);
+      }
+      elseif ($htmlType === 'Select Date') {
+        if ($cFields[$fid]['attributes']['is_view']) {
+          $submitted[$key] = date('YmdHis', strtotime($submitted[$key]));
+        }
+      }
+      elseif ($isSerialized) {
+        // Merge values from both contacts for multivalue fields, CRM-4385
+        // get the existing custom values from db.
+        $customParams = ['entityID' => $mainId, $key => TRUE];
+        $customfieldValues = CRM_Core_BAO_CustomValueTable::getValues($customParams);
+        if (!empty($customfieldValues[$key])) {
+          $existingValue = explode(CRM_Core_DAO::VALUE_SEPARATOR, $customfieldValues[$key]);
+          if (is_array($existingValue) && !empty($existingValue)) {
+            $mergeValue = $submittedCustomFields = [];
+            if ($value == 'null') {
+              // CRM-19074 if someone has deliberately chosen to overwrite with 'null', respect it.
+              $submitted[$key] = $value;
+            }
+            else {
+              if ($value) {
+                $submittedCustomFields = explode(CRM_Core_DAO::VALUE_SEPARATOR, $value);
               }
-              else {
-                if ($value) {
-                  $submittedCustomFields = explode(CRM_Core_DAO::VALUE_SEPARATOR, $value);
-                }
 
-                // CRM-19653: overwrite or add the existing custom field value with dupicate contact's
-                // custom field value stored at $submittedCustomValue.
-                foreach ($submittedCustomFields as $k => $v) {
-                  if ($v != '' && !in_array($v, $mergeValue)) {
-                    $mergeValue[] = $v;
-                  }
+              // CRM-19653: overwrite or add the existing custom field value with dupicate contact's
+              // custom field value stored at $submittedCustomValue.
+              foreach ($submittedCustomFields as $k => $v) {
+                if ($v != '' && !in_array($v, $mergeValue)) {
+                  $mergeValue[] = $v;
                 }
+              }
 
-                //keep state and country as array format.
-                //for checkbox and m-select format w/ VALUE_SEPARATOR
-                if (in_array($htmlType, [
-                  'CheckBox',
-                  'Multi-Select',
-                ])) {
-                  $submitted[$key] = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR,
-                      $mergeValue
-                    ) . CRM_Core_DAO::VALUE_SEPARATOR;
-                }
-                else {
-                  $submitted[$key] = $mergeValue;
-                }
+              //keep state and country as array format.
+              //for checkbox and m-select format w/ VALUE_SEPARATOR
+              if (in_array($htmlType, ['CheckBox', 'Select'])) {
+                $submitted[$key] = CRM_Utils_Array::implodePadded($mergeValue);
+              }
+              else {
+                $submitted[$key] = $mergeValue;
               }
             }
           }
-          elseif (in_array($htmlType, [
-            'Multi-Select Country',
-            'Multi-Select State/Province',
-          ])) {
-            //we require submitted values should be in array format
-            if ($value) {
-              $mergeValueArray = explode(CRM_Core_DAO::VALUE_SEPARATOR, $value);
-              //hack to remove null values from array.
-              $mergeValue = [];
-              foreach ($mergeValueArray as $k => $v) {
-                if ($v != '') {
-                  $mergeValue[] = $v;
-                }
+        }
+        elseif (in_array($htmlType, ['Select Country', 'Select State/Province'])) {
+          //we require submitted values should be in array format
+          if ($value) {
+            $mergeValueArray = explode(CRM_Core_DAO::VALUE_SEPARATOR, $value);
+            //hack to remove null values from array.
+            $mergeValue = [];
+            foreach ($mergeValueArray as $k => $v) {
+              if ($v != '') {
+                $mergeValue[] = $v;
               }
-              $submitted[$key] = $mergeValue;
             }
+            $submitted[$key] = $mergeValue;
           }
-          break;
-
-        default:
-          break;
+        }
       }
     }
     return [$cFields, $submitted];
diff --git a/civicrm/CRM/Event/BAO/Event.php b/civicrm/CRM/Event/BAO/Event.php
index 1e178acae2a7ccb8b55829300d402d65ec06fa90..794e5d14e5a4b00c469db67c7c42805fea015a8b 100644
--- a/civicrm/CRM/Event/BAO/Event.php
+++ b/civicrm/CRM/Event/BAO/Event.php
@@ -846,10 +846,6 @@ WHERE civicrm_event.is_active = 1
     // be clearer & safer here
     $permissions = CRM_Core_Permission::event(CRM_Core_Permission::VIEW);
 
-    // check if we're in shopping cart mode for events
-    $enable_cart = Civi::settings()->get('enable_cart');
-    if ($enable_cart) {
-    }
     while ($dao->fetch()) {
       if (!empty($permissions) && in_array($dao->event_id, $permissions)) {
         $info = [];
@@ -891,7 +887,9 @@ WHERE civicrm_event.is_active = 1
         $info['location'] = $address;
         $info['url'] = CRM_Utils_System::url('civicrm/event/info', 'reset=1&id=' . $dao->event_id, TRUE, NULL, FALSE);
 
-        if ($enable_cart) {
+        // @todo Move to eventcart extension
+        // check if we're in shopping cart mode for events
+        if ((bool) Civi::settings()->get('enable_cart')) {
           $reg = CRM_Event_Cart_BAO_EventInCart::get_registration_link($dao->event_id);
           $info['registration_link'] = CRM_Utils_System::url($reg['path'], $reg['query'], TRUE);
           $info['registration_link_text'] = $reg['label'];
@@ -2387,4 +2385,45 @@ LEFT  JOIN  civicrm_price_field_value value ON ( value.id = lineItem.price_field
     ];
   }
 
+  /**
+   * Get the appropriate links to iCal pages/feeds.
+   *
+   * @param int $eventId
+   *
+   * @return array
+   *   All of the icons to show.
+   */
+  public static function getICalLinks($eventId = NULL) {
+    $return = $eventId ? [] : [
+      [
+        'url' => CRM_Utils_System::url('civicrm/event/ical', 'reset=1&list=1&html=1', TRUE, NULL, TRUE),
+        'text' => ts('HTML listing of current and future public events.'),
+        'icon' => 'fa-th-list',
+      ],
+      [
+        'url' => CRM_Utils_System::url('civicrm/event/ical', 'reset=1&list=1&rss=1', TRUE, NULL, TRUE),
+        'text' => ts('Get RSS 2.0 feed for current and future public events.'),
+        'icon' => 'fa-rss',
+      ],
+    ];
+    $query = [
+      'reset' => 1,
+    ];
+    if ($eventId) {
+      $query['id'] = $eventId;
+    }
+    $return[] = [
+      'url' => CRM_Utils_System::url('civicrm/event/ical', $query, TRUE, NULL, TRUE),
+      'text' => $eventId ? ts('Download iCalendar entry for this event.') : ts('Download iCalendar entry for current and future public events.'),
+      'icon' => 'fa-download',
+    ];
+    $query['list'] = 1;
+    $return[] = [
+      'url' => CRM_Utils_System::url('civicrm/event/ical', $query, TRUE, NULL, TRUE),
+      'text' => $eventId ? ts('iCalendar feed for this event.') : ts('iCalendar feed for current and future public events.'),
+      'icon' => 'fa-link',
+    ];
+    return $return;
+  }
+
 }
diff --git a/civicrm/CRM/Event/Cart/BAO/MerParticipant.php b/civicrm/CRM/Event/Cart/BAO/MerParticipant.php
index 553ab9578b08a158326aa0129fc2d2e8f6f343d0..4cdf4e341b9c90d17b4ac33c3bb9c26b7ca0e260 100644
--- a/civicrm/CRM/Event/Cart/BAO/MerParticipant.php
+++ b/civicrm/CRM/Event/Cart/BAO/MerParticipant.php
@@ -38,7 +38,7 @@ class CRM_Event_Cart_BAO_MerParticipant extends CRM_Event_BAO_Participant {
     $a = (array) $participant;
     $this->copyValues($a);
 
-    $this->email = $participant['email'] ?? NULL;
+    $this->email = $a['email'] ?? NULL;
   }
 
   /**
diff --git a/civicrm/CRM/Event/Controller/Registration.php b/civicrm/CRM/Event/Controller/Registration.php
index 5233eb3e80358903226b5efb37308f6c8216bde3..561d2ed8b8ef5e221a9a8a0647090f1b697cce34 100644
--- a/civicrm/CRM/Event/Controller/Registration.php
+++ b/civicrm/CRM/Event/Controller/Registration.php
@@ -10,11 +10,7 @@
  */
 
 /**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
+ * Class CRM_Event_Controller_Registration
  */
 class CRM_Event_Controller_Registration extends CRM_Core_Controller {
 
diff --git a/civicrm/CRM/Event/Controller/Search.php b/civicrm/CRM/Event/Controller/Search.php
index 8bf9a9996ebf0b5a8932559edb676fe18ec38674..927c9422fe53ed306733c892647849f89131a7f1 100644
--- a/civicrm/CRM/Event/Controller/Search.php
+++ b/civicrm/CRM/Event/Controller/Search.php
@@ -9,14 +9,6 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
 /**
  * This class is used by the Search functionality.
  *
@@ -36,6 +28,8 @@ class CRM_Event_Controller_Search extends CRM_Core_Controller {
    * @param string $title
    * @param bool|int $action
    * @param bool $modal
+   *
+   * @throws \CRM_Core_Exception
    */
   public function __construct($title = NULL, $action = CRM_Core_Action::NONE, $modal = TRUE) {
 
diff --git a/civicrm/CRM/Event/Form/ManageEvent/TabHeader.php b/civicrm/CRM/Event/Form/ManageEvent/TabHeader.php
index 890782c624000db48c27a42b30f5ab4623d25a10..6b4c83d199510f27b32804144b6a445c8cdf3d5a 100644
--- a/civicrm/CRM/Event/Form/ManageEvent/TabHeader.php
+++ b/civicrm/CRM/Event/Form/ManageEvent/TabHeader.php
@@ -86,8 +86,7 @@ class CRM_Event_Form_ManageEvent_TabHeader {
     }
 
     // check if we're in shopping cart mode for events
-    $enableCart = Civi::settings()->get('enable_cart');
-    if (!$enableCart) {
+    if (!(bool) Civi::settings()->get('enable_cart')) {
       unset($tabs['conference']);
     }
 
@@ -107,7 +106,7 @@ LEFT JOIN  civicrm_recurring_entity re ON ( e.id = re.entity_id AND re.entity_ta
 WHERE      e.id = %1
 ";
       //Check if repeat is configured
-      $eventHasParent = CRM_Core_BAO_RecurringEntity::getParentFor($eventID, 'civicrm_event');
+      CRM_Core_BAO_RecurringEntity::getParentFor($eventID, 'civicrm_event');
       $params = [
         1 => [$eventID, 'Integer'],
         2 => [$eventNameMapping->getId(), 'Integer'],
diff --git a/civicrm/CRM/Event/Form/Registration.php b/civicrm/CRM/Event/Form/Registration.php
index efa71d460556a4aeffee7128924ee669f44e61fc..8e69eb560e4f8a535aae9225061464d44bc9c021 100644
--- a/civicrm/CRM/Event/Form/Registration.php
+++ b/civicrm/CRM/Event/Form/Registration.php
@@ -199,10 +199,10 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
     $this->_isEventFull = $this->get('isEventFull');
     $this->_lineItemParticipantsCount = $this->get('lineItemParticipants');
     if (!is_array($this->_lineItem)) {
-      $this->_lineItem = array();
+      $this->_lineItem = [];
     }
     if (!is_array($this->_lineItemParticipantsCount)) {
-      $this->_lineItemParticipantsCount = array();
+      $this->_lineItemParticipantsCount = [];
     }
     $this->_availableRegistrations = $this->get('availableRegistrations');
     $this->_participantIDS = $this->get('participantIDs');
@@ -232,7 +232,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
       }
 
       // get all the values from the dao object
-      $this->_values = $this->_fields = array();
+      $this->_values = $this->_fields = [];
 
       //retrieve event information
       $params = array('id' => $this->_eventId);
@@ -519,7 +519,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
       if ($contactID) {
         //FIX CRM-9653
         if (is_array($id)) {
-          $fields = array();
+          $fields = [];
           foreach ($id as $profileID) {
             $field = CRM_Core_BAO_UFGroup::getFields($profileID, FALSE, CRM_Core_Action::ADD,
               NULL, NULL, FALSE, NULL,
@@ -643,7 +643,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
     }
     $eventFee = $form->_values['fee'] ?? NULL;
     if (!is_array($eventFee) || empty($eventFee)) {
-      $form->_values['fee'] = array();
+      $form->_values['fee'] = [];
     }
 
     //fix for non-upgraded price sets.CRM-4256.
@@ -876,7 +876,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
 
     $priceSetId = $form->get('priceSetId');
     $addParticipantNum = substr($form->_name, 12);
-    $priceSetFields = $priceSetDetails = array();
+    $priceSetFields = $priceSetDetails = [];
     $hasPriceFieldsCount = FALSE;
     if ($priceSetId) {
       $priceSetDetails = $form->get('priceSet');
@@ -975,7 +975,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
     $priceSetDetails = $form->get('priceSet');
 
     foreach ($params as $key => & $value) {
-      $vals = array();
+      $vals = [];
       if (strpos($key, 'price_') !== FALSE) {
         $fieldId = substr($key, 6);
         if (!array_key_exists($fieldId, $priceSetDetails['fields']) ||
@@ -1014,7 +1014,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
     $priceSet = $form->get('priceSet');
     $priceSetId = $form->get('priceSetId');
 
-    $optionsCount = array();
+    $optionsCount = [];
     if (!$priceSetId ||
       !is_array($priceSet) ||
       empty($priceSet) ||
@@ -1024,7 +1024,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
       return $optionsCount;
     }
 
-    $priceSetFields = $priceMaxFieldDetails = array();
+    $priceSetFields = $priceMaxFieldDetails = [];
     if (!empty($priceSet['optionsCountTotal'])) {
       $priceSetFields = $priceSet['optionsCountDetails']['fields'];
     }
@@ -1123,7 +1123,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
    * @param array $optionFullIds
    * @param CRM_Core_Form $form
    */
-  public static function resetElementValue($optionFullIds = array(), &$form) {
+  public static function resetElementValue($optionFullIds = [], &$form) {
     if (!is_array($optionFullIds) ||
       empty($optionFullIds) ||
       !$form->isSubmitted()
@@ -1196,7 +1196,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
    * @param array $optionIds
    * @param CRM_Core_Form $form
    */
-  public static function resetSubmittedValue($elementName, $optionIds = array(), &$form) {
+  public static function resetSubmittedValue($elementName, $optionIds = [], &$form) {
     if (empty($elementName) ||
       !$form->elementExists($elementName) ||
       !$form->getSubmitValue($elementName)
@@ -1247,7 +1247,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
    * @return array
    */
   public static function validatePriceSet(&$form, $params) {
-    $errors = array();
+    $errors = [];
     $hasOptMaxValue = FALSE;
     if (!is_array($params) || empty($params)) {
       return $errors;
@@ -1268,7 +1268,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
       return $errors;
     }
 
-    $optionsCountDetails = $optionsMaxValueDetails = array();
+    $optionsCountDetails = $optionsMaxValueDetails = [];
     if (
       isset($priceSetDetails['optionsMaxValueTotal'])
       && $priceSetDetails['optionsMaxValueTotal']
@@ -1289,7 +1289,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
       $feeBlock = $priceSetDetails['fields'];
     }
 
-    $optionMaxValues = $fieldSelected = array();
+    $optionMaxValues = $fieldSelected = [];
     foreach ($params as $pNum => $values) {
       if (!is_array($values) || $values == 'skip') {
         continue;
@@ -1348,7 +1348,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
 
     //validate for option max value.
     foreach ($optionMaxValues as $fieldId => $values) {
-      $options = CRM_Utils_Array::value('options', $feeBlock[$fieldId], array());
+      $options = CRM_Utils_Array::value('options', $feeBlock[$fieldId], []);
       foreach ($values as $optId => $total) {
         $optMax = $optionsMaxValueDetails[$fieldId]['options'][$optId];
         $opDbCount = CRM_Utils_Array::value('db_total_count', $options[$optId], 0);
@@ -1383,7 +1383,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
     $this->_participantId = $participantID;
     $this->set('participantId', $this->_participantId);
 
-    $ids = $participantValues = array();
+    $ids = $participantValues = [];
     $participantParams = array('id' => $this->_participantId);
     CRM_Event_BAO_Participant::getValues($participantParams, $participantValues, $ids);
     $this->_values['participant'] = $participantValues[$this->_participantId];
@@ -1486,14 +1486,14 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
    */
   public function processRegistration($params, $contactID = NULL) {
     $session = CRM_Core_Session::singleton();
-    $this->_participantInfo = array();
+    $this->_participantInfo = [];
 
     // CRM-4320, lets build array of cancelled additional participant ids
     // those are drop or skip by primary at the time of confirmation.
     // get all in and then unset those are confirmed.
     $cancelledIds = $this->_additionalParticipantIds;
 
-    $participantCount = array();
+    $participantCount = [];
     foreach ($params as $participantNum => $record) {
       if ($record == 'skip') {
         $participantCount[$participantNum] = 'skip';
diff --git a/civicrm/CRM/Event/Form/Registration/Confirm.php b/civicrm/CRM/Event/Form/Registration/Confirm.php
index 4547e4c173de2faaa05f8ce4a0a5695cd680ea58..14afb06fe5ba23bdd9b3f502c2eddab132cd7b3e 100644
--- a/civicrm/CRM/Event/Form/Registration/Confirm.php
+++ b/civicrm/CRM/Event/Form/Registration/Confirm.php
@@ -439,33 +439,13 @@ class CRM_Event_Form_Registration_Confirm extends CRM_Event_Form_Registration {
     $fields = [];
     foreach ($params as $key => $value) {
       CRM_Event_Form_Registration_Confirm::fixLocationFields($value, $fields, $this);
-      //unset the billing parameters if it is pay later mode
-      //to avoid creation of billing location
-      // @todo - the reasoning for this is unclear - elsewhere we check what fields are provided by
-      // the form & if billing fields exist we create the address, relying on the form to collect
-      // only information we intend to store.
       if ($this->_allowWaitlist
         || $this->_requireApproval
         || (!empty($value['is_pay_later']) && !$this->_isBillingAddressRequiredForPayLater)
         || empty($value['is_primary'])
       ) {
-        $billingFields = [
-          "email-{$this->_bltID}",
-          'billing_first_name',
-          'billing_middle_name',
-          'billing_last_name',
-          "billing_street_address-{$this->_bltID}",
-          "billing_city-{$this->_bltID}",
-          "billing_state_province-{$this->_bltID}",
-          "billing_state_province_id-{$this->_bltID}",
-          "billing_postal_code-{$this->_bltID}",
-          "billing_country-{$this->_bltID}",
-          "billing_country_id-{$this->_bltID}",
-          "address_name-{$this->_bltID}",
-        ];
-        foreach ($billingFields as $field) {
-          unset($value[$field]);
-        }
+        // This is confusing because unnecessary code around it has been removed. It is not
+        // clear why we do this / whether we should.
         if (!empty($value['is_pay_later'])) {
           $this->_values['params']['is_pay_later'] = TRUE;
         }
diff --git a/civicrm/CRM/Event/Form/Registration/Register.php b/civicrm/CRM/Event/Form/Registration/Register.php
index e39d386e6bb353a67b6d88019baabb2525759981..a2bc57ef5f7218ca9e58135637a9cca5c4942af6 100644
--- a/civicrm/CRM/Event/Form/Registration/Register.php
+++ b/civicrm/CRM/Event/Form/Registration/Register.php
@@ -467,25 +467,24 @@ class CRM_Event_Form_Registration_Register extends CRM_Event_Form_Registration {
 
       // CRM-11182 - Optional confirmation screen
       // Change button label depending on whether the next action is confirm or register
+      $buttonParams = [
+        'type' => 'upload',
+        'spacing' => '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
+        'isDefault' => TRUE,
+      ];
       if (
         !$this->_values['event']['is_multiple_registrations']
         && !$this->_values['event']['is_monetary']
         && !$this->_values['event']['is_confirm_enabled']
       ) {
-        $buttonLabel = ts('Register');
+        $buttonParams['name'] = ts('Register');
       }
       else {
-        $buttonLabel = ts('Review your registration');
+        $buttonParams['name'] = ts('Review your registration');
+        $buttonParams['icon'] = 'fa-chevron-right';
       }
 
-      $this->addButtons([
-        [
-          'type' => 'upload',
-          'name' => $buttonLabel,
-          'spacing' => '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
-          'isDefault' => TRUE,
-        ],
-      ]);
+      $this->addButtons([$buttonParams]);
     }
 
     $this->addFormRule(['CRM_Event_Form_Registration_Register', 'formRule'], $this);
diff --git a/civicrm/CRM/Event/Form/Registration/ThankYou.php b/civicrm/CRM/Event/Form/Registration/ThankYou.php
index d186528e1877f414de3449af464e138cb2f25024..4f9898b540065ff07aff5cfb1a5d8678f0ea0d0d 100644
--- a/civicrm/CRM/Event/Form/Registration/ThankYou.php
+++ b/civicrm/CRM/Event/Form/Registration/ThankYou.php
@@ -175,6 +175,8 @@ class CRM_Event_Form_Registration_ThankYou extends CRM_Event_Form_Registration {
       $this->assign('friendURL', $url);
     }
 
+    $this->assign('iCal', CRM_Event_BAO_Event::getICalLinks($this->_eventId));
+
     $this->freeze();
 
     //lets give meaningful status message, CRM-4320.
diff --git a/civicrm/CRM/Event/Form/Task/Batch.php b/civicrm/CRM/Event/Form/Task/Batch.php
index 3979b29edd7ea53936b9878e2218d08e85de28a1..9a52015e42d35786af9a9454dc0119dbc03687f1 100644
--- a/civicrm/CRM/Event/Form/Task/Batch.php
+++ b/civicrm/CRM/Event/Form/Task/Batch.php
@@ -75,19 +75,18 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task {
   /**
    * Build the form object.
    *
-   *
-   * @return void
+   * @throws \CRM_Core_Exception
    */
   public function buildQuickForm() {
     $ufGroupId = $this->get('ufGroupId');
     if (!$ufGroupId) {
-      CRM_Core_Error::fatal('ufGroupId is missing');
+      CRM_Core_Error::statusBounce('ufGroupId is missing');
     }
 
     $this->_title = ts('Update multiple participants') . ' - ' . CRM_Core_BAO_UFGroup::getTitle($ufGroupId);
     CRM_Utils_System::setTitle($this->_title);
     $this->addDefaultButtons(ts('Save'));
-    $this->_fields = [];
+
     $this->_fields = CRM_Core_BAO_UFGroup::getFields($ufGroupId, FALSE, CRM_Core_Action::VIEW);
     if (array_key_exists('participant_status', $this->_fields)) {
       $this->assign('statusProfile', 1);
@@ -194,7 +193,7 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task {
           }
         }
         else {
-          if ($field['name'] == 'participant_role') {
+          if ($field['name'] === 'participant_role') {
             $field['is_multiple'] = TRUE;
           }
           // handle non custom fields
@@ -208,7 +207,7 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task {
     // don't set the status message when form is submitted.
     $buttonName = $this->controller->getButtonName('submit');
 
-    if ($suppressFields && $buttonName != '_qf_Batch_next') {
+    if ($suppressFields && $buttonName !== '_qf_Batch_next') {
       CRM_Core_Session::setStatus(ts("File type field(s) in the selected profile are not supported for Update multiple participants."), ts('Unsupported Field Type'), 'info');
     }
 
@@ -218,12 +217,11 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task {
   /**
    * Set default values for the form.
    *
-   *
-   * @return void
+   * @return array
    */
   public function setDefaultValues() {
     if (empty($this->_fields)) {
-      return;
+      return [];
     }
 
     $defaults = [];
@@ -265,6 +263,8 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task {
    * @param int $statusId
    *
    * @return mixed
+   * @throws \CRM_Core_Exception
+   * @throws \CiviCRM_API3_Exception
    */
   public static function updatePendingOnlineContribution($participantId, $statusId) {
     if (!$participantId || !$statusId) {
@@ -315,13 +315,17 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task {
   /**
    * Update contribution status.
    *
+   * @param array $params
+   *
+   * @return NULL|int
+   * @throws \CRM_Core_Exception
+   * @throws \CiviCRM_API3_Exception
+   * @throws \Exception
+   *
    * @deprecated
    * This is only called from one place in the code &
    * it is unclear whether it is a function on the way in or on the way out
    *
-   * @param array $params
-   *
-   * @return NULL|int
    */
   public static function updateContributionStatus($params) {
     // get minimum required values.
@@ -346,7 +350,7 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task {
       );
     }
 
-    if ($componentName == 'Event') {
+    if ($componentName === 'Event') {
       $name = 'event';
       $ids['participant'] = $componentId;
 
@@ -358,7 +362,7 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task {
       }
     }
 
-    if ($componentName == 'Membership') {
+    if ($componentName === 'Membership') {
       $name = 'contribute';
       $ids['membership'] = $componentId;
     }
@@ -367,14 +371,13 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task {
     $input['component'] = $name;
 
     $baseIPN = new CRM_Core_Payment_BaseIPN();
-    $transaction = new CRM_Core_Transaction();
 
     // reset template values.
     $template = CRM_Core_Smarty::singleton();
     $template->clearTemplateVars();
 
     if (!$baseIPN->validateData($input, $ids, $objects, FALSE)) {
-      CRM_Core_Error::fatal();
+      throw new CRM_Core_Exception('validation error');
     }
 
     $contribution = &$objects['contribution'];
@@ -385,11 +388,13 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task {
     ]);
     $input['IAmAHorribleNastyBeyondExcusableHackInTheCRMEventFORMTaskClassThatNeedsToBERemoved'] = $params['IAmAHorribleNastyBeyondExcusableHackInTheCRMEventFORMTaskClassThatNeedsToBERemoved'] ?? NULL;
     if ($statusId == $contributionStatuses['Cancelled']) {
+      $transaction = new CRM_Core_Transaction();
       $baseIPN->cancelled($objects, $transaction, $input);
       $transaction->commit();
       return $statusId;
     }
     elseif ($statusId == $contributionStatuses['Failed']) {
+      $transaction = new CRM_Core_Transaction();
       $baseIPN->failed($objects, $transaction, $input);
       $transaction->commit();
       return $statusId;
@@ -397,7 +402,6 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task {
 
     // status is not pending
     if ($contribution->contribution_status_id != $contributionStatuses['Pending']) {
-      $transaction->commit();
       return;
     }
 
@@ -426,7 +430,7 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task {
     //complete the contribution.
     // @todo use the api - ie civicrm_api3('Contribution', 'completetransaction', $input);
     // as this method is not preferred / supported.
-    $baseIPN->completeTransaction($input, $ids, $objects, $transaction, FALSE);
+    CRM_Contribute_BAO_Contribution::completeOrder($input, $ids, $objects);
 
     // reset template values before processing next transactions
     $template->clearTemplateVars();
@@ -448,7 +452,10 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task {
   }
 
   /**
-   * @param $params
+   * @param array $params
+   *
+   * @throws \CRM_Core_Exception
+   * @throws \CiviCRM_API3_Exception
    */
   public function submit($params) {
     $statusClasses = CRM_Event_PseudoConstant::participantStatusClass();
diff --git a/civicrm/CRM/Event/Import/Form/MapField.php b/civicrm/CRM/Event/Import/Form/MapField.php
index b418566f2b288bdb4935c3549e6ae67a431497f2..f208534d4a304ffb8e8996172c7095f6b3586aa2 100644
--- a/civicrm/CRM/Event/Import/Form/MapField.php
+++ b/civicrm/CRM/Event/Import/Form/MapField.php
@@ -38,7 +38,7 @@ class CRM_Event_Import_Form_MapField extends CRM_Import_Form_MapField {
 
     $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader');
     $this->_onDuplicate = $this->get('onDuplicate');
-    $highlightedFields = array();
+    $highlightedFields = [];
     if ($skipColumnHeader) {
       $this->assign('skipColumnHeader', $skipColumnHeader);
       $this->assign('rowDisplayCount', 3);
@@ -107,7 +107,7 @@ class CRM_Event_Import_Form_MapField extends CRM_Import_Form_MapField {
       //mapping is to be loaded from database
 
       $params = array('id' => $savedMapping);
-      $temp = array();
+      $temp = [];
       $mappingDetails = CRM_Core_BAO_Mapping::retrieve($params, $temp);
 
       $this->assign('loadedMapping', $mappingDetails->name);
@@ -135,7 +135,7 @@ class CRM_Event_Import_Form_MapField extends CRM_Import_Form_MapField {
 
     $this->addFormRule(array('CRM_Event_Import_Form_MapField', 'formRule'), $this);
 
-    $defaults = array();
+    $defaults = [];
     $mapperKeys = array_keys($this->_mapperFields);
     $hasHeaders = !empty($this->_columnHeaders);
     $headerPatterns = $this->get('headerPatterns');
@@ -181,7 +181,7 @@ class CRM_Event_Import_Form_MapField extends CRM_Import_Form_MapField {
             $jsSet = TRUE;
           }
           else {
-            $defaults["mapper[$i]"] = array();
+            $defaults["mapper[$i]"] = [];
           }
           if (!$jsSet) {
             for ($k = 1; $k < 4; $k++) {
@@ -276,12 +276,12 @@ class CRM_Event_Import_Form_MapField extends CRM_Import_Form_MapField {
    *   list of errors to be posted back to the form
    */
   public static function formRule($fields, $files, $self) {
-    $errors = array();
+    $errors = [];
     // define so we avoid notices below
     $errors['_qf_default'] = '';
     $fieldMessage = NULL;
     if (!array_key_exists('savedMapping', $fields)) {
-      $importKeys = array();
+      $importKeys = [];
       foreach ($fields['mapper'] as $mapperPart) {
         $importKeys[] = $mapperPart[0];
       }
@@ -392,10 +392,10 @@ class CRM_Event_Import_Form_MapField extends CRM_Import_Form_MapField {
     $seperator = $this->controller->exportValue('DataSource', 'fieldSeparator');
     $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader');
 
-    $mapperKeys = array();
-    $mapper = array();
+    $mapperKeys = [];
+    $mapper = [];
     $mapperKeys = $this->controller->exportValue($this->_name, 'mapper');
-    $mapperKeysMain = array();
+    $mapperKeysMain = [];
 
     for ($i = 0; $i < $this->_columnCount; $i++) {
       $mapper[$i] = $this->_mapperFields[$mapperKeys[$i][0]];
@@ -414,7 +414,7 @@ class CRM_Event_Import_Form_MapField extends CRM_Import_Form_MapField {
       $mappingFields->mapping_id = $params['mappingId'];
       $mappingFields->find();
 
-      $mappingFieldsId = array();
+      $mappingFieldsId = [];
       while ($mappingFields->fetch()) {
         if ($mappingFields->id) {
           $mappingFieldsId[$mappingFields->column_number] = $mappingFields->id;
diff --git a/civicrm/CRM/Event/Page/DashBoard.php b/civicrm/CRM/Event/Page/DashBoard.php
index f92fc7a0904347e5ddadcae555cdf857ccf9e386..9f86809e13c49c2731e46e1454a5993e965f85e0 100644
--- a/civicrm/CRM/Event/Page/DashBoard.php
+++ b/civicrm/CRM/Event/Page/DashBoard.php
@@ -9,14 +9,6 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
 /**
  * This is page is for Event Dashboard
  */
@@ -32,8 +24,7 @@ class CRM_Event_Page_DashBoard extends CRM_Core_Page {
     CRM_Utils_System::setTitle(ts('CiviEvent'));
 
     $eventSummary = CRM_Event_BAO_Event::getEventSummary();
-    $enableCart = Civi::settings()->get('enable_cart');
-    $eventSummary['tab'] = CRM_Event_Page_ManageEvent::tabs($enableCart);
+    $eventSummary['tab'] = CRM_Event_Page_ManageEvent::tabs();
 
     $actionColumn = FALSE;
     if (!empty($eventSummary) &&
@@ -50,6 +41,7 @@ class CRM_Event_Page_DashBoard extends CRM_Core_Page {
 
     $this->assign('actionColumn', $actionColumn);
     $this->assign('eventSummary', $eventSummary);
+    $this->assign('iCal', CRM_Event_BAO_Event::getICalLinks());
   }
 
   /**
diff --git a/civicrm/CRM/Event/Page/EventInfo.php b/civicrm/CRM/Event/Page/EventInfo.php
index c07681da8bbc3ecb1735c1c4c35c5af2e96dd964..265b3f5dfeb81ab09a04d8351c591a2fb6fcccdc 100644
--- a/civicrm/CRM/Event/Page/EventInfo.php
+++ b/civicrm/CRM/Event/Page/EventInfo.php
@@ -46,6 +46,8 @@ class CRM_Event_Page_EventInfo extends CRM_Core_Page {
     $context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this, FALSE, 'register');
     $this->assign('context', $context);
 
+    $this->assign('iCal', CRM_Event_BAO_Event::getICalLinks($this->_id));
+
     // Sometimes we want to suppress the Event Full msg
     $noFullMsg = CRM_Utils_Request::retrieve('noFullMsg', 'String', $this, FALSE, 'false');
 
@@ -126,7 +128,7 @@ class CRM_Event_Page_EventInfo extends CRM_Core_Page {
             }
             // show tax rate with amount
             $invoiceSettings = Civi::settings()->get('contribution_invoice_settings');
-            $taxTerm = $invoiceSettings['tax_term'] ?? NULL;
+            $taxTerm = Civi::settings()->get('tax_term');
             $displayOpt = $invoiceSettings['tax_display_settings'] ?? NULL;
             $invoicing = $invoiceSettings['invoicing'] ?? NULL;
             foreach ($fieldValues['options'] as $optionId => $optionVal) {
@@ -339,8 +341,7 @@ class CRM_Event_Page_EventInfo extends CRM_Core_Page {
     $this->assign('location', $values['location']);
 
     if (CRM_Core_Permission::check(['access CiviEvent', 'edit all events'])) {
-      $enableCart = Civi::settings()->get('enable_cart');
-      $this->assign('manageEventLinks', CRM_Event_Page_ManageEvent::tabs($enableCart));
+      $this->assign('manageEventLinks', CRM_Event_Page_ManageEvent::tabs());
     }
 
     return parent::run();
diff --git a/civicrm/CRM/Event/Page/ManageEvent.php b/civicrm/CRM/Event/Page/ManageEvent.php
index 20b4ed5e86e0760342d540828e73f05846261bce..bc7badd236d30c9614641857e90209c9a0cbcc96 100644
--- a/civicrm/CRM/Event/Page/ManageEvent.php
+++ b/civicrm/CRM/Event/Page/ManageEvent.php
@@ -130,7 +130,8 @@ class CRM_Event_Page_ManageEvent extends CRM_Core_Page {
    * @return array
    *   (reference) of tab links
    */
-  public static function &tabs($enableCart) {
+  public static function &tabs() {
+    $enableCart = Civi::settings()->get('enable_cart');
     $cacheKey = $enableCart ? 1 : 0;
     if (!(self::$_tabLinks)) {
       self::$_tabLinks = [];
@@ -224,6 +225,7 @@ class CRM_Event_Page_ManageEvent extends CRM_Core_Page {
 
     // assign vars to templates
     $this->assign('action', $action);
+    $this->assign('iCal', CRM_Event_BAO_Event::getICalLinks());
     $id = CRM_Utils_Request::retrieve('id', 'Positive',
       $this, FALSE, 0, 'REQUEST'
     );
diff --git a/civicrm/CRM/Event/StateMachine/Registration.php b/civicrm/CRM/Event/StateMachine/Registration.php
index b86e7baf2072b63316ff32bc2b614c4088391133..e70a4bc8efe92060289c208e90cfa9bfb8696d1d 100644
--- a/civicrm/CRM/Event/StateMachine/Registration.php
+++ b/civicrm/CRM/Event/StateMachine/Registration.php
@@ -9,17 +9,8 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
 /**
  * State machine for managing different states of the EventWizard process.
- *
  */
 class CRM_Event_StateMachine_Registration extends CRM_Core_StateMachine {
 
@@ -27,9 +18,9 @@ class CRM_Event_StateMachine_Registration extends CRM_Core_StateMachine {
    * Class constructor.
    *
    * @param object $controller
-   * @param \const|int $action
+   * @param int $action
    *
-   * @return \CRM_Event_StateMachine_Registration CRM_Event_StateMachine
+   * @throws \CRM_Core_Exception
    */
   public function __construct($controller, $action = CRM_Core_Action::NONE) {
     parent::__construct($controller, $action);
diff --git a/civicrm/CRM/Event/StateMachine/Search.php b/civicrm/CRM/Event/StateMachine/Search.php
index f98de49db6e345196072c8dd6df3b6688eaed139..f235ac402638c563d0a0aa3da9a6ea69228bc309 100644
--- a/civicrm/CRM/Event/StateMachine/Search.php
+++ b/civicrm/CRM/Event/StateMachine/Search.php
@@ -10,11 +10,7 @@
  */
 
 /**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
+ * Class CRM_Event_StateMachine_Search
  */
 class CRM_Event_StateMachine_Search extends CRM_Core_StateMachine {
 
@@ -28,7 +24,7 @@ class CRM_Event_StateMachine_Search extends CRM_Core_StateMachine {
   /**
    * Class constructor.
    *
-   * @param object $controller
+   * @param CRM_Core_Controller $controller
    * @param int $action
    */
   public function __construct($controller, $action = CRM_Core_Action::NONE) {
diff --git a/civicrm/CRM/Event/xml/Menu/Event.xml b/civicrm/CRM/Event/xml/Menu/Event.xml
index 9bf47c9d1a66fbfe00660ef678b208d35d9dba7d..fda18a1b9221b9bc9f53bbbc4185824fb539e9f9 100644
--- a/civicrm/CRM/Event/xml/Menu/Event.xml
+++ b/civicrm/CRM/Event/xml/Menu/Event.xml
@@ -72,7 +72,6 @@
      <desc>Create and edit event configuration including times, locations, online registration forms, and fees. Links for iCal and RSS syndication.</desc>
      <access_arguments>access CiviCRM,access CiviEvent</access_arguments>
      <adminGroup>CiviEvent</adminGroup>
-     <icon>admin/small/event_manage.png</icon>
      <weight>370</weight>
   </item>
   <item>
@@ -82,7 +81,6 @@
      <desc>Administrators can create Event Templates - which are basically master event records pre-filled with default values</desc>
      <access_arguments>access CiviEvent</access_arguments>
      <adminGroup>CiviEvent</adminGroup>
-     <icon>admin/small/template.png</icon>
      <weight>375</weight>
   </item>
   <item>
@@ -92,7 +90,6 @@
      <desc>Use Event Types to categorize your events. Event feeds can be filtered by Event Type and participant searches can use Event Type as a criteria.</desc>
      <access_arguments>administer CiviCRM,access CiviEvent</access_arguments>
      <adminGroup>CiviEvent</adminGroup>
-     <icon>admin/small/event_type.png</icon>
      <weight>385</weight>
   </item>
   <item>
@@ -102,7 +99,6 @@
      <desc>Define statuses for event participants here (e.g. Registered, Attended, Cancelled...). You can then assign statuses and search for participants by status.</desc>
      <access_arguments>administer CiviCRM,access CiviEvent</access_arguments>
      <adminGroup>CiviEvent</adminGroup>
-     <icon>admin/small/parti_status.png</icon>
      <weight>390</weight>
   </item>
   <item>
@@ -112,7 +108,6 @@
      <desc>Define participant roles for events here (e.g. Attendee, Host, Speaker...). You can then assign roles and search for participants by role.</desc>
      <access_arguments>administer CiviCRM,access CiviEvent</access_arguments>
      <adminGroup>CiviEvent</adminGroup>
-     <icon>admin/small/parti_role.png</icon>
      <weight>395</weight>
   </item>
   <item>
@@ -121,7 +116,6 @@
      <desc>Template to control participant listing display.</desc>
      <page_callback>CRM_Admin_Page_Options</page_callback>
      <adminGroup>CiviEvent</adminGroup>
-     <icon>admin/small/01.png</icon>
      <weight>398</weight>
   </item>
   <item>
diff --git a/civicrm/CRM/Event/xml/Menu/PCP.xml b/civicrm/CRM/Event/xml/Menu/PCP.xml
index d64ccb5d0a901f1cc3fcde88a5dc12b2689f2466..3b4012f7c6ba5d271d8b7f990d39aca6224c8cc5 100644
--- a/civicrm/CRM/Event/xml/Menu/PCP.xml
+++ b/civicrm/CRM/Event/xml/Menu/PCP.xml
@@ -7,7 +7,6 @@
      <path_arguments>context=event</path_arguments>
      <desc>View and manage existing personal campaign pages.</desc>
      <adminGroup>CiviEvent</adminGroup>
-     <icon>admin/small/contribution_types.png</icon>
      <weight>362</weight>
   </item>
   <item>
diff --git a/civicrm/CRM/Extension/Browser.php b/civicrm/CRM/Extension/Browser.php
index 89994cb00ebee2c52960bd74e7986feb65cdb208..ff173a54a334e5fd91ac5550e354fa5245b5d208 100644
--- a/civicrm/CRM/Extension/Browser.php
+++ b/civicrm/CRM/Extension/Browser.php
@@ -94,10 +94,10 @@ class CRM_Extension_Browser {
    */
   public function checkRequirements() {
     if (!$this->isEnabled()) {
-      return array();
+      return [];
     }
 
-    $errors = array();
+    $errors = [];
 
     if (!$this->cacheDir || !is_dir($this->cacheDir) || !is_writable($this->cacheDir)) {
       $civicrmDestination = urlencode(CRM_Utils_System::url('civicrm/admin/extensions', 'reset=1'));
@@ -124,10 +124,10 @@ class CRM_Extension_Browser {
    */
   public function getExtensions() {
     if (!$this->isEnabled() || count($this->checkRequirements())) {
-      return array();
+      return [];
     }
 
-    $exts = array();
+    $exts = [];
 
     $remote = $this->_discoverRemote();
     if (is_array($remote)) {
@@ -180,7 +180,7 @@ class CRM_Extension_Browser {
       $remotes = json_decode($this->grabCachedJson(), TRUE);
     }
 
-    $this->_remotesDiscovered = array();
+    $this->_remotesDiscovered = [];
     foreach ((array) $remotes as $id => $xml) {
       $ext = CRM_Extension_Info::loadFromString($xml);
       $this->_remotesDiscovered[] = $ext;
@@ -230,7 +230,7 @@ class CRM_Extension_Browser {
     if (FALSE === $this->getRepositoryUrl()) {
       // don't check if the user has configured civi not to check an external
       // url for extensions. See CRM-10575.
-      return array();
+      return [];
     }
 
     $filename = $this->cacheDir . DIRECTORY_SEPARATOR . self::CACHE_JSON_FILE . '.' . md5($this->getRepositoryUrl());
diff --git a/civicrm/CRM/Extension/ClassLoader.php b/civicrm/CRM/Extension/ClassLoader.php
index 9ce09a44922e6ca33a3e8699ac549906a687d42e..2e181a887784b13be254a32e4e665329bbb62da6 100644
--- a/civicrm/CRM/Extension/ClassLoader.php
+++ b/civicrm/CRM/Extension/ClassLoader.php
@@ -10,12 +10,7 @@
  */
 
 /**
- *
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
+ * Class CRM_Extension_ClassLoader
  */
 class CRM_Extension_ClassLoader {
 
diff --git a/civicrm/CRM/Extension/Container/Default.php b/civicrm/CRM/Extension/Container/Default.php
index 04796ad933b1cc85e728e7aedf0251da9416a064..f5a6c4ce3cd686dceeb8bad3d1f98b1b63fb38d4 100644
--- a/civicrm/CRM/Extension/Container/Default.php
+++ b/civicrm/CRM/Extension/Container/Default.php
@@ -26,7 +26,7 @@ class CRM_Extension_Container_Default extends CRM_Extension_Container_Basic {
    * @return array
    */
   public function checkRequirements() {
-    $errors = array();
+    $errors = [];
 
     // In current configuration, we don't construct the default container
     // unless baseDir is set, so this error condition is more theoretical.
diff --git a/civicrm/CRM/Extension/Downloader.php b/civicrm/CRM/Extension/Downloader.php
index cc43c8be6ee4c0efa0ff193f3f95cf8b0361ff53..a76ca105e1c4debd3edd295410edbd52f243a499 100644
--- a/civicrm/CRM/Extension/Downloader.php
+++ b/civicrm/CRM/Extension/Downloader.php
@@ -49,7 +49,7 @@ class CRM_Extension_Downloader {
    *   list of error messages; empty if OK
    */
   public function checkRequirements($extensionInfo = NULL) {
-    $errors = array();
+    $errors = [];
 
     if (!$this->containerDir || !is_dir($this->containerDir) || !is_writable($this->containerDir)) {
       $civicrmDestination = urlencode(CRM_Utils_System::url('civicrm/admin/extensions', 'reset=1'));
diff --git a/civicrm/CRM/Financial/BAO/FinancialItem.php b/civicrm/CRM/Financial/BAO/FinancialItem.php
index 60c6332f6c32cffa36ae77c6a7f09ae6ee6ad6bd..317fb8dadb7a49756b58b090e800bcca5ba711fc 100644
--- a/civicrm/CRM/Financial/BAO/FinancialItem.php
+++ b/civicrm/CRM/Financial/BAO/FinancialItem.php
@@ -85,10 +85,8 @@ class CRM_Financial_BAO_FinancialItem extends CRM_Financial_DAO_FinancialItem {
     ];
 
     if ($taxTrxnID) {
-      $invoiceSettings = Civi::settings()->get('contribution_invoice_settings');
-      $taxTerm = $invoiceSettings['tax_term'] ?? NULL;
       $params['amount'] = $lineItem->tax_amount;
-      $params['description'] = $taxTerm;
+      $params['description'] = Civi::settings()->get('tax_term');
       $accountRelName = 'Sales Tax Account is';
     }
     else {
diff --git a/civicrm/CRM/Financial/BAO/Payment.php b/civicrm/CRM/Financial/BAO/Payment.php
index 8a06f7f26cc39a1fd13fd0a7f66a8a316c22e062..05de84f0b77fd063cfd34a55637643643994203f 100644
--- a/civicrm/CRM/Financial/BAO/Payment.php
+++ b/civicrm/CRM/Financial/BAO/Payment.php
@@ -42,7 +42,7 @@ class CRM_Financial_BAO_Payment {
     $isPaymentCompletesContribution = self::isPaymentCompletesContribution($params['contribution_id'], $params['total_amount'], $contributionStatus);
     $lineItems = self::getPayableLineItems($params);
 
-    $whiteList = ['check_number', 'payment_processor_id', 'fee_amount', 'total_amount', 'contribution_id', 'net_amount', 'card_type_id', 'pan_truncation', 'trxn_result_code', 'payment_instrument_id', 'trxn_id', 'trxn_date'];
+    $whiteList = ['check_number', 'payment_processor_id', 'fee_amount', 'total_amount', 'contribution_id', 'net_amount', 'card_type_id', 'pan_truncation', 'trxn_result_code', 'payment_instrument_id', 'trxn_id', 'trxn_date', 'order_reference'];
     $paymentTrxnParams = array_intersect_key($params, array_fill_keys($whiteList, 1));
     $paymentTrxnParams['is_payment'] = 1;
     // Really we should have a DB default.
@@ -163,10 +163,41 @@ class CRM_Financial_BAO_Payment {
       //  change status to refunded.
       self::updateContributionStatus($contribution['id'], 'Refunded');
     }
+    self::updateRelatedContribution($params, $params['contribution_id']);
     CRM_Contribute_BAO_Contribution::recordPaymentActivity($params['contribution_id'], CRM_Utils_Array::value('participant_id', $params), $params['total_amount'], $trxn->currency, $trxn->trxn_date);
     return $trxn;
   }
 
+  /**
+   * Function to update contribution's check_number and trxn_id by
+   *  concatenating values from financial trxn's check_number and trxn_id respectively
+   *
+   * @param array $params
+   * @param int $contributionID
+   */
+  public static function updateRelatedContribution($params, $contributionID) {
+    $contributionDAO = new CRM_Contribute_DAO_Contribution();
+    $contributionDAO->id = $contributionID;
+    $contributionDAO->find(TRUE);
+
+    foreach (['trxn_id', 'check_number'] as $fieldName) {
+      if (!empty($params[$fieldName])) {
+        $values = [];
+        if (!empty($contributionDAO->$fieldName)) {
+          $values = explode(',', $contributionDAO->$fieldName);
+        }
+        // if submitted check_number or trxn_id value is
+        //   already present then ignore else add to $values array
+        if (!in_array($params[$fieldName], $values)) {
+          $values[] = $params[$fieldName];
+        }
+        $contributionDAO->$fieldName = implode(',', $values);
+      }
+    }
+
+    $contributionDAO->save();
+  }
+
   /**
    * Send an email confirming a payment that has been received.
    *
diff --git a/civicrm/CRM/Financial/Form/PaymentEdit.php b/civicrm/CRM/Financial/Form/PaymentEdit.php
index f131ef76776bf4fa1eb7cc0a5ec293d2df5c5b2f..b40f1325a344db138a3caf4fe3818a8d901f422a 100644
--- a/civicrm/CRM/Financial/Form/PaymentEdit.php
+++ b/civicrm/CRM/Financial/Form/PaymentEdit.php
@@ -214,7 +214,7 @@ class CRM_Financial_Form_PaymentEdit extends CRM_Core_Form {
       civicrm_api3('FinancialTrxn', 'create', $submittedValues);
     }
 
-    self::updateRelatedContribution($submittedValues, $this->_contributionID);
+    CRM_Financial_BAO_Payment::updateRelatedContribution($submittedValues, $this->_contributionID);
   }
 
   /**
@@ -230,35 +230,6 @@ class CRM_Financial_Form_PaymentEdit extends CRM_Core_Form {
     $this->submit($params);
   }
 
-  /**
-   * Function to update contribution's check_number and trxn_id by
-   *  concatenating values from financial trxn's check_number and trxn_id respectively
-   *
-   * @param array $params
-   * @param int $contributionID
-   */
-  public static function updateRelatedContribution($params, $contributionID) {
-    $contributionDAO = new CRM_Contribute_DAO_Contribution();
-    $contributionDAO->id = $contributionID;
-    $contributionDAO->find(TRUE);
-
-    foreach (['trxn_id', 'check_number'] as $fieldName) {
-      if (!empty($params[$fieldName])) {
-        if (!empty($contributionDAO->$fieldName)) {
-          $values = explode(',', $contributionDAO->$fieldName);
-          // if submitted check_number or trxn_id value is
-          //   already present then ignore else add to $values array
-          if (!in_array($params[$fieldName], $values)) {
-            $values[] = $params[$fieldName];
-          }
-          $contributionDAO->$fieldName = implode(',', $values);
-        }
-      }
-    }
-
-    $contributionDAO->save();
-  }
-
   /**
    * Get payment fields
    */
diff --git a/civicrm/CRM/Financial/Form/SalesTaxTrait.php b/civicrm/CRM/Financial/Form/SalesTaxTrait.php
index 45710bd0649902059157b1b2fa4c85e8c2b7509e..3ea4d0183c5f54f8200fb6ab8f639b46d4a87cda 100644
--- a/civicrm/CRM/Financial/Form/SalesTaxTrait.php
+++ b/civicrm/CRM/Financial/Form/SalesTaxTrait.php
@@ -36,12 +36,10 @@ trait CRM_Financial_Form_SalesTaxTrait {
    * @return string
    */
   public function getSalesTaxTerm() {
-    $invoiceSettings = Civi::settings()->get('contribution_invoice_settings');
-    $invoicing = $invoiceSettings['invoicing'] ?? NULL;
-    if (!$invoicing) {
+    if (!Civi::settings()->get('invoicing')) {
       return '';
     }
-    return $invoiceSettings['tax_term'] ?? NULL;
+    return Civi::settings()->get('tax_term');
   }
 
   /**
diff --git a/civicrm/CRM/Grant/BAO/Grant.php b/civicrm/CRM/Grant/BAO/Grant.php
index 3d5fa8e1aca82686f84eaaaba467e40989d8a91e..c314802a2b487d2b89f41ac931185fbf0cd1d1cc 100644
--- a/civicrm/CRM/Grant/BAO/Grant.php
+++ b/civicrm/CRM/Grant/BAO/Grant.php
@@ -10,11 +10,7 @@
  */
 
 /**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
+ * Class CRM_Grant_BAO_Grant
  */
 class CRM_Grant_BAO_Grant extends CRM_Grant_DAO_Grant {
 
@@ -113,21 +109,14 @@ class CRM_Grant_BAO_Grant extends CRM_Grant_DAO_Grant {
    * Add grant.
    *
    * @param array $params
-   *   Reference array contains the values submitted by the form.
    * @param array $ids
-   *   Reference array contains the id.
-   *
    *
    * @return object
    */
-  public static function add(&$params, &$ids) {
-
-    if (!empty($ids['grant_id'])) {
-      CRM_Utils_Hook::pre('edit', 'Grant', $ids['grant_id'], $params);
-    }
-    else {
-      CRM_Utils_Hook::pre('create', 'Grant', NULL, $params);
-    }
+  public static function add($params, $ids = []) {
+    $id = $ids['grant_id'] ?? $params['id'] ?? NULL;
+    $hook = $id ? 'edit' : 'create';
+    CRM_Utils_Hook::pre($hook, 'Grant', $id, $params);
 
     // first clean up all the money fields
     $moneyFields = [
@@ -154,7 +143,7 @@ class CRM_Grant_BAO_Grant extends CRM_Grant_DAO_Grant {
       }
     }
     $grant = new CRM_Grant_DAO_Grant();
-    $grant->id = $ids['grant_id'] ?? NULL;
+    $grant->id = $id;
 
     $grant->copyValues($params);
 
@@ -200,27 +189,20 @@ class CRM_Grant_BAO_Grant extends CRM_Grant_DAO_Grant {
       );
     }
 
-    if (!empty($ids['grant'])) {
-      CRM_Utils_Hook::post('edit', 'Grant', $grant->id, $grant);
-    }
-    else {
-      CRM_Utils_Hook::post('create', 'Grant', $grant->id, $grant);
-    }
+    CRM_Utils_Hook::post($hook, 'Grant', $grant->id, $grant);
 
     return $result;
   }
 
   /**
-   * Create the event.
+   * Adds a grant.
    *
    * @param array $params
-   *   Reference array contains the values submitted by the form.
    * @param array $ids
-   *   Reference array contains the id.
    *
    * @return object
    */
-  public static function create(&$params, &$ids) {
+  public static function create($params, $ids = []) {
     $transaction = new CRM_Core_Transaction();
 
     $grant = self::add($params, $ids);
diff --git a/civicrm/CRM/Grant/BAO/Query.php b/civicrm/CRM/Grant/BAO/Query.php
index 5efbb3b1ff94964f1b80c1500147a742ba7e2361..d91699ca81b2ea44b91b1e828955cc37235a840d 100644
--- a/civicrm/CRM/Grant/BAO/Query.php
+++ b/civicrm/CRM/Grant/BAO/Query.php
@@ -10,11 +10,7 @@
  */
 
 /**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
+ * Class CRM_Grant_BAO_Query
  */
 class CRM_Grant_BAO_Query extends CRM_Core_BAO_Query {
 
diff --git a/civicrm/CRM/Grant/Form/Grant.php b/civicrm/CRM/Grant/Form/Grant.php
index 7f81aceb68d3cb4418e0839fd20c351b1baedc12..80d4f97b8db5f702c24f97f2b78c00021cd812d8 100644
--- a/civicrm/CRM/Grant/Form/Grant.php
+++ b/civicrm/CRM/Grant/Form/Grant.php
@@ -9,14 +9,6 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
 /**
  * This class generates form components for processing a case
  *
diff --git a/civicrm/CRM/Grant/Form/Task.php b/civicrm/CRM/Grant/Form/Task.php
index 561bd0bfc9fcdf5197daf4f57855454e530d55ee..b983cec3d72afac8c84b7804bc115370af162b88 100644
--- a/civicrm/CRM/Grant/Form/Task.php
+++ b/civicrm/CRM/Grant/Form/Task.php
@@ -9,14 +9,6 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
 /**
  * Class for grant form task actions.
  * FIXME: This needs refactoring to properly inherit from CRM_Core_Form_Task and share more functions.
diff --git a/civicrm/CRM/Group/Form/Edit.php b/civicrm/CRM/Group/Form/Edit.php
index fa3135f426cb92767d4ea71c4fb0bb2ac239a0ce..d0e6ac595dfe27175072cd070c8fa436e0d3e9f6 100644
--- a/civicrm/CRM/Group/Form/Edit.php
+++ b/civicrm/CRM/Group/Form/Edit.php
@@ -102,7 +102,7 @@ class CRM_Group_Form_Edit extends CRM_Core_Form {
       );
       CRM_Utils_System::appendBreadCrumb($breadCrumb);
 
-      $this->_groupValues = array();
+      $this->_groupValues = [];
       $params = array('id' => $this->_id);
       $this->_group = CRM_Contact_BAO_Group::retrieve($params, $this->_groupValues);
       $this->_title = $this->_groupValues['title'];
@@ -172,14 +172,14 @@ class CRM_Group_Form_Edit extends CRM_Core_Form {
    * @return array
    */
   public function setDefaultValues() {
-    $defaults = array();
+    $defaults = [];
     if (isset($this->_id)) {
       $defaults = $this->_groupValues;
       if (!empty($defaults['group_type'])) {
         $types = explode(CRM_Core_DAO::VALUE_SEPARATOR,
           substr($defaults['group_type'], 1, -1)
         );
-        $defaults['group_type'] = array();
+        $defaults['group_type'] = [];
         foreach ($types as $type) {
           $defaults['group_type'][$type] = 1;
         }
@@ -286,7 +286,7 @@ class CRM_Group_Form_Edit extends CRM_Core_Form {
    *   list of errors to be posted back to the form
    */
   public static function formRule($fields, $fileParams, $options) {
-    $errors = array();
+    $errors = [];
 
     $doParentCheck = $options['doParentCheck'];
     $self = &$options['selfObj'];
@@ -360,7 +360,7 @@ WHERE  title = %1
 
       // CRM-21431 If all group_type are unchecked, the change will not be saved otherwise.
       if (!isset($params['group_type'])) {
-        $params['group_type'] = array();
+        $params['group_type'] = [];
       }
 
       $params['is_reserved'] = CRM_Utils_Array::value('is_reserved', $params, FALSE);
@@ -411,7 +411,7 @@ WHERE  title = %1
    */
   public static function buildParentGroups(&$form) {
     $groupNames = CRM_Core_PseudoConstant::group();
-    $parentGroups = $parentGroupElements = array();
+    $parentGroups = $parentGroupElements = [];
     if (isset($form->_id) && !empty($form->_groupValues['parents'])) {
       $parentGroupIds = explode(',', $form->_groupValues['parents']);
       foreach ($parentGroupIds as $parentGroupId) {
@@ -433,7 +433,7 @@ WHERE  title = %1
       $potentialParentGroupIds = array_keys($groupNames);
     }
 
-    $parentGroupSelectValues = array();
+    $parentGroupSelectValues = [];
     foreach ($potentialParentGroupIds as $potentialParentGroupId) {
       if (array_key_exists($potentialParentGroupId, $groupNames)) {
         $parentGroupSelectValues[$potentialParentGroupId] = $groupNames[$potentialParentGroupId];
diff --git a/civicrm/CRM/Import/Form/Preview.php b/civicrm/CRM/Import/Form/Preview.php
index 3d01c5a300b5a32dd0a8660ca291d8abb5ec47b5..5067a22c4813b91bc298b50c07768ddfbef18955 100644
--- a/civicrm/CRM/Import/Form/Preview.php
+++ b/civicrm/CRM/Import/Form/Preview.php
@@ -36,21 +36,37 @@ abstract class CRM_Import_Form_Preview extends CRM_Core_Form {
    * Build the form object.
    */
   public function buildQuickForm() {
+
+    // FIXME: This is a hack...
+    // The tpl contains javascript that starts the import on form submit
+    // Since our back/cancel buttons are of html type "submit" we have to prevent a form submit event when they are clicked
+    // Hacking in some onclick js to make them act more like links instead of buttons
+    $path = CRM_Utils_System::currentPath();
+    $query = ['_qf_MapField_display' => 'true'];
+    $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String');
+    if (CRM_Utils_Rule::qfKey($qfKey)) {
+      $query['qfKey'] = $qfKey;
+    }
+    $previousURL = CRM_Utils_System::url($path, $query, FALSE, NULL, FALSE);
+    $cancelURL = CRM_Utils_System::url($path, 'reset=1', FALSE, NULL, FALSE);
+
     $this->addButtons([
-        [
-          'type' => 'back',
-          'name' => ts('Previous'),
-        ],
-        [
-          'type' => 'next',
-          'name' => ts('Import Now'),
-          'spacing' => '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
-          'isDefault' => TRUE,
-        ],
-        [
-          'type' => 'cancel',
-          'name' => ts('Cancel'),
-        ],
+      [
+        'type' => 'back',
+        'name' => ts('Previous'),
+        'js' => ['onclick' => "location.href='{$previousURL}'; return false;"],
+      ],
+      [
+        'type' => 'next',
+        'name' => ts('Import Now'),
+        'spacing' => '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
+        'isDefault' => TRUE,
+      ],
+      [
+        'type' => 'cancel',
+        'name' => ts('Cancel'),
+        'js' => ['onclick' => "location.href='{$cancelURL}'; return false;"],
+      ],
     ]);
   }
 
diff --git a/civicrm/CRM/Invoicing/Utils.php b/civicrm/CRM/Invoicing/Utils.php
index dbcc4befacd850f6a2731d5949d8baf853b09cf2..24fe4078e755e02d67cbfc1a370ec8a1ae6bf96d 100644
--- a/civicrm/CRM/Invoicing/Utils.php
+++ b/civicrm/CRM/Invoicing/Utils.php
@@ -69,8 +69,7 @@ class CRM_Invoicing_Utils {
    * is unsupported. Here we have a wrapper function to make later cleanup easier.
    */
   public static function getTaxTerm() {
-    $invoiceSettings = Civi::settings()->get('contribution_invoice_settings');
-    return $invoiceSettings['tax_term'] ?? NULL;
+    return Civi::settings()->get('tax_term');
   }
 
 }
diff --git a/civicrm/CRM/Logging/Schema.php b/civicrm/CRM/Logging/Schema.php
index 5d4ae83f83933a0ef95cfccc71a259c38d3bd634..183af4100ac696a62e314f46dcfcf4a1c56747e4 100644
--- a/civicrm/CRM/Logging/Schema.php
+++ b/civicrm/CRM/Logging/Schema.php
@@ -142,6 +142,9 @@ AND    TABLE_NAME LIKE 'civicrm_%'
     // do not log civicrm_mailing_event* tables, CRM-12300
     $this->tables = preg_grep('/^civicrm_mailing_event_/', $this->tables, PREG_GREP_INVERT);
 
+    // dev/core#1762 Don't log subscription_history
+    $this->tables = preg_grep('/^civicrm_subscription_history/', $this->tables, PREG_GREP_INVERT);
+
     // do not log civicrm_mailing_recipients table, CRM-16193
     $this->tables = array_diff($this->tables, ['civicrm_mailing_recipients']);
     $this->logTableSpec = array_fill_keys($this->tables, []);
diff --git a/civicrm/CRM/Mailing/BAO/Mailing.php b/civicrm/CRM/Mailing/BAO/Mailing.php
index 46ee694de39a6fe71101dbb11a04b4b14faa4751..8169513e89a136a0bba1ebb65f9b2fe2240ba900 100644
--- a/civicrm/CRM/Mailing/BAO/Mailing.php
+++ b/civicrm/CRM/Mailing/BAO/Mailing.php
@@ -690,6 +690,7 @@ class CRM_Mailing_BAO_Mailing extends CRM_Mailing_DAO_Mailing {
       }
 
       $this->templates['mailingID'] = $this->id;
+      $this->templates['campaign_id'] = $this->campaign_id;
       $this->templates['template_type'] = $this->template_type;
       CRM_Utils_Hook::alterMailContent($this->templates);
     }
@@ -1558,8 +1559,8 @@ ORDER BY   civicrm_email.is_bulkmail DESC
         // correct template IDs here
         'override_verp' => TRUE,
         'forward_replies' => FALSE,
-        'open_tracking' => TRUE,
-        'url_tracking' => TRUE,
+        'open_tracking' => Civi::settings()->get('open_tracking_default'),
+        'url_tracking' => Civi::settings()->get('url_tracking_default'),
         'visibility' => 'Public Pages',
         'replyto_email' => $domain_email,
         'header_id' => CRM_Mailing_PseudoConstant::defaultComponent('header_id', ''),
diff --git a/civicrm/CRM/Mailing/BAO/MailingAB.php b/civicrm/CRM/Mailing/BAO/MailingAB.php
index 02b305e4991101422a2b50c3553ad882c73b5496..7d26a2c1000a2f9f43e615dfdd0a7d5be3befe45 100644
--- a/civicrm/CRM/Mailing/BAO/MailingAB.php
+++ b/civicrm/CRM/Mailing/BAO/MailingAB.php
@@ -34,15 +34,13 @@ class CRM_Mailing_BAO_MailingAB extends CRM_Mailing_DAO_MailingAB {
    *   Form values.
    *
    * @param array $params
-   * @param array $ids
    *
-   * @return object
-   *   $mailingab      The new mailingab object
+   * @return CRM_Mailing_DAO_MailingAB
    */
-  public static function create(&$params, $ids = []) {
+  public static function create(&$params) {
     $transaction = new CRM_Core_Transaction();
 
-    $mailingab = self::add($params, $ids);
+    $mailingab = self::writeRecord($params);
 
     if (is_a($mailingab, 'CRM_Core_Error')) {
       $transaction->rollback();
@@ -52,47 +50,6 @@ class CRM_Mailing_BAO_MailingAB extends CRM_Mailing_DAO_MailingAB {
     return $mailingab;
   }
 
-  /**
-   * function to add the mailings.
-   *
-   * @param array $params
-   *   Reference array contains the values submitted by the form.
-   * @param array $ids
-   *   Reference array contains the id.
-   *
-   *
-   * @return object
-   */
-  public static function add(&$params, $ids = []) {
-    $id = CRM_Utils_Array::value('mailingab_id', $ids, CRM_Utils_Array::value('id', $params));
-
-    if ($id) {
-      CRM_Utils_Hook::pre('edit', 'MailingAB', $id, $params);
-    }
-    else {
-      CRM_Utils_Hook::pre('create', 'MailingAB', NULL, $params);
-    }
-
-    $mailingab = new CRM_Mailing_DAO_MailingAB();
-    $mailingab->id = $id;
-    if (!$id) {
-      $mailingab->domain_id = CRM_Utils_Array::value('domain_id', $params, CRM_Core_Config::domainID());
-    }
-
-    $mailingab->copyValues($params);
-
-    $result = $mailingab->save();
-
-    if ($id) {
-      CRM_Utils_Hook::post('edit', 'MailingAB', $mailingab->id, $mailingab);
-    }
-    else {
-      CRM_Utils_Hook::post('create', 'MailingAB', $mailingab->id, $mailingab);
-    }
-
-    return $result;
-  }
-
   /**
    * Delete MailingAB and all its associated records.
    *
diff --git a/civicrm/CRM/Mailing/BAO/Query.php b/civicrm/CRM/Mailing/BAO/Query.php
index f734f1cd1285bab053d1b015a6101088dbd87b4c..b9d58a99fcb6e5612aeb4d14f457b1e7673e32db 100644
--- a/civicrm/CRM/Mailing/BAO/Query.php
+++ b/civicrm/CRM/Mailing/BAO/Query.php
@@ -122,7 +122,7 @@ class CRM_Mailing_BAO_Query {
    * rather than a static function.
    */
   public static function getSearchFieldMetadata() {
-    $fields = ['mailing_job_start_date'];
+    $fields = ['mailing_job_start_date', 'is_archived'];
     $metadata = civicrm_api3('Mailing', 'getfields', [])['values'];
     $metadata = array_merge($metadata, civicrm_api3('MailingJob', 'getfields', [])['values']);
     return array_intersect_key($metadata, array_flip($fields));
diff --git a/civicrm/CRM/Mailing/BAO/TrackableURL.php b/civicrm/CRM/Mailing/BAO/TrackableURL.php
index 86650e3afe3dab47bf4baadd4e46f2b0d36a73a2..0113e53bb9b7cbd063c3ff63ed184369f0455515 100644
--- a/civicrm/CRM/Mailing/BAO/TrackableURL.php
+++ b/civicrm/CRM/Mailing/BAO/TrackableURL.php
@@ -76,7 +76,9 @@ class CRM_Mailing_BAO_TrackableURL extends CRM_Mailing_DAO_TrackableURL {
       $urlCache[$mailing_id . $url] = $redirect;
     }
 
-    $returnUrl = CRM_Utils_System::externUrl('extern/url', "u=$id&qid=$queue_id");
+    // This looks silly - calling the hook twice. This smells like an accident. Restoring old cache-based lookup.
+    // $returnUrl = CRM_Utils_System::externUrl('extern/url', "u=$id&qid=$queue_id");
+    $returnUrl = "{$urlCache[$mailing_id . $url]}&qid={$queue_id}";
 
     if ($hrefExists) {
       $returnUrl = "href='{$returnUrl}' rel='nofollow'";
diff --git a/civicrm/CRM/Mailing/DAO/MailingAB.php b/civicrm/CRM/Mailing/DAO/MailingAB.php
index 64aec05fb14c632b55d72d50c0d77e02ef44a863..37fa94d1b996f7735280955d1957e2957399d43f 100644
--- a/civicrm/CRM/Mailing/DAO/MailingAB.php
+++ b/civicrm/CRM/Mailing/DAO/MailingAB.php
@@ -6,7 +6,7 @@
  *
  * Generated from xml/schema/CRM/Mailing/MailingAB.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:74ff2df50144a54a2c5a740187f6a8ca)
+ * (GenCodeChecksum:808ef560b5f6c959cb4f3ceea87f5e38)
  */
 
 /**
@@ -227,6 +227,7 @@ class CRM_Mailing_DAO_MailingAB extends CRM_Core_DAO {
           'type' => CRM_Utils_Type::T_INT,
           'title' => ts('Domain ID'),
           'description' => ts('Which site is this mailing for'),
+          'required' => TRUE,
           'where' => 'civicrm_mailing_abtest.domain_id',
           'table_name' => 'civicrm_mailing_abtest',
           'entity' => 'MailingAB',
diff --git a/civicrm/CRM/Mailing/Event/BAO/Bounce.php b/civicrm/CRM/Mailing/Event/BAO/Bounce.php
index f9056e6f7913568449439b3a8eeda30d85943ad3..57c8c3a99b3eaabaeeda142be55581dc3965816f 100644
--- a/civicrm/CRM/Mailing/Event/BAO/Bounce.php
+++ b/civicrm/CRM/Mailing/Event/BAO/Bounce.php
@@ -224,7 +224,7 @@ class CRM_Mailing_Event_BAO_Bounce extends CRM_Mailing_Event_DAO_Bounce {
 
     $dao->query($query);
 
-    $results = array();
+    $results = [];
 
     while ($dao->fetch()) {
       $url = CRM_Utils_System::url('civicrm/contact/view',
diff --git a/civicrm/CRM/Mailing/Form/Search.php b/civicrm/CRM/Mailing/Form/Search.php
index f2a96717239aadc2f40c0a9945ed8664d8b9bb98..04f321d995b289edbc67a2088a12bc67f20476a7 100644
--- a/civicrm/CRM/Mailing/Form/Search.php
+++ b/civicrm/CRM/Mailing/Form/Search.php
@@ -29,6 +29,9 @@ class CRM_Mailing_Form_Search extends CRM_Core_Form_Search {
     parent::preProcess();
   }
 
+  /**
+   * @throws \CiviCRM_API3_Exception
+   */
   public function buildQuickForm() {
     $parent = $this->controller->getParent();
     $nameTextLabel = ($parent->_sms) ? ts('SMS Name') : ts('Mailing Name');
@@ -44,6 +47,8 @@ class CRM_Mailing_Form_Search extends CRM_Core_Form_Search {
       CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Contact', 'sort_name')
     );
 
+    CRM_Mailing_BAO_Query::buildSearchForm($this);
+
     CRM_Campaign_BAO_Campaign::addCampaignInComponentSearch($this);
 
     // CRM-15434 - Fix mailing search by status in non-English languages
@@ -52,7 +57,6 @@ class CRM_Mailing_Form_Search extends CRM_Core_Form_Search {
       $this->addElement('checkbox', "mailing_status[$statusId]", NULL, $statusName);
     }
     $this->addElement('checkbox', 'status_unscheduled', NULL, ts('Draft / Unscheduled'));
-    $this->addYesNo('is_archived', ts('Mailing is Archived'), TRUE);
 
     // Search by language, if multi-lingual
     $enabledLanguages = CRM_Core_I18n::languages(TRUE);
@@ -77,9 +81,11 @@ class CRM_Mailing_Form_Search extends CRM_Core_Form_Search {
 
   /**
    * @return array
+   * @throws \CRM_Core_Exception
    */
   public function setDefaultValues() {
     $defaults = $statusVals = [];
+    $entityDefaults = parent::setDefaultValues();
     $parent = $this->controller->getParent();
 
     if ($parent->get('unscheduled')) {
@@ -99,11 +105,15 @@ class CRM_Mailing_Form_Search extends CRM_Core_Form_Search {
     if ($parent->_sms) {
       $defaults['sms'] = 1;
     }
-    return $defaults;
+    return array_merge($defaults, $entityDefaults);
   }
 
+  /**
+   * @throws \CRM_Core_Exception
+   */
   public function postProcess() {
-    $params = $this->controller->exportValues($this->_name);
+    $this->setFormValues();
+    $params = $this->_formValues;
 
     if (!empty($params['mailing_relative'])) {
       list($params['mailing_low'], $params['mailing_high']) = CRM_Utils_Date::getFromTo($params['mailing_relative'], $params['mailing_low'], $params['mailing_high']);
@@ -141,4 +151,26 @@ class CRM_Mailing_Form_Search extends CRM_Core_Form_Search {
     }
   }
 
+  /**
+   * Handle force=1 in the url.
+   *
+   * Search field metadata is normally added in buildForm but we are bypassing that in this flow
+   * (I've always found the flow kinda confusing & perhaps that is the problem but this mitigates)
+   *
+   * @throws \CiviCRM_API3_Exception
+   */
+  protected function handleForcedSearch() {
+    $this->setSearchMetadata();
+    $this->addContactSearchFields();
+  }
+
+  /**
+   * Set the metadata for the form.
+   *
+   * @throws \CiviCRM_API3_Exception
+   */
+  protected function setSearchMetadata() {
+    $this->addSearchFieldMetadata(['Mailing' => CRM_Mailing_BAO_Query::getSearchFieldMetadata()]);
+  }
+
 }
diff --git a/civicrm/CRM/Mailing/Form/Unsubscribe.php b/civicrm/CRM/Mailing/Form/Unsubscribe.php
index 4f2c4f78747be87579de2e9c831513ec5ad13c69..95a675965955cdc94a9d15d1de94864963300984 100644
--- a/civicrm/CRM/Mailing/Form/Unsubscribe.php
+++ b/civicrm/CRM/Mailing/Form/Unsubscribe.php
@@ -16,6 +16,14 @@
  */
 class CRM_Mailing_Form_Unsubscribe extends CRM_Core_Form {
 
+  /**
+   * Prevent people double-submitting the form (e.g. by double-clicking).
+   * https://lab.civicrm.org/dev/core/-/issues/1773
+   *
+   * @var bool
+   */
+  public $submitOnce = TRUE;
+
   public function preProcess() {
 
     $this->_type = 'unsubscribe';
diff --git a/civicrm/CRM/Mailing/Page/Browse.php b/civicrm/CRM/Mailing/Page/Browse.php
index 769919e7b3c653c8bfa87a171080bfc8db57e9ac..9982bd1a79b52560a07b1398d4a73132edd9e5e0 100644
--- a/civicrm/CRM/Mailing/Page/Browse.php
+++ b/civicrm/CRM/Mailing/Page/Browse.php
@@ -64,7 +64,7 @@ class CRM_Mailing_Page_Browse extends CRM_Core_Page {
   public function preProcess() {
     Civi::resources()->addStyleFile('civicrm', 'css/searchForm.css', 1, 'html-header');
 
-    $this->_unscheduled = $this->_archived = $archiveLinks = FALSE;
+    $this->_unscheduled = $archiveLinks = FALSE;
     $this->_mailingId = CRM_Utils_Request::retrieve('mid', 'Positive', $this);
     $this->_sms = CRM_Utils_Request::retrieve('sms', 'Positive', $this);
 
@@ -119,6 +119,7 @@ class CRM_Mailing_Page_Browse extends CRM_Core_Page {
     $newArgs = func_get_args();
     // since we want only first function argument
     $newArgs = $newArgs[0];
+    $this->_isArchived = $this->isArchived($newArgs);
     if (isset($_GET['runJobs']) || CRM_Utils_Array::value('2', $newArgs) == 'queue') {
       $mailerJobSize = Civi::settings()->get('mailerJobSize');
       CRM_Mailing_BAO_MailingJob::runJobs_pre($mailerJobSize);
@@ -140,10 +141,7 @@ class CRM_Mailing_Page_Browse extends CRM_Core_Page {
     }
     $this->set('unscheduled', $this->_unscheduled);
 
-    if (CRM_Utils_Array::value(3, $newArgs) == 'archived') {
-      $this->_archived = TRUE;
-    }
-    $this->set('archived', $this->_archived);
+    $this->set('archived', $this->isArchived($newArgs));
 
     if (CRM_Utils_Array::value(3, $newArgs) == 'scheduled') {
       $this->_scheduled = TRUE;
@@ -260,7 +258,8 @@ class CRM_Mailing_Page_Browse extends CRM_Core_Page {
       $urlParams .= '&scheduled=false';
       $this->assign('unscheduled', TRUE);
     }
-    elseif (CRM_Utils_Array::value(3, $newArgs) == 'archived') {
+
+    if ($this->isArchived($newArgs)) {
       $urlString .= '/archived';
       $this->assign('archived', TRUE);
     }
@@ -353,4 +352,17 @@ class CRM_Mailing_Page_Browse extends CRM_Core_Page {
     return implode(' AND ', $clauses);
   }
 
+  /**
+   * Is the search limited to archived mailings.
+   *
+   * @param array $urlArguments
+   *
+   * @return bool
+   *
+   * @throws \CRM_Core_Exception
+   */
+  protected function isArchived($urlArguments): bool {
+    return in_array('archived', $urlArguments, TRUE) || CRM_Utils_Request::retrieveValue('is_archived', 'Boolean');
+  }
+
 }
diff --git a/civicrm/CRM/Mailing/Page/Common.php b/civicrm/CRM/Mailing/Page/Common.php
index 5a39c63f3cfec8b66fb11b791c621375d45d6726..4bae016690a9bd3523ead06a9c5660201663395c 100644
--- a/civicrm/CRM/Mailing/Page/Common.php
+++ b/civicrm/CRM/Mailing/Page/Common.php
@@ -43,17 +43,13 @@ class CRM_Mailing_Page_Common extends CRM_Core_Page {
       throw new CRM_Core_Exception(ts("There was an error in your request"));
     }
 
-    $cancel = CRM_Utils_Request::retrieve("_qf_{$this->_type}_cancel", 'String', CRM_Core_DAO::$_nullObject,
-      FALSE, NULL, $_REQUEST
-    );
+    $cancel = CRM_Utils_Request::retrieve("_qf_{$this->_type}_cancel", 'String');
     if ($cancel) {
       $config = CRM_Core_Config::singleton();
       CRM_Utils_System::redirect($config->userFrameworkBaseURL);
     }
 
-    $confirm = CRM_Utils_Request::retrieve('confirm', 'Boolean', CRM_Core_DAO::$_nullObject,
-      FALSE, NULL, $_REQUEST
-    );
+    $confirm = CRM_Utils_Request::retrieve('confirm', 'Boolean');
 
     list($displayName, $email) = CRM_Mailing_Event_BAO_Queue::getContactInfo($queue_id);
     $this->assign('display_name', $displayName);
diff --git a/civicrm/CRM/Mailing/Page/Open.php b/civicrm/CRM/Mailing/Page/Open.php
new file mode 100644
index 0000000000000000000000000000000000000000..ac26fbfff8f61b9143025c945fe8b80521903973
--- /dev/null
+++ b/civicrm/CRM/Mailing/Page/Open.php
@@ -0,0 +1,58 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ */
+
+/**
+ * Indicate that a CiviMail message has been opened
+ *
+ * General Usage: civicrm/mailing/open?qid={event_queue_id}
+ *
+ * NOTE: The parameter name has changed slightly from 'extern/open.php?q={event_queue_id}`.
+ */
+class CRM_Mailing_Page_Open extends CRM_Core_Page {
+
+  /**
+   * Mark the mailing as opened
+   *
+   * @throws \CRM_Core_Exception
+   */
+  public function run() {
+    $queue_id = CRM_Utils_Request::retrieveValue('qid', 'Positive', NULL, FALSE, 'GET');
+    if (!$queue_id) {
+      // Deprecated: "?q=" is problematic in Drupal integrations, but we'll accept if igiven
+      $queue_id = CRM_Utils_Request::retrieveValue('q', 'Positive', NULL, FALSE, 'GET');;
+    }
+    if (!$queue_id) {
+      echo "Missing input parameters\n";
+      exit();
+    }
+
+    CRM_Mailing_Event_BAO_Opened::open($queue_id);
+
+    $filename = Civi::paths()->getPath('[civicrm.root]/i/tracker.gif');
+
+    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+    header('Content-Description: File Transfer');
+    header('Content-type: image/gif');
+    header('Content-Length: ' . filesize($filename));
+    header('Content-Disposition: inline; filename=tracker.gif');
+
+    readfile($filename);
+
+    CRM_Utils_System::civiExit();
+  }
+
+}
diff --git a/civicrm/CRM/Mailing/Page/Url.php b/civicrm/CRM/Mailing/Page/Url.php
new file mode 100644
index 0000000000000000000000000000000000000000..29be68c661101efbe3f9f91ec9563c3c7e30256c
--- /dev/null
+++ b/civicrm/CRM/Mailing/Page/Url.php
@@ -0,0 +1,104 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ */
+
+/**
+ * Redirects a user to the full url from a mailing url.
+ *
+ * General Usage: civicrm/mailing/url?qid={event_queue_id}&u={url_id}
+ *
+ * Additional arguments may be handled by extractPassthroughParameters().
+ */
+class CRM_Mailing_Page_Url extends CRM_Core_Page {
+
+  /**
+   * Redirect the user to the specified url.
+   *
+   * @throws \CRM_Core_Exception
+   */
+  public function run() {
+    $queue_id = CRM_Utils_Request::retrieveValue('qid', 'Integer');
+    $url_id = CRM_Utils_Request::retrieveValue('u', 'Integer', NULL, TRUE);
+    $url = CRM_Mailing_Event_BAO_TrackableURLOpen::track($queue_id, $url_id);
+    $query_string = $this->extractPassthroughParameters();
+
+    if (strlen($query_string) > 0) {
+      // Parse the url to preserve the fragment.
+      $pieces = parse_url($url);
+
+      if (isset($pieces['fragment'])) {
+        $url = str_replace('#' . $pieces['fragment'], '', $url);
+      }
+
+      // Handle additional query string params.
+      if ($query_string) {
+        if (stristr($url, '?')) {
+          $url .= '&' . $query_string;
+        }
+        else {
+          $url .= '?' . $query_string;
+        }
+      }
+
+      // slap the fragment onto the end per URL spec
+      if (isset($pieces['fragment'])) {
+        $url .= '#' . $pieces['fragment'];
+      }
+    }
+    CRM_Utils_System::redirect($url, [
+      'for' => 'civicrm/mailing/url',
+      'queue_id' => $queue_id,
+      'url_id' => $url_id,
+    ]);
+  }
+
+  /**
+   * Determine if this request has any valid pass-through parameters.
+   *
+   * Under CRM-7103 (v3.3), all unrecognized query-parameters (besides qid/u) are passed
+   * through as part of the redirect. This mechanism is relevant to certain
+   * customizations (eg using `hook_alterMailParams` to append extra URL args)
+   * but does not matter for normal URLs.
+   *
+   * The functionality seems vaguely problematic (IMHO) - especially now that
+   * 'extern/url.php' is moving into the CMS/Civi router ('civicrm/mailing/url').
+   * But it's the current protocol.
+   *
+   * A better design might be to support `hook_alterRedirect` in the CiviMail
+   * click-through tracking. Then you don't have to take any untrusted inputs
+   * and you can fix URL mistakes in realtime.
+   *
+   * @return string
+   * @link https://issues.civicrm.org/jira/browse/CRM-7103
+   */
+  protected function extractPassthroughParameters():string {
+    $config = CRM_Core_Config::singleton();
+
+    $query_param = $_GET;
+    unset($query_param['qid']);
+    unset($query_param['u']);
+    unset($query_param[$config->userFrameworkURLVar]);
+    if ($config->userFramework === 'WordPress') {
+      // Ugh
+      unset($query_param['page']);
+      unset($query_param['noheader']);
+    }
+
+    $query_string = http_build_query($query_param);
+    return $query_string;
+  }
+
+}
diff --git a/civicrm/CRM/Mailing/xml/Menu/Mailing.xml b/civicrm/CRM/Mailing/xml/Menu/Mailing.xml
index 639406b8af87326734ec140498bdc722289d2082..d4cc7d274ae72f7f582c099917f90cc4a78eefdd 100644
--- a/civicrm/CRM/Mailing/xml/Menu/Mailing.xml
+++ b/civicrm/CRM/Mailing/xml/Menu/Mailing.xml
@@ -17,7 +17,6 @@
     <desc>Configure spool period, throttling and other mailer settings.</desc>
     <access_arguments>access CiviCRM,access CiviMail</access_arguments>
     <adminGroup>CiviMail</adminGroup>
-    <icon>admin/small/07.png</icon>
     <weight>400</weight>
   </item>
   <item>
@@ -27,7 +26,6 @@
     <desc>Configure the header and footer used for mailings. Customize the content of automated Subscribe, Unsubscribe, Resubscribe and Opt-out messages.</desc>
     <access_arguments>access CiviCRM,access CiviMail</access_arguments>
     <adminGroup>CiviMail</adminGroup>
-    <icon>admin/small/Profile.png</icon>
     <weight>410</weight>
   </item>
   <item>
@@ -36,7 +34,6 @@
     <desc>List of Email Addresses which can be used when sending emails to contacts.</desc>
     <page_callback>CRM_Admin_Page_Options</page_callback>
     <adminGroup>CiviMail</adminGroup>
-    <icon>admin/small/title.png</icon>
     <weight>415</weight>
   </item>
   <item>
@@ -46,7 +43,6 @@
     <desc>Configure email account setting.</desc>
     <access_arguments>access CiviCRM,access CiviMail</access_arguments>
     <adminGroup>CiviMail</adminGroup>
-    <icon>admin/small/07.png</icon>
     <weight>420</weight>
   </item>
   <item>
@@ -206,4 +202,14 @@
     <page_callback>CRM_Mailing_Page_AJAX::getContactMailings</page_callback>
     <access_arguments>access CiviCRM</access_arguments>
   </item>
+  <item>
+    <path>civicrm/mailing/url</path>
+    <page_callback>CRM_Mailing_Page_Url</page_callback>
+    <access_arguments>*always allow*</access_arguments>
+  </item>
+  <item>
+    <path>civicrm/mailing/open</path>
+    <page_callback>CRM_Mailing_Page_Open</page_callback>
+    <access_arguments>*always allow*</access_arguments>
+  </item>
 </menu>
diff --git a/civicrm/CRM/Member/BAO/MembershipType.php b/civicrm/CRM/Member/BAO/MembershipType.php
index 8a56e9bde509f4f7577993002007d10d4a96ae09..09b3eef0c4d44b6c89843b7da7af3acf475fc79f 100644
--- a/civicrm/CRM/Member/BAO/MembershipType.php
+++ b/civicrm/CRM/Member/BAO/MembershipType.php
@@ -740,7 +740,6 @@ class CRM_Member_BAO_MembershipType extends CRM_Member_DAO_MembershipType {
       if (!empty($results)) {
         $results['label'] = $results['name'] = $params['name'];
         $results['amount'] = empty($params['minimum_fee']) ? 0 : $params['minimum_fee'];
-        $optionsIds['id'] = $results['id'];
       }
       else {
         $results = [
@@ -756,12 +755,12 @@ class CRM_Member_BAO_MembershipType extends CRM_Member_DAO_MembershipType {
       if ($previousID) {
         CRM_Member_Form_MembershipType::checkPreviousPriceField($previousID, $priceSetId, $membershipTypeId, $optionsIds);
         if (!empty($optionsIds['option_id'])) {
-          $optionsIds['id'] = current(CRM_Utils_Array::value('option_id', $optionsIds));
+          $results['id'] = current($optionsIds['option_id']);
         }
       }
       $results['financial_type_id'] = $params['financial_type_id'] ?? NULL;
       $results['description'] = $params['description'] ?? NULL;
-      CRM_Price_BAO_PriceFieldValue::add($results, $optionsIds);
+      CRM_Price_BAO_PriceFieldValue::add($results);
     }
   }
 
diff --git a/civicrm/CRM/Member/DAO/MembershipType.php b/civicrm/CRM/Member/DAO/MembershipType.php
index 58c07d9bd7a753d53a94835fbf32ea245ac91fbf..9e51fd1fcc3c2bf6fde34de7da47c6d7989b93f3 100644
--- a/civicrm/CRM/Member/DAO/MembershipType.php
+++ b/civicrm/CRM/Member/DAO/MembershipType.php
@@ -6,7 +6,7 @@
  *
  * Generated from xml/schema/CRM/Member/MembershipType.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:c188b3772bd7ba843454e591a1fb0fd6)
+ * (GenCodeChecksum:c75161ef1844cf9a3977d458f0cb515b)
  */
 
 /**
@@ -237,6 +237,7 @@ class CRM_Member_DAO_MembershipType extends CRM_Core_DAO {
           'type' => CRM_Utils_Type::T_STRING,
           'title' => ts('Membership Type'),
           'description' => ts('Name of Membership Type'),
+          'required' => TRUE,
           'maxlength' => 128,
           'size' => CRM_Utils_Type::HUGE,
           'import' => TRUE,
diff --git a/civicrm/CRM/Member/Form.php b/civicrm/CRM/Member/Form.php
index a2e2ec3aa0a29ad1a01f2966dfe369db3c54845a..73f7cb46a915db91f77e033d904794c03f3b7b38 100644
--- a/civicrm/CRM/Member/Form.php
+++ b/civicrm/CRM/Member/Form.php
@@ -356,27 +356,14 @@ class CRM_Member_Form extends CRM_Contribute_Form_AbstractEditPayment {
   /**
    * Create a recurring contribution record.
    *
-   * Recurring contribution parameters are set explicitly rather than merging paymentParams because it's hard
-   * to know the downstream impacts if we keep passing around the same array.
+   * @param array $contributionRecurParams
    *
-   * @param $paymentParams
+   * @param int $membershipID
    *
    * @return array
    * @throws \CiviCRM_API3_Exception
    */
-  protected function processRecurringContribution($paymentParams) {
-    $membershipID = $paymentParams['membership_type_id'][1];
-    $contributionRecurParams = [
-      'contact_id' => $paymentParams['contactID'],
-      'amount' => $paymentParams['total_amount'],
-      'contribution_status_id' => 'Pending',
-      'payment_processor_id' => $paymentParams['payment_processor_id'],
-      'campaign_id' => $paymentParams['campaign_id'],
-      'financial_type_id' => $paymentParams['financial_type_id'],
-      'is_email_receipt' => $paymentParams['is_email_receipt'],
-      'payment_instrument_id' => $paymentParams['payment_instrument_id'],
-      'invoice_id' => $paymentParams['invoice_id'],
-    ];
+  protected function processRecurringContribution($contributionRecurParams, $membershipID) {
 
     $mapping = [
       'frequency_interval' => 'duration_interval',
diff --git a/civicrm/CRM/Member/Form/Membership.php b/civicrm/CRM/Member/Form/Membership.php
index 412b6d27ee871198fdef4f0062143e53b49503d2..dd4d948daae327e6681f16760f6d3369a6d1ed06 100644
--- a/civicrm/CRM/Member/Form/Membership.php
+++ b/civicrm/CRM/Member/Form/Membership.php
@@ -357,20 +357,12 @@ class CRM_Member_Form_Membership extends CRM_Member_Form {
     }
     $this->assign('alreadyAutoRenew', $alreadyAutoRenew);
 
-    $this->assign('member_is_test', CRM_Utils_Array::value('member_is_test', $defaults));
+    $this->assign('member_is_test', $defaults['member_is_test'] ?? NULL);
+    $this->assign('membership_status_id', $defaults['status_id'] ?? NULL);
+    $this->assign('is_pay_later', !empty($defaults['is_pay_later']));
 
-    $this->assign('membership_status_id', CRM_Utils_Array::value('status_id', $defaults));
-
-    if (!empty($defaults['is_pay_later'])) {
-      $this->assign('is_pay_later', TRUE);
-    }
     if ($this->_mode) {
       $defaults = $this->getBillingDefaults($defaults);
-      // hack to simplify credit card entry for testing
-      // $defaults['credit_card_type']     = 'Visa';
-      // $defaults['credit_card_number']   = '4807731747657838';
-      // $defaults['cvv2']                 = '000';
-      // $defaults['credit_card_exp_date'] = array( 'Y' => '2012', 'M' => '05' );
     }
 
     //setting default join date if there is no join date
@@ -925,7 +917,7 @@ class CRM_Member_Form_Membership extends CRM_Member_Form {
    * or FALSE otherwise.
    */
   private function convertIsOverrideValue() {
-    $this->_params['is_override'] = CRM_Member_StatusOverrideTypes::isOverridden($this->_params['is_override']);
+    $this->_params['is_override'] = CRM_Member_StatusOverrideTypes::isOverridden($this->_params['is_override'] ?? CRM_Member_StatusOverrideTypes::NO);
   }
 
   /**
@@ -1098,8 +1090,6 @@ class CRM_Member_Form_Membership extends CRM_Member_Form {
       $formValues['financial_type_id'] = $this->_priceSet['financial_type_id'];
     }
 
-    $config = CRM_Core_Config::singleton();
-
     $membershipTypeValues = [];
     foreach ($this->_memTypeSelected as $memType) {
       $membershipTypeValues[$memType]['membership_type_id'] = $memType;
@@ -1335,7 +1325,7 @@ class CRM_Member_Form_Membership extends CRM_Member_Form {
       // add all the additional payment params we need
       $formValues['amount'] = $params['total_amount'];
       // @todo this is a candidate for beginPostProcessFunction.
-      $formValues['currencyID'] = $config->defaultCurrency;
+      $formValues['currencyID'] = CRM_Core_Config::singleton()->defaultCurrency;
       $formValues['description'] = ts("Contribution submitted by a staff person using member's credit card for signup");
       $formValues['invoiceID'] = md5(uniqid(rand(), TRUE));
       $formValues['financial_type_id'] = $params['financial_type_id'];
@@ -1456,7 +1446,7 @@ class CRM_Member_Form_Membership extends CRM_Member_Form {
       $params['contribution_source'] = ts('%1 Membership Signup: Credit card or direct debit (by %2)',
         [1 => $membershipType, 2 => $userName]
       );
-      $params['source'] = $formValues['source'] ? $formValues['source'] : $params['contribution_source'];
+      $params['source'] = $formValues['source'] ?: $params['contribution_source'];
       $params['trxn_id'] = $result['trxn_id'] ?? NULL;
       $params['is_test'] = ($this->_mode === 'live') ? 0 : 1;
       if (!empty($formValues['send_receipt'])) {
@@ -1856,7 +1846,7 @@ class CRM_Member_Form_Membership extends CRM_Member_Form {
       ]);
 
       $membership = $createdMemberships[$memType];
-      $memEndDate = ($membership->end_date) ? $membership->end_date : $endDate;
+      $memEndDate = $membership->end_date ?: $endDate;
 
       //get the end date from calculated dates.
       if (!$memEndDate && !$isRecur) {
diff --git a/civicrm/CRM/Member/Form/MembershipRenewal.php b/civicrm/CRM/Member/Form/MembershipRenewal.php
index 04287f6bfe5691e53be33e0f10af35a8dfff54ae..8f688e9b555b06c28343194107e83b1435ab8ce1 100644
--- a/civicrm/CRM/Member/Form/MembershipRenewal.php
+++ b/civicrm/CRM/Member/Form/MembershipRenewal.php
@@ -537,20 +537,30 @@ class CRM_Member_Form_MembershipRenewal extends CRM_Member_Form {
       if (!empty($this->_params['send_receipt'])) {
         $paymentParams['email'] = $this->_contributorEmail;
       }
-      $paymentParams['is_email_receipt'] = !empty($this->_params['send_receipt']);
 
       $paymentParams['contactID'] = $this->_contributorContactID;
 
       CRM_Core_Payment_Form::mapParams($this->_bltID, $this->_params, $paymentParams, TRUE);
 
-      $payment = $this->_paymentProcessor['object'];
-
       if (!empty($this->_params['auto_renew'])) {
-        $contributionRecurParams = $this->processRecurringContribution($paymentParams);
+
+        $contributionRecurParams = $this->processRecurringContribution([
+          'contact_id' => $this->_contributorContactID,
+          'amount' => $this->_params['total_amount'],
+          'contribution_status_id' => 'Pending',
+          'payment_processor_id' => $this->_params['payment_processor_id'],
+          'campaign_id' => $this->_params['campaign_id'],
+          'financial_type_id' => $this->_params['financial_type_id'],
+          'is_email_receipt' => !empty($this->_params['send_receipt']),
+          'payment_instrument_id' => $this->_params['payment_instrument_id'],
+          'invoice_id' => $this->_params['invoice_id'],
+        ], $membershipID = $paymentParams['membership_type_id'][1]);
+
         $contributionRecurID = $contributionRecurParams['contributionRecurID'];
         $paymentParams = array_merge($paymentParams, $contributionRecurParams);
       }
 
+      $payment = $this->_paymentProcessor['object'];
       $result = $payment->doPayment($paymentParams);
       $this->_params = array_merge($this->_params, $result);
 
@@ -585,11 +595,10 @@ class CRM_Member_Form_MembershipRenewal extends CRM_Member_Form {
       $membershipSource = $this->_params['membership_source'];
     }
 
-    // @todo Move this into CRM_Member_BAO_Membership::processMembership
     $pending = ($this->_params['contribution_status_id'] == CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending'));
-    list($membership) = CRM_Member_BAO_Membership::processMembership(
+    $membership = $this->processMembership(
       $this->_contactID, $this->_params['membership_type_id'][1], $isTestMembership,
-      $renewalDate, NULL, $customFieldsFormatted, $numRenewTerms, $this->_membershipId,
+      $renewalDate, $customFieldsFormatted, $numRenewTerms, $this->_membershipId,
       $pending,
       $contributionRecurID, $membershipSource, $this->_params['is_pay_later'], CRM_Utils_Array::value('campaign_id',
       $this->_params)
@@ -725,4 +734,184 @@ class CRM_Member_Form_MembershipRenewal extends CRM_Member_Form {
     );
   }
 
+  /**
+   * Process membership.
+   *
+   * This is duplicated from the BAO class - on the basis that it's actually easier to divide & conquer when
+   * it comes to clearing up really bad code.
+   *
+   * @param int $contactID
+   * @param int $membershipTypeID
+   * @param bool $is_test
+   * @param string $changeToday
+   * @param $customFieldsFormatted
+   * @param $numRenewTerms
+   * @param int $membershipID
+   * @param $pending
+   * @param int $contributionRecurID
+   * @param $membershipSource
+   * @param $isPayLater
+   * @param int $campaignId
+   *
+   * @return CRM_Member_BAO_Membership
+   * @throws \CRM_Core_Exception
+   * @throws \CiviCRM_API3_Exception
+   */
+  public function processMembership($contactID, $membershipTypeID, $is_test, $changeToday, $customFieldsFormatted, $numRenewTerms, $membershipID, $pending, $contributionRecurID, $membershipSource, $isPayLater, $campaignId) {
+    $updateStatusId = FALSE;
+    $allStatus = CRM_Member_PseudoConstant::membershipStatus();
+    $format = '%Y%m%d';
+    $membershipTypeDetails = CRM_Member_BAO_MembershipType::getMembershipTypeDetails($membershipTypeID);
+    $ids = [];
+
+    // CRM-7297 - allow membership type to be be changed during renewal so long as the parent org of new membershipType
+    // is the same as the parent org of an existing membership of the contact
+    $currentMembership = CRM_Member_BAO_Membership::getContactMembership($contactID, $membershipTypeID,
+      $is_test, $membershipID, TRUE
+    );
+
+    // Do NOT do anything.
+    //1. membership with status : PENDING/CANCELLED (CRM-2395)
+    //2. Paylater/IPN renew. CRM-4556.
+    if ($pending || in_array($currentMembership['status_id'], [
+      array_search('Pending', $allStatus),
+      // CRM-15475
+      array_search('Cancelled', CRM_Member_PseudoConstant::membershipStatus(NULL, " name = 'Cancelled' ", 'name', FALSE, TRUE)),
+    ])) {
+
+      $memParams = [
+        'id' => $currentMembership['id'],
+        'status_id' => $currentMembership['status_id'],
+        'start_date' => $currentMembership['start_date'],
+        'end_date' => $currentMembership['end_date'],
+        'join_date' => $currentMembership['join_date'],
+        'membership_type_id' => $membershipTypeID,
+        'max_related' => !empty($membershipTypeDetails['max_related']) ? $membershipTypeDetails['max_related'] : NULL,
+        'membership_activity_status' => ($pending || $isPayLater) ? 'Scheduled' : 'Completed',
+      ];
+      if ($contributionRecurID) {
+        $memParams['contribution_recur_id'] = $contributionRecurID;
+      }
+      // @todo stop passing $ids - it is empty
+      return CRM_Member_BAO_Membership::create($memParams, $ids);
+    }
+
+    // Check and fix the membership if it is STALE
+    CRM_Member_BAO_Membership::fixMembershipStatusBeforeRenew($currentMembership, $changeToday);
+
+    // Now Renew the membership
+    if (!$currentMembership['is_current_member']) {
+      // membership is not CURRENT
+
+      // CRM-7297 Membership Upsell - calculate dates based on new membership type
+      $dates = CRM_Member_BAO_MembershipType::getRenewalDatesForMembershipType($currentMembership['id'],
+        $changeToday,
+        $membershipTypeID,
+        $numRenewTerms
+      );
+
+      $currentMembership['join_date'] = CRM_Utils_Date::customFormat($currentMembership['join_date'], $format);
+      foreach (['start_date', 'end_date'] as $dateType) {
+        $currentMembership[$dateType] = $dates[$dateType] ?? NULL;
+      }
+      $currentMembership['is_test'] = $is_test;
+
+      if (!empty($membershipSource)) {
+        $currentMembership['source'] = $membershipSource;
+      }
+      else {
+        $currentMembership['source'] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_Membership',
+          $currentMembership['id'],
+          'source'
+        );
+      }
+
+      if (!empty($currentMembership['id'])) {
+        $ids['membership'] = $currentMembership['id'];
+      }
+      $memParams = $currentMembership;
+      $memParams['membership_type_id'] = $membershipTypeID;
+
+      //set the log start date.
+      $memParams['log_start_date'] = CRM_Utils_Date::customFormat($dates['log_start_date'], $format);
+    }
+    else {
+
+      // CURRENT Membership
+      $membership = new CRM_Member_DAO_Membership();
+      $membership->id = $currentMembership['id'];
+      $membership->find(TRUE);
+      // CRM-7297 Membership Upsell - calculate dates based on new membership type
+      $dates = CRM_Member_BAO_MembershipType::getRenewalDatesForMembershipType($membership->id,
+        $changeToday,
+        $membershipTypeID,
+        $numRenewTerms
+      );
+
+      // Insert renewed dates for CURRENT membership
+      $memParams = [];
+      $memParams['join_date'] = $membership->join_date;
+      $memParams['start_date'] = $membership->start_date;
+      $memParams['end_date'] = $dates['end_date'] ?? NULL;
+      $memParams['membership_type_id'] = $membershipTypeID;
+
+      //set the log start date.
+      $memParams['log_start_date'] = CRM_Utils_Date::customFormat($dates['log_start_date'], $format);
+
+      //CRM-18067
+      if (!empty($membershipSource)) {
+        $memParams['source'] = $membershipSource;
+      }
+      elseif (empty($membership->source)) {
+        $memParams['source'] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_Membership',
+          $currentMembership['id'],
+          'source'
+        );
+      }
+
+      if (!empty($currentMembership['id'])) {
+        $ids['membership'] = $currentMembership['id'];
+      }
+      $memParams['membership_activity_status'] = ($pending || $isPayLater) ? 'Scheduled' : 'Completed';
+    }
+    //CRM-4555
+    if ($pending) {
+      $updateStatusId = array_search('Pending', $allStatus);
+    }
+
+    // Putting this in an IF is precautionary as it seems likely that it would be ignored if empty, but
+    // perhaps shouldn't be?
+    if ($contributionRecurID) {
+      $memParams['contribution_recur_id'] = $contributionRecurID;
+    }
+    //CRM-4555
+    //if we decided status here and want to skip status
+    //calculation in create( ); then need to pass 'skipStatusCal'.
+    if ($updateStatusId) {
+      $memParams['status_id'] = $updateStatusId;
+      $memParams['skipStatusCal'] = TRUE;
+    }
+
+    //since we are renewing,
+    //make status override false.
+    $memParams['is_override'] = FALSE;
+
+    $params['modified_id'] = $contactID;
+
+    //inherit campaign from contrib page.
+    if (isset($campaignId)) {
+      $memParams['campaign_id'] = $campaignId;
+    }
+
+    $memParams['custom'] = $customFieldsFormatted;
+    // @todo stop passing $ids (membership and userId may be set by this point)
+    $membership = CRM_Member_BAO_Membership::create($memParams, $ids);
+
+    // not sure why this statement is here, seems quite odd :( - Lobo: 12/26/2010
+    // related to: http://forum.civicrm.org/index.php/topic,11416.msg49072.html#msg49072
+    $membership->find(TRUE);
+
+    return $membership;
+  }
+
 }
diff --git a/civicrm/CRM/Member/Import/Form/MapField.php b/civicrm/CRM/Member/Import/Form/MapField.php
index 1d537f7bcb24c624bea6028f724267a2518207b8..e7c110692f8c87352cd46e66d70ec75c951d4b5d 100644
--- a/civicrm/CRM/Member/Import/Form/MapField.php
+++ b/civicrm/CRM/Member/Import/Form/MapField.php
@@ -47,7 +47,7 @@ class CRM_Member_Import_Form_MapField extends CRM_Import_Form_MapField {
     $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader');
     $this->_onDuplicate = $this->get('onDuplicate', $onDuplicate ?? "");
 
-    $highlightedFields = array();
+    $highlightedFields = [];
     if ($skipColumnHeader) {
       $this->assign('skipColumnHeader', $skipColumnHeader);
       $this->assign('rowDisplayCount', 3);
@@ -120,7 +120,7 @@ class CRM_Member_Import_Form_MapField extends CRM_Import_Form_MapField {
       //mapping is to be loaded from database
 
       $params = array('id' => $savedMapping);
-      $temp = array();
+      $temp = [];
       $mappingDetails = CRM_Core_BAO_Mapping::retrieve($params, $temp);
 
       $this->assign('loadedMapping', $mappingDetails->name);
@@ -150,7 +150,7 @@ class CRM_Member_Import_Form_MapField extends CRM_Import_Form_MapField {
 
     //-------- end of saved mapping stuff ---------
 
-    $defaults = array();
+    $defaults = [];
     $mapperKeys = array_keys($this->_mapperFields);
     $hasHeaders = !empty($this->_columnHeaders);
     $headerPatterns = $this->get('headerPatterns');
@@ -198,7 +198,7 @@ class CRM_Member_Import_Form_MapField extends CRM_Import_Form_MapField {
             $jsSet = TRUE;
           }
           else {
-            $defaults["mapper[$i]"] = array();
+            $defaults["mapper[$i]"] = [];
           }
           if (!$jsSet) {
             for ($k = 1; $k < 4; $k++) {
@@ -293,10 +293,10 @@ class CRM_Member_Import_Form_MapField extends CRM_Import_Form_MapField {
    *   list of errors to be posted back to the form
    */
   public static function formRule($fields, $files, $self) {
-    $errors = array();
+    $errors = [];
 
     if (!array_key_exists('savedMapping', $fields)) {
-      $importKeys = array();
+      $importKeys = [];
       foreach ($fields['mapper'] as $mapperPart) {
         $importKeys[] = $mapperPart[0];
       }
@@ -403,12 +403,12 @@ class CRM_Member_Import_Form_MapField extends CRM_Import_Form_MapField {
     $seperator = $this->controller->exportValue('DataSource', 'fieldSeparator');
     $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader');
 
-    $mapperKeys = array();
-    $mapper = array();
+    $mapperKeys = [];
+    $mapper = [];
     $mapperKeys = $this->controller->exportValue($this->_name, 'mapper');
-    $mapperKeysMain = array();
-    $mapperLocType = array();
-    $mapperPhoneType = array();
+    $mapperKeysMain = [];
+    $mapperLocType = [];
+    $mapperPhoneType = [];
 
     for ($i = 0; $i < $this->_columnCount; $i++) {
       $mapper[$i] = $this->_mapperFields[$mapperKeys[$i][0]];
@@ -441,7 +441,7 @@ class CRM_Member_Import_Form_MapField extends CRM_Import_Form_MapField {
       $mappingFields->mapping_id = $params['mappingId'];
       $mappingFields->find();
 
-      $mappingFieldsId = array();
+      $mappingFieldsId = [];
       while ($mappingFields->fetch()) {
         if ($mappingFields->id) {
           $mappingFieldsId[$mappingFields->column_number] = $mappingFields->id;
diff --git a/civicrm/CRM/Member/Page/DashBoard.php b/civicrm/CRM/Member/Page/DashBoard.php
index 57fb30cda56fb6f0448871adac2319d423471302..8ba2539c1361a95036a7795bb844a86055072f4b 100644
--- a/civicrm/CRM/Member/Page/DashBoard.php
+++ b/civicrm/CRM/Member/Page/DashBoard.php
@@ -40,7 +40,7 @@ class CRM_Member_Page_DashBoard extends CRM_Core_Page {
     }
     $this->assign('membershipSummary', TRUE);
     CRM_Utils_System::setTitle(ts('CiviMember'));
-    $membershipSummary = array();
+    $membershipSummary = [];
     $preMonth = date("Y-m-d", mktime(0, 0, 0, date("m") - 1, 01, date("Y")));
     $preMonthEnd = date("Y-m-t", mktime(0, 0, 0, date("m") - 1, 01, date("Y")));
 
diff --git a/civicrm/CRM/Member/xml/Menu/Member.xml b/civicrm/CRM/Member/xml/Menu/Member.xml
index ffe0eb051ee2bab092fcfec1ef85b1c04f58426b..c8b0dae7f8f249ba958d9b6e19c53bc0b1e9d068 100644
--- a/civicrm/CRM/Member/xml/Menu/Member.xml
+++ b/civicrm/CRM/Member/xml/Menu/Member.xml
@@ -24,7 +24,6 @@
      <page_callback>CRM_Member_Page_MembershipType</page_callback>
      <desc>Define the types of memberships you want to offer. For each type, you can specify a 'name' (Gold Member, Honor Society Member...), a description, duration, and a minimum fee.</desc>
      <adminGroup>CiviMember</adminGroup>
-     <icon>admin/small/membership_type.png</icon>
      <weight>370</weight>
   </item>
   <item>
@@ -33,7 +32,6 @@
      <page_callback>CRM_Member_Page_MembershipStatus</page_callback>
      <desc>Status 'rules' define the current status for a membership based on that membership's start and end dates. You can adjust the default status options and rules as needed to meet your needs.</desc>
      <adminGroup>CiviMember</adminGroup>
-     <icon>admin/small/membership_status.png</icon>
      <weight>380</weight>
   </item>
   <item>
diff --git a/civicrm/CRM/Pledge/BAO/Pledge.php b/civicrm/CRM/Pledge/BAO/Pledge.php
index 4fdc3ec5f656de8e41ffb395d8b79aac1cd6ba6e..1390af09f9a22d17a9b8786ab6284cb75881aa5c 100644
--- a/civicrm/CRM/Pledge/BAO/Pledge.php
+++ b/civicrm/CRM/Pledge/BAO/Pledge.php
@@ -137,7 +137,7 @@ class CRM_Pledge_BAO_Pledge extends CRM_Pledge_DAO_Pledge {
     $isRecalculatePledgePayment = self::isPaymentsRequireRecalculation($params);
     $transaction = new CRM_Core_Transaction();
 
-    $paymentParams = array();
+    $paymentParams = [];
     if (!empty($params['installment_amount'])) {
       $params['amount'] = $params['installment_amount'] * $params['installments'];
     }
@@ -206,7 +206,7 @@ class CRM_Pledge_BAO_Pledge extends CRM_Pledge_DAO_Pledge {
       "action=view&reset=1&id={$pledge->id}&cid={$pledge->contact_id}&context=home"
     );
 
-    $recentOther = array();
+    $recentOther = [];
     if (CRM_Core_Permission::checkActionPermission('CiviPledge', CRM_Core_Action::UPDATE)) {
       $recentOther['editUrl'] = CRM_Utils_System::url('civicrm/contact/view/pledge',
         "action=update&reset=1&id={$pledge->id}&cid={$pledge->contact_id}&context=home"
@@ -330,7 +330,7 @@ class CRM_Pledge_BAO_Pledge extends CRM_Pledge_DAO_Pledge {
    * @return array|null
    */
   public static function getTotalAmountAndCount($status = NULL, $startDate = NULL, $endDate = NULL) {
-    $where = array();
+    $where = [];
     $select = $from = $queryDate = NULL;
     $statusId = CRM_Core_PseudoConstant::getKey('CRM_Pledge_BAO_Pledge', 'status_id', $status);
 
@@ -365,7 +365,7 @@ GROUP BY  currency
     $start = substr($startDate, 0, 8);
     $end = substr($endDate, 0, 8);
     $pCount = 0;
-    $pamount = array();
+    $pamount = [];
     $dao = CRM_Core_DAO::executeQuery($query);
     while ($dao->fetch()) {
       $pCount += $dao->pledge_count;
@@ -380,7 +380,7 @@ GROUP BY  currency
       ),
     );
 
-    $where = array();
+    $where = [];
     switch ($status) {
       case 'Completed':
         $select = 'sum( total_amount ) as received_pledge , count( cd.id ) as received_count';
@@ -428,7 +428,7 @@ GROUP BY  currency
 ";
     if ($select) {
       $dao = CRM_Core_DAO::executeQuery($query);
-      $amount = array();
+      $amount = [];
       $count = 0;
 
       while ($dao->fetch()) {
@@ -462,7 +462,7 @@ GROUP BY  currency
    *   return the list of pledge fields
    */
   public static function getHonorContacts($honorId) {
-    $params = array();
+    $params = [];
     $honorDAO = new CRM_Contribute_DAO_ContributionSoft();
     $honorDAO->contact_id = $honorId;
     $honorDAO->find();
@@ -506,7 +506,7 @@ GROUP BY  currency
    */
   public static function sendAcknowledgment(&$form, $params) {
     //handle Acknowledgment.
-    $allPayments = $payments = array();
+    $allPayments = $payments = [];
 
     // get All Payments status types.
     $paymentStatusTypes = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
@@ -516,7 +516,7 @@ GROUP BY  currency
 
     if (!empty($allPayments)) {
       foreach ($allPayments as $payID => $values) {
-        $contributionValue = $contributionStatus = array();
+        $contributionValue = $contributionStatus = [];
         if (isset($values['contribution_id'])) {
           $contributionParams = array('id' => $values['contribution_id']);
           $returnProperties = array('contribution_status_id', 'receive_date');
@@ -573,7 +573,7 @@ GROUP BY  currency
       'domain' => array('name', 'phone', 'address', 'email'),
       'contact' => CRM_Core_SelectValues::contactTokens(),
     );
-    $domainValues = array();
+    $domainValues = [];
     foreach ($tokens['domain'] as $token) {
       $domainValues[$token] = CRM_Utils_Token::getDomainTokenReplacement($token, $domain);
     }
@@ -599,10 +599,10 @@ GROUP BY  currency
     if (!empty($params['hidden_custom'])) {
       $groupTree = CRM_Core_BAO_CustomGroup::getTree('Pledge', NULL, $params['id']);
       $pledgeParams = array(array('pledge_id', '=', $params['id'], 0, 0));
-      $customGroup = array();
+      $customGroup = [];
       // retrieve custom data
       foreach ($groupTree as $groupID => $group) {
-        $customFields = $customValues = array();
+        $customFields = $customValues = [];
         if ($groupID == 'info') {
           continue;
         }
@@ -708,7 +708,7 @@ GROUP BY  currency
   public static function exportableFields($checkPermission = TRUE) {
     if (!self::$_exportableFields) {
       if (!self::$_exportableFields) {
-        self::$_exportableFields = array();
+        self::$_exportableFields = [];
       }
 
       $fields = CRM_Pledge_DAO_Pledge::export();
@@ -773,12 +773,12 @@ GROUP BY  currency
    *   associated array of pledge id(s)
    */
   public static function getContactPledges($contactID) {
-    $pledgeDetails = array();
+    $pledgeDetails = [];
     $pledgeStatuses = CRM_Core_OptionGroup::values('pledge_status',
       FALSE, FALSE, FALSE, NULL, 'name'
     );
 
-    $status = array();
+    $status = [];
 
     // get pending and in progress status
     foreach (array(
@@ -833,7 +833,7 @@ GROUP BY  currency
    */
   public static function updatePledgeStatus($params) {
 
-    $returnMessages = array();
+    $returnMessages = [];
 
     $sendReminders = CRM_Utils_Array::value('send_reminders', $params, FALSE);
 
@@ -881,7 +881,7 @@ SELECT  pledge.contact_id              as contact_id,
     $dao = CRM_Core_DAO::executeQuery($query);
 
     $now = date('Ymd');
-    $pledgeDetails = $contactIds = $pledgePayments = $pledgeStatus = array();
+    $pledgeDetails = $contactIds = $pledgePayments = $pledgeStatus = [];
     while ($dao->fetch()) {
       $checksumValue = CRM_Contact_BAO_Contact_Utils::generateChecksum($dao->contact_id);
 
@@ -941,7 +941,7 @@ SELECT  pledge.contact_id              as contact_id,
         'contact' => CRM_Core_SelectValues::contactTokens(),
       );
 
-      $domainValues = array();
+      $domainValues = [];
       foreach ($tokens['domain'] as $token) {
         $domainValues[$token] = CRM_Utils_Token::getDomainTokenReplacement($token, $domain);
       }
@@ -1100,7 +1100,7 @@ SELECT  pledge.contact_id              as contact_id,
     ));
     $paymentDAO->find();
 
-    $paymentIDs = array();
+    $paymentIDs = [];
     while ($paymentDAO->fetch()) {
       $paymentIDs[] = $paymentDAO->id;
     }
@@ -1261,7 +1261,7 @@ SELECT  pledge.contact_id              as contact_id,
    *
    * @return array|bool
    */
-  public static function buildOptions($fieldName, $context = NULL, $props = array()) {
+  public static function buildOptions($fieldName, $context = NULL, $props = []) {
     $result = parent::buildOptions($fieldName, $context, $props);
     if ($fieldName == 'status_id') {
       $result = array_diff($result, array('Failed'));
diff --git a/civicrm/CRM/Pledge/BAO/PledgeBlock.php b/civicrm/CRM/Pledge/BAO/PledgeBlock.php
index 8d363e06f9acbef5eac403ca8358b1da8944389b..2672e631cf95ee0c28d205c62e89ee7668aa4170 100644
--- a/civicrm/CRM/Pledge/BAO/PledgeBlock.php
+++ b/civicrm/CRM/Pledge/BAO/PledgeBlock.php
@@ -70,58 +70,18 @@ class CRM_Pledge_BAO_PledgeBlock extends CRM_Pledge_DAO_PledgeBlock {
   }
 
   /**
-   * Add pledgeBlock.
+   * Add or update pledgeBlock.
    *
    * @param array $params
-   *   Reference array contains the values submitted by the form.
-   *
    *
    * @return object
    */
-  public static function add(&$params) {
-
-    if (!empty($params['id'])) {
-      CRM_Utils_Hook::pre('edit', 'PledgeBlock', $params['id'], $params);
-    }
-    else {
-      CRM_Utils_Hook::pre('create', 'PledgeBlock', NULL, $params);
-    }
-
-    $pledgeBlock = new CRM_Pledge_DAO_PledgeBlock();
-
-    // fix for pledge_frequency_unit
-    $freqUnits = $params['pledge_frequency_unit'] ?? NULL;
-
-    if ($freqUnits && is_array($freqUnits)) {
-      unset($params['pledge_frequency_unit']);
-      $newFreqUnits = array();
-      foreach ($freqUnits as $k => $v) {
-        if ($v) {
-          $newFreqUnits[$k] = $v;
-        }
-      }
-
-      $freqUnits = $newFreqUnits;
-      if (is_array($freqUnits) && !empty($freqUnits)) {
-        $freqUnits = implode(CRM_Core_DAO::VALUE_SEPARATOR, array_keys($freqUnits));
-        $pledgeBlock->pledge_frequency_unit = $freqUnits;
-      }
-      else {
-        $pledgeBlock->pledge_frequency_unit = '';
-      }
+  public static function add($params) {
+    // FIXME: This is assuming checkbox input like ['foo' => 1, 'bar' => 0, 'baz' => 1]. Not API friendly.
+    if (!empty($params['pledge_frequency_unit']) && is_array($params['pledge_frequency_unit'])) {
+      $params['pledge_frequency_unit'] = array_keys(array_filter($params['pledge_frequency_unit']));
     }
-
-    $pledgeBlock->copyValues($params);
-    $result = $pledgeBlock->save();
-
-    if (!empty($params['id'])) {
-      CRM_Utils_Hook::post('edit', 'PledgeBlock', $pledgeBlock->id, $pledgeBlock);
-    }
-    else {
-      CRM_Utils_Hook::post('create', 'Pledge', $pledgeBlock->id, $pledgeBlock);
-    }
-
-    return $result;
+    return self::writeRecord($params);
   }
 
   /**
@@ -159,7 +119,7 @@ class CRM_Pledge_BAO_PledgeBlock extends CRM_Pledge_DAO_PledgeBlock {
    * @return array
    */
   public static function getPledgeBlock($pageID) {
-    $pledgeBlock = array();
+    $pledgeBlock = [];
 
     $dao = new CRM_Pledge_DAO_PledgeBlock();
     $dao->entity_table = 'civicrm_contribution_page';
@@ -180,7 +140,7 @@ class CRM_Pledge_BAO_PledgeBlock extends CRM_Pledge_DAO_PledgeBlock {
     //build pledge payment fields.
     if (!empty($form->_values['pledge_id'])) {
       //get all payments required details.
-      $allPayments = array();
+      $allPayments = [];
       $returnProperties = array(
         'status_id',
         'scheduled_date',
@@ -193,9 +153,9 @@ class CRM_Pledge_BAO_PledgeBlock extends CRM_Pledge_DAO_PledgeBlock {
       // get all status
       $allStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
 
-      $nextPayment = array();
+      $nextPayment = [];
       $isNextPayment = FALSE;
-      $overduePayments = array();
+      $overduePayments = [];
       foreach ($allPayments as $payID => $value) {
         if ($allStatus[$value['status_id']] == 'Overdue') {
           $overduePayments[$payID] = array(
@@ -224,7 +184,7 @@ class CRM_Pledge_BAO_PledgeBlock extends CRM_Pledge_DAO_PledgeBlock {
       }
 
       // build check box array for payments.
-      $payments = array();
+      $payments = [];
       if (!empty($overduePayments)) {
         foreach ($overduePayments as $id => $payment) {
           $label = ts("%1 - due on %2 (overdue)", array(
@@ -276,7 +236,7 @@ class CRM_Pledge_BAO_PledgeBlock extends CRM_Pledge_DAO_PledgeBlock {
       }
       // Frequency unit drop-down label suffixes switch from *ly to *(s)
       $freqUnitVals = explode(CRM_Core_DAO::VALUE_SEPARATOR, $pledgeBlock['pledge_frequency_unit']);
-      $freqUnits = array();
+      $freqUnits = [];
       $frequencyUnits = CRM_Core_OptionGroup::values('recur_frequency_units');
       foreach ($freqUnitVals as $key => $val) {
         if (array_key_exists($val, $frequencyUnits)) {
@@ -287,7 +247,7 @@ class CRM_Pledge_BAO_PledgeBlock extends CRM_Pledge_DAO_PledgeBlock {
       // CRM-18854
       if (!empty($pledgeBlock['is_pledge_start_date_visible'])) {
         if (!empty($pledgeBlock['pledge_start_date'])) {
-          $defaults = array();
+          $defaults = [];
           $date = (array) json_decode($pledgeBlock['pledge_start_date']);
           foreach ($date as $field => $value) {
             switch ($field) {
diff --git a/civicrm/CRM/Pledge/BAO/PledgePayment.php b/civicrm/CRM/Pledge/BAO/PledgePayment.php
index 38f947cc69fe9f1b8d8a1b2b08b14f1f03712524..d049f9e55af6191b43b88ea805df822a12fe969d 100644
--- a/civicrm/CRM/Pledge/BAO/PledgePayment.php
+++ b/civicrm/CRM/Pledge/BAO/PledgePayment.php
@@ -154,31 +154,11 @@ WHERE     pledge_id = %1
    *   pledge payment id
    */
   public static function add($params) {
-    if (!empty($params['id'])) {
-      CRM_Utils_Hook::pre('edit', 'PledgePayment', $params['id'], $params);
-    }
-    else {
-      CRM_Utils_Hook::pre('create', 'PledgePayment', NULL, $params);
-    }
-
-    $payment = new CRM_Pledge_DAO_PledgePayment();
-    $payment->copyValues($params);
-
     // set currency for CRM-1496
-    if (!isset($payment->currency)) {
-      $payment->currency = CRM_Core_Config::singleton()->defaultCurrency;
+    if (empty($params['id']) && !isset($params['currency'])) {
+      $params['currency'] = CRM_Core_Config::singleton()->defaultCurrency;
     }
-
-    $result = $payment->save();
-
-    if (!empty($params['id'])) {
-      CRM_Utils_Hook::post('edit', 'PledgePayment', $payment->id, $payment);
-    }
-    else {
-      CRM_Utils_Hook::post('create', 'PledgePayment', $payment->id, $payment);
-    }
-
-    return $result;
+    return self::writeRecord($params);
   }
 
   /**
diff --git a/civicrm/CRM/Price/BAO/LineItem.php b/civicrm/CRM/Price/BAO/LineItem.php
index 516b2c84ec2330c4e39347497a07f06914e3d31f..c974eeb48f3c1a9d105114246181b808c898391d 100644
--- a/civicrm/CRM/Price/BAO/LineItem.php
+++ b/civicrm/CRM/Price/BAO/LineItem.php
@@ -286,7 +286,7 @@ WHERE li.contribution_id = %1";
     }
     if ($invoicing) {
       // @todo - this is an inappropriate place to be doing form level assignments.
-      $taxTerm = $invoiceSettings['tax_term'] ?? NULL;
+      $taxTerm = Civi::settings()->get('tax_term');
       $smarty = CRM_Core_Smarty::singleton();
       $smarty->assign('taxTerm', $taxTerm);
       $smarty->assign('getTaxDetails', $getTaxDetails);
diff --git a/civicrm/CRM/Price/BAO/PriceField.php b/civicrm/CRM/Price/BAO/PriceField.php
index cf3bf99f5f9dcf93ff34bd12d617aec52055b408..091d9810e414e42ca5a8946cfe9612b3aa835ecb 100644
--- a/civicrm/CRM/Price/BAO/PriceField.php
+++ b/civicrm/CRM/Price/BAO/PriceField.php
@@ -9,14 +9,6 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
 /**
  * Business objects for managing price fields.
  *
@@ -306,7 +298,7 @@ class CRM_Price_BAO_PriceField extends CRM_Price_DAO_PriceField {
     $valueFieldName = 'amount';
     $seperator = '|';
     $invoiceSettings = Civi::settings()->get('contribution_invoice_settings');
-    $taxTerm = $invoiceSettings['tax_term'] ?? NULL;
+    $taxTerm = Civi::settings()->get('tax_term');
     $displayOpt = $invoiceSettings['tax_display_settings'] ?? NULL;
     $invoicing = $invoiceSettings['invoicing'] ?? NULL;
     switch ($field->html_type) {
@@ -386,10 +378,10 @@ class CRM_Price_BAO_PriceField extends CRM_Price_DAO_PriceField {
             $opt['label'] = !empty($opt['label']) ? $opt['label'] . '<span class="crm-price-amount-label-separator">&nbsp;-&nbsp;</span>' : '';
             $preHelpText = $postHelpText = '';
             if (!empty($opt['help_pre'])) {
-              $preHelpText = '<span class="crm-price-amount-help-pre description">' . $opt['help_pre'] . '</span>: ';
+              $preHelpText = '<span class="crm-price-amount-help-pre description">' . $opt['help_pre'] . '</span><span class="crm-price-amount-help-pre-separator">:&nbsp;</span>';
             }
             if (!empty($opt['help_post'])) {
-              $postHelpText = ': <span class="crm-price-amount-help-post description">' . $opt['help_post'] . '</span>';
+              $postHelpText = '<span class="crm-price-amount-help-post-separator">:&nbsp;</span><span class="crm-price-amount-help-post description">' . $opt['help_post'] . '</span>';
             }
             if (isset($taxAmount) && $invoicing) {
               if ($displayOpt == 'Do_not_show') {
diff --git a/civicrm/CRM/Price/BAO/PriceFieldValue.php b/civicrm/CRM/Price/BAO/PriceFieldValue.php
index 34d431210a9b6959da5e4b93d29701e9d5c5ff90..7ad8514b754ee96b6276545d4853dc7ba137a023 100644
--- a/civicrm/CRM/Price/BAO/PriceFieldValue.php
+++ b/civicrm/CRM/Price/BAO/PriceFieldValue.php
@@ -26,35 +26,18 @@ class CRM_Price_BAO_PriceFieldValue extends CRM_Price_DAO_PriceFieldValue {
    *
    * @param array $params
    *
-   * @param array $ids
-   *  Deprecated variable.
-   *
    * @return CRM_Price_DAO_PriceFieldValue
    */
-  public static function add(&$params, $ids = []) {
-    $hook = empty($params['id']) ? 'create' : 'edit';
-    CRM_Utils_Hook::pre($hook, 'PriceFieldValue', CRM_Utils_Array::value('id', $params), $params);
-
-    $fieldValueBAO = new CRM_Price_BAO_PriceFieldValue();
-    $fieldValueBAO->copyValues($params);
-
-    // CRM-16189
-    $priceFieldID = $params['price_field_id'] ?? NULL;
-
-    $id = CRM_Utils_Array::value('id', $ids, CRM_Utils_Array::value('id', $params));
+  public static function add($params) {
+    $fieldValueBAO = self::writeRecord($params);
 
-    if (!$priceFieldID) {
-      $priceFieldID = CRM_Core_DAO::getFieldValue('CRM_Price_BAO_PriceFieldValue', $id, 'price_field_id');
-    }
     if (!empty($params['is_default'])) {
+      $priceFieldID = $params['price_field_id'] ?? CRM_Core_DAO::getFieldValue('CRM_Price_BAO_PriceFieldValue', $fieldValueBAO->id, 'price_field_id');
       $query = 'UPDATE civicrm_price_field_value SET is_default = 0 WHERE  price_field_id = %1';
-      $p = [1 => [$params['price_field_id'], 'Integer']];
+      $p = [1 => [$priceFieldID, 'Integer']];
       CRM_Core_DAO::executeQuery($query, $p);
     }
 
-    $fieldValueBAO->save();
-    CRM_Utils_Hook::post($hook, 'PriceFieldValue', $fieldValueBAO->id, $fieldValueBAO);
-
     // Reset the cached values in this function.
     CRM_Price_BAO_PriceField::getOptions(CRM_Utils_Array::value('price_field_id', $params), FALSE, TRUE);
     return $fieldValueBAO;
@@ -78,7 +61,7 @@ class CRM_Price_BAO_PriceFieldValue extends CRM_Price_DAO_PriceFieldValue {
       return NULL;
     }
     if (!$id && empty($params['name'])) {
-      $params['name'] = strtolower(CRM_Utils_String::munge($params['label'], '_', 242));
+      $params['name'] = strtolower(CRM_Utils_String::munge(($params['label'] ?? '_'), '_', 242));
     }
 
     if ($id && !empty($params['weight'])) {
@@ -93,13 +76,8 @@ class CRM_Price_BAO_PriceFieldValue extends CRM_Price_DAO_PriceFieldValue {
       $fieldValues = ['price_field_id' => CRM_Utils_Array::value('price_field_id', $params, 0)];
       $params['weight'] = CRM_Utils_Weight::updateOtherWeights('CRM_Price_DAO_PriceFieldValue', $oldWeight, $params['weight'], $fieldValues);
     }
-    else {
-      if (!$id) {
-        CRM_Core_DAO::setCreateDefaults($params, self::getDefaults());
-        if (empty($params['name'])) {
-          $params['name'] = CRM_Utils_String::munge(CRM_Utils_Array::value('label', $params), '_', 64);
-        }
-      }
+    elseif (!$id) {
+      CRM_Core_DAO::setCreateDefaults($params, self::getDefaults());
     }
 
     $financialType = $params['financial_type_id'] ?? NULL;
@@ -110,7 +88,8 @@ class CRM_Price_BAO_PriceFieldValue extends CRM_Price_DAO_PriceFieldValue {
     if (!empty($financialType) && !array_key_exists($financialType, $financialTypes) && $params['is_active']) {
       throw new CRM_Core_Exception("Financial Type for Price Field Option is either disabled or does not exist");
     }
-    return self::add($params, $ids);
+    $params['id'] = $id;
+    return self::add($params);
   }
 
   /**
diff --git a/civicrm/CRM/Price/BAO/PriceSet.php b/civicrm/CRM/Price/BAO/PriceSet.php
index 7983cc531ba93806bdd11cae12e78a91550647e8..dcb362b67b310bc86e5bd4fc8e976a6d4912ae7b 100644
--- a/civicrm/CRM/Price/BAO/PriceSet.php
+++ b/civicrm/CRM/Price/BAO/PriceSet.php
@@ -71,6 +71,7 @@ class CRM_Price_BAO_PriceSet extends CRM_Price_DAO_PriceSet {
     $priceSetBAO->save();
 
     CRM_Utils_Hook::post($hook, 'PriceSet', $priceSetBAO->id, $priceSetBAO);
+    unset(\Civi::$statics['CRM_Core_PseudoConstant']);
     return $priceSetBAO;
   }
 
@@ -113,26 +114,22 @@ class CRM_Price_BAO_PriceSet extends CRM_Price_DAO_PriceSet {
    *
    */
   public static function getDefaultPriceSet($entity = 'contribution') {
-    if (!empty(self::$_defaultPriceSet[$entity])) {
-      return self::$_defaultPriceSet[$entity];
-    }
-    $entityName = 'default_contribution_amount';
-    if ($entity == 'membership') {
-      $entityName = 'default_membership_type_amount';
+    if (isset(\Civi::$statics[__CLASS__][$entity])) {
+      return \Civi::$statics[__CLASS__][$entity];
     }
+    $priceSetName = ($entity === 'membership') ? 'default_membership_type_amount' : 'default_contribution_amount';
 
     $sql = "
 SELECT      ps.id AS setID, pfv.price_field_id AS priceFieldID, pfv.id AS priceFieldValueID, pfv.name, pfv.label, pfv.membership_type_id, pfv.amount, pfv.financial_type_id
 FROM        civicrm_price_set ps
 LEFT JOIN   civicrm_price_field pf ON pf.`price_set_id` = ps.id
 LEFT JOIN   civicrm_price_field_value pfv ON pfv.price_field_id = pf.id
-WHERE       ps.name = '{$entityName}'
+WHERE       ps.name = '{$priceSetName}'
 ";
 
     $dao = CRM_Core_DAO::executeQuery($sql);
-    self::$_defaultPriceSet[$entity] = [];
     while ($dao->fetch()) {
-      self::$_defaultPriceSet[$entity][$dao->priceFieldValueID] = [
+      \Civi::$statics[__CLASS__][$entity][$dao->priceFieldValueID] = [
         'setID' => $dao->setID,
         'priceFieldID' => $dao->priceFieldID,
         'name' => $dao->name,
@@ -144,7 +141,7 @@ WHERE       ps.name = '{$entityName}'
       ];
     }
 
-    return self::$_defaultPriceSet[$entity];
+    return \Civi::$statics[__CLASS__][$entity];
   }
 
   /**
@@ -1171,6 +1168,7 @@ WHERE  id = %1";
     $copy->save();
 
     CRM_Utils_Hook::copy('Set', $copy);
+    unset(\Civi::$statics['CRM_Core_PseudoConstant']);
     return $copy;
   }
 
diff --git a/civicrm/CRM/Price/DAO/PriceField.php b/civicrm/CRM/Price/DAO/PriceField.php
index 56dc69ac28974cefb4e3012a4e1ab9f1d0cb9eed..987ae6614fd66b8787cd7e5faf0d4432d9f179f5 100644
--- a/civicrm/CRM/Price/DAO/PriceField.php
+++ b/civicrm/CRM/Price/DAO/PriceField.php
@@ -6,7 +6,7 @@
  *
  * Generated from xml/schema/CRM/Price/PriceField.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:88307b88d5be27887068a778e663b0a2)
+ * (GenCodeChecksum:691cc70f15386dd0c2cd4ab63f9798af)
  */
 
 /**
@@ -204,6 +204,7 @@ class CRM_Price_DAO_PriceField extends CRM_Core_DAO {
             'table' => 'civicrm_price_set',
             'keyColumn' => 'id',
             'labelColumn' => 'title',
+            'nameColumn' => 'name',
           ],
         ],
         'name' => [
diff --git a/civicrm/CRM/Price/DAO/PriceFieldValue.php b/civicrm/CRM/Price/DAO/PriceFieldValue.php
index 15ab9471e6150e409822fadb6759927567ff3750..058ddf7a6f80e535ea8dbc396bcbd1f0d0bf25fc 100644
--- a/civicrm/CRM/Price/DAO/PriceFieldValue.php
+++ b/civicrm/CRM/Price/DAO/PriceFieldValue.php
@@ -6,7 +6,7 @@
  *
  * Generated from xml/schema/CRM/Price/PriceFieldValue.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:a5371a5b399a3e0730bc216886ce5efe)
+ * (GenCodeChecksum:47c904125e3421dde14b5fcd9b8a6f16)
  */
 
 /**
@@ -217,6 +217,7 @@ class CRM_Price_DAO_PriceFieldValue extends CRM_Core_DAO {
           'type' => CRM_Utils_Type::T_STRING,
           'title' => ts('Name'),
           'description' => ts('Price field option name'),
+          'required' => TRUE,
           'maxlength' => 255,
           'size' => CRM_Utils_Type::HUGE,
           'where' => 'civicrm_price_field_value.name',
@@ -233,6 +234,7 @@ class CRM_Price_DAO_PriceFieldValue extends CRM_Core_DAO {
           'type' => CRM_Utils_Type::T_STRING,
           'title' => ts('Label'),
           'description' => ts('Price field option label'),
+          'required' => TRUE,
           'maxlength' => 255,
           'size' => CRM_Utils_Type::HUGE,
           'where' => 'civicrm_price_field_value.label',
diff --git a/civicrm/CRM/Price/Page/Field.php b/civicrm/CRM/Price/Page/Field.php
index 3563bd3dcc805892e35cd8643d2f3dbb59002089..20c55449a6c60100850ee804f6a67fe8c8639b56 100644
--- a/civicrm/CRM/Price/Page/Field.php
+++ b/civicrm/CRM/Price/Page/Field.php
@@ -111,7 +111,7 @@ class CRM_Price_Page_Field extends CRM_Core_Page {
 
     // display taxTerm for priceFields
     $invoiceSettings = Civi::settings()->get('contribution_invoice_settings');
-    $taxTerm = $invoiceSettings['tax_term'] ?? NULL;
+    $taxTerm = Civi::settings()->get('tax_term');
     $invoicing = $invoiceSettings['invoicing'] ?? NULL;
     $getTaxDetails = FALSE;
     $taxRate = CRM_Core_PseudoConstant::getTaxRates();
diff --git a/civicrm/CRM/Price/Page/Option.php b/civicrm/CRM/Price/Page/Option.php
index d477b90b83c6361328d56c04811a145ff595ab01..a8ea081f5004055436705270132fab44e85273b8 100644
--- a/civicrm/CRM/Price/Page/Option.php
+++ b/civicrm/CRM/Price/Page/Option.php
@@ -130,7 +130,7 @@ class CRM_Price_Page_Option extends CRM_Core_Page {
     $taxRate = CRM_Core_PseudoConstant::getTaxRates();
     // display taxTerm for priceFields
     $invoiceSettings = Civi::settings()->get('contribution_invoice_settings');
-    $taxTerm = $invoiceSettings['tax_term'] ?? NULL;
+    $taxTerm = Civi::settings()->get('tax_term');
     $invoicing = $invoiceSettings['invoicing'] ?? NULL;
     $getTaxDetails = FALSE;
     foreach ($customOption as $id => $values) {
@@ -160,12 +160,6 @@ class CRM_Price_Page_Option extends CRM_Core_Page {
           $action -= CRM_Core_Action::DISABLE;
         }
       }
-      if (!empty($customOption[$id]['is_default'])) {
-        $customOption[$id]['is_default'] = '<img src="' . $config->resourceBase . 'i/check.gif" />';
-      }
-      else {
-        $customOption[$id]['is_default'] = '';
-      }
       $customOption[$id]['order'] = $customOption[$id]['weight'];
       $customOption[$id]['action'] = CRM_Core_Action::formLink(self::actionLinks(), $action,
         [
diff --git a/civicrm/CRM/Queue/ErrorPolicy.php b/civicrm/CRM/Queue/ErrorPolicy.php
index 110e07bc919987a9038a1ecb378ca9bfd99b8e04..f7bc774add4f107ae9ece1ce8d6864d90a7d0295 100644
--- a/civicrm/CRM/Queue/ErrorPolicy.php
+++ b/civicrm/CRM/Queue/ErrorPolicy.php
@@ -13,12 +13,12 @@
  * To ensure that PHP errors or unhandled exceptions are reported in JSON
  * format, wrap this around your code. For example:
  *
- * @code
+ * ```
  * $errorContainer = new CRM_Queue_ErrorPolicy();
  * $errorContainer->call(function() {
  *    ...include some files, do some work, etc...
  * });
- * @endcode
+ * ```
  *
  * Note: Most of the code in this class is pretty generic vis-a-vis error
  * handling -- except for 'reportError', whose message format is only
diff --git a/civicrm/CRM/Queue/Service.php b/civicrm/CRM/Queue/Service.php
index c86f790ad4690abba7f467de7c754eefdfba3f35..60d2521cba1507852862a7cc55f2e201ea9a3777 100644
--- a/civicrm/CRM/Queue/Service.php
+++ b/civicrm/CRM/Queue/Service.php
@@ -15,7 +15,7 @@
  * different queue-providers may store the queue content in different
  * ways (in memory, in SQL, or in an external service).
  *
- * @code
+ * ```
  * $queue = CRM_Queue_Service::singleton()->create(array(
  *   'type' => 'interactive',
  *   'name' => 'upgrade-tasks',
@@ -31,7 +31,7 @@
  *     $queue->releaseItem($item);
  *   }
  * }
- * @endcode
+ * ```
  */
 class CRM_Queue_Service {
 
diff --git a/civicrm/CRM/Report/Form.php b/civicrm/CRM/Report/Form.php
index abbd87fd93aaffb348d349fa380281e2af282b16..f09de2def6efe856dd694eee5e371e78d75d5690 100644
--- a/civicrm/CRM/Report/Form.php
+++ b/civicrm/CRM/Report/Form.php
@@ -139,11 +139,6 @@ class CRM_Report_Form extends CRM_Core_Form {
    */
   protected $_groupFilter = FALSE;
 
-  /**
-   * Required for civiexportexcel.
-   */
-  public $supportsExportExcel = TRUE;
-
   /**
    * Has the report been optimised for group filtering.
    *
@@ -1440,7 +1435,7 @@ class CRM_Report_Form extends CRM_Core_Form {
     if (!CRM_Core_Permission::check('view report sql')) {
       return;
     }
-    $ignored_output_modes = ['pdf', 'csv', 'print', 'excel2007'];
+    $ignored_output_modes = ['pdf', 'csv', 'print'];
     if (in_array($this->_outputMode, $ignored_output_modes)) {
       return;
     }
@@ -2862,11 +2857,6 @@ WHERE cg.extends IN ('" . implode("','", $this->_customGroupExtends) . "') AND
       $this->_absoluteUrl = TRUE;
       $this->addPaging = FALSE;
     }
-    elseif ($this->_outputMode == 'excel2007') {
-      $printOnly = TRUE;
-      $this->_absoluteUrl = TRUE;
-      $this->addPaging = FALSE;
-    }
     elseif ($this->_outputMode == 'copy' && $this->_criteriaForm) {
       $this->_createNew = TRUE;
     }
@@ -3501,9 +3491,6 @@ WHERE cg.extends IN ('" . implode("','", $this->_customGroupExtends) . "') AND
     elseif ($this->_outputMode == 'csv') {
       CRM_Report_Utils_Report::export2csv($this, $rows);
     }
-    elseif ($this->_outputMode == 'excel2007') {
-      CRM_CiviExportExcel_Utils_Report::export2excel2007($this, $rows);
-    }
     elseif ($this->_outputMode == 'group') {
       $group = $this->_params['groups'];
       $this->add2group($group);
diff --git a/civicrm/CRM/Report/Form/Campaign/SurveyDetails.php b/civicrm/CRM/Report/Form/Campaign/SurveyDetails.php
index a5496a54696fb069d4bdef25e5a573f81382fa15..c3f9967b4ecb89a6ace2931b3d6f952a916f3b90 100644
--- a/civicrm/CRM/Report/Form/Campaign/SurveyDetails.php
+++ b/civicrm/CRM/Report/Form/Campaign/SurveyDetails.php
@@ -42,7 +42,7 @@ class CRM_Report_Form_Campaign_SurveyDetails extends CRM_Report_Form {
    * say Q1, Q2 instead of the full title - to save space.
    * @var array
    */
-  private $_columnTitleOverrides = array();
+  private $_columnTitleOverrides = [];
 
   /**
    */
@@ -52,7 +52,7 @@ class CRM_Report_Form_Campaign_SurveyDetails extends CRM_Report_Form {
   public function __construct() {
     //filter options for survey activity status.
     $responseStatus = array('' => '- Any -');
-    self::$_surveyRespondentStatus = array();
+    self::$_surveyRespondentStatus = [];
     $activityStatus = CRM_Core_PseudoConstant::activityStatus('name');
     if ($statusId = array_search('Scheduled', $activityStatus)) {
       $responseStatus[$statusId] = ts('Reserved');
@@ -64,10 +64,10 @@ class CRM_Report_Form_Campaign_SurveyDetails extends CRM_Report_Form {
     }
 
     $optionGroups = CRM_Campaign_BAO_Survey::getResultSets('name');
-    $resultOptions = array();
+    $resultOptions = [];
     foreach ($optionGroups as $gid => $name) {
       if ($name) {
-        $value = array();
+        $value = [];
         $value = CRM_Core_OptionGroup::values($name);
         if (!empty($value)) {
           $value = array_combine($value, $value);
@@ -221,12 +221,12 @@ class CRM_Report_Form_Campaign_SurveyDetails extends CRM_Report_Form {
   }
 
   public function select() {
-    $select = array();
+    $select = [];
 
     //add the survey response fields.
     $this->_addSurveyResponseColumns();
 
-    $this->_columnHeaders = array();
+    $this->_columnHeaders = [];
     foreach ($this->_columns as $tableName => $table) {
       if (!isset($table['fields'])) {
         continue;
@@ -302,7 +302,7 @@ class CRM_Report_Form_Campaign_SurveyDetails extends CRM_Report_Form {
   }
 
   public function where() {
-    $clauses = array();
+    $clauses = [];
     foreach ($this->_columns as $tableName => $table) {
       if (array_key_exists('filters', $table)) {
         foreach ($table['filters'] as $fieldName => $field) {
@@ -376,9 +376,9 @@ class CRM_Report_Form_Campaign_SurveyDetails extends CRM_Report_Form {
       return $coverSheet;
     }
 
-    $fieldIds = array();
+    $fieldIds = [];
 
-    $surveyResponseFields = array();
+    $surveyResponseFields = [];
     foreach ($this->_columns as $tableName => $values) {
       if (!is_array($values['fields'])) {
         continue;
@@ -408,7 +408,7 @@ INNER JOIN  civicrm_option_value val ON ( val.option_group_id = field.option_gro
      WHERE  field.id IN (' . implode(' , ', $fieldIds) . ' )
   Order By  val.weight';
       $field = CRM_Core_DAO::executeQuery($query);
-      $options = array();
+      $options = [];
       while ($field->fetch()) {
         $name = "custom_{$field->id}";
         $surveyResponseFields[$name]['options'][$field->value] = $field->label;
@@ -426,7 +426,7 @@ INNER JOIN  civicrm_option_value val ON ( val.option_group_id = survey.result_id
      WHERE  survey.id IN ( ' . implode(' , ', array_values($surveyIds)) . ' )
   Order By  val.weight';
     $resultSet = CRM_Core_DAO::executeQuery($query);
-    $surveyResultFields = array();
+    $surveyResultFields = [];
     while ($resultSet->fetch()) {
       $surveyResultFields[$resultSet->id]['title'] = $resultSet->title;
       $surveyResultFields[$resultSet->id]['options'][$resultSet->value] = $resultSet->label;
@@ -518,7 +518,7 @@ INNER JOIN  civicrm_survey survey ON ( survey.result_id = grp.id )
   Order By  val.weight';
 
     $result = CRM_Core_DAO::executeQuery($query);
-    $resultSet = array();
+    $resultSet = [];
     while ($result->fetch()) {
       $resultSet[$result->id][$result->value] = $result->label;
     }
@@ -531,7 +531,7 @@ INNER JOIN  civicrm_survey survey ON ( survey.result_id = grp.id )
       if (!empty($row['civicrm_activity_survey_id'])) {
         $surveyId = $row['civicrm_activity_survey_id'];
       }
-      $result = CRM_Utils_Array::value($surveyId, $resultSet, array());
+      $result = CRM_Utils_Array::value($surveyId, $resultSet, []);
       $resultLabel = $row['civicrm_activity_result'] ?? NULL;
       if ($respondentStatus == 'Reserved') {
         $row['civicrm_activity_result'] = implode(' | ', array_keys($result));
@@ -556,8 +556,8 @@ INNER JOIN  civicrm_survey survey ON ( survey.result_id = grp.id )
       return;
     }
 
-    $surveyResponseFields = array();
-    $surveyResponseFieldIds = array();
+    $surveyResponseFields = [];
+    $surveyResponseFieldIds = [];
     foreach ($this->_columns as $tableName => $values) {
       if (!is_array($values['fields'])) {
         continue;
@@ -610,8 +610,8 @@ INNER JOIN  civicrm_custom_group cg ON ( cg.id = cf.custom_group_id )
      WHERE  cf.id IN ( ' . implode(' , ', $surveyResponseFieldIds) . ' )
   Order By  ov.weight';
 
-    $responseFields = array();
-    $fieldValueMap = array();
+    $responseFields = [];
+    $fieldValueMap = [];
     $properties = array(
       'id',
       'data_type',
@@ -656,7 +656,7 @@ INNER JOIN  civicrm_custom_group cg ON ( cg.id = cf.custom_group_id )
           in_array($this->_outputMode, array('print', 'pdf'))
         ) {
           $optGrpId = $responseFields[$name]['option_group_id'] ?? NULL;
-          $options = CRM_Utils_Array::value($optGrpId, $fieldValueMap, array());
+          $options = CRM_Utils_Array::value($optGrpId, $fieldValueMap, []);
           $value = implode(' | ', array_keys($options));
         }
         else {
@@ -678,7 +678,7 @@ INNER JOIN  civicrm_custom_group cg ON ( cg.id = cf.custom_group_id )
       return;
     }
 
-    $responseFields = array();
+    $responseFields = [];
     foreach ($surveyIds as $surveyId) {
       $responseFields += CRM_Campaign_BAO_Survey::getSurveyResponseFields($surveyId);
       $this->_surveyResponseFields = $responseFields;
@@ -702,7 +702,7 @@ INNER JOIN  civicrm_custom_group cg ON ( cg.id = cf.custom_group_id )
         $this->_locationBasedPhoneField = TRUE;
       }
     }
-    $responseFieldIds = array();
+    $responseFieldIds = [];
     foreach (array_keys($responseFields) as $key) {
       $cfId = CRM_Core_BAO_CustomField::getKeyID($key);
       if ($cfId) {
diff --git a/civicrm/CRM/Report/Form/Contact/Summary.php b/civicrm/CRM/Report/Form/Contact/Summary.php
index 485f4d814f2398bc5ea061f896ed3c414ed0dbb2..c920b08f7df63c87b18fbf8608e4096753ff41d6 100644
--- a/civicrm/CRM/Report/Form/Contact/Summary.php
+++ b/civicrm/CRM/Report/Form/Contact/Summary.php
@@ -134,7 +134,7 @@ class CRM_Report_Form_Contact_Summary extends CRM_Report_Form {
    * @return array
    */
   public static function formRule($fields, $files, $self) {
-    $errors = $grouping = array();
+    $errors = $grouping = [];
     return $errors;
   }
 
@@ -156,7 +156,7 @@ class CRM_Report_Form_Contact_Summary extends CRM_Report_Form {
 
     $sql = $this->buildQuery(TRUE);
 
-    $rows = $graphRows = array();
+    $rows = $graphRows = [];
     $this->buildRows($sql, $rows);
 
     $this->formatDisplay($rows);
diff --git a/civicrm/CRM/Report/Form/Contribute/Detail.php b/civicrm/CRM/Report/Form/Contribute/Detail.php
index 9d4c12d22a02607edeaaa8e4f8a28d04b4704ece..73c1369b3a9f06be969dd9d0f954563ba163a136 100644
--- a/civicrm/CRM/Report/Form/Contribute/Detail.php
+++ b/civicrm/CRM/Report/Form/Contribute/Detail.php
@@ -640,7 +640,8 @@ UNION ALL
     $contributionTypes = CRM_Contribute_PseudoConstant::financialType();
     $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'label');
     $paymentInstruments = CRM_Contribute_PseudoConstant::paymentInstrument();
-    $contributionPages = CRM_Contribute_PseudoConstant::contributionPage();
+    // We pass in TRUE as 2nd param so that even disabled contribution page titles are returned and replaced in the report
+    $contributionPages = CRM_Contribute_PseudoConstant::contributionPage(NULL, TRUE);
     $batches = CRM_Batch_BAO_Batch::getBatches();
     foreach ($rows as $rowNum => $row) {
       if (!empty($this->_noRepeats) && $this->_outputMode != 'csv') {
diff --git a/civicrm/CRM/Report/Form/Contribute/Repeat.php b/civicrm/CRM/Report/Form/Contribute/Repeat.php
index 7b66f28d6d32ea7af483b4e3718925c4de0d450f..aa4846c532f9a99f44e7458bacaec971d8a433ed 100644
--- a/civicrm/CRM/Report/Form/Contribute/Repeat.php
+++ b/civicrm/CRM/Report/Form/Contribute/Repeat.php
@@ -244,7 +244,7 @@ class CRM_Report_Form_Contribute_Repeat extends CRM_Report_Form {
    * Override parent select for reasons someone will someday make sense of & document.
    */
   public function select() {
-    $select = array();
+    $select = [];
     $append = NULL;
     // since contact fields not related to financial type
     if (array_key_exists('financial_type', $this->_params['group_bys']) ||
@@ -409,7 +409,7 @@ LEFT JOIN $this->tempTableRepeat2 {$this->_aliases['civicrm_contribution']}2
     }
 
     if (!$this->_amountClauseWithAND) {
-      $amountClauseWithAND = array();
+      $amountClauseWithAND = [];
       if (!empty($clauses['total_amount1'])) {
         $amountClauseWithAND[] = str_replace("{$this->_aliases['civicrm_contribution']}.total_amount",
           "{$this->_aliases['civicrm_contribution']}1.total_amount_sum", $clauses['total_amount1']);
@@ -478,7 +478,7 @@ LEFT JOIN $this->tempTableRepeat2 {$this->_aliases['civicrm_contribution']}2
    */
   public static function formRule($fields, $files, $self) {
 
-    $errors = $checkDate = $errorCount = array();
+    $errors = $checkDate = $errorCount = [];
 
     $rules = array(
       'id' => array(
@@ -558,7 +558,7 @@ LEFT JOIN $this->tempTableRepeat2 {$this->_aliases['civicrm_contribution']}2
       foreach ($fields['fields'] as $fld_id => $value) {
         if (!($fld_id == 'total_amount1') && !($fld_id == 'total_amount2') && !(substr($fld_id, 0, 7) === "custom_")) {
           $found = FALSE;
-          $invlidGroups = array();
+          $invlidGroups = [];
           foreach ($fields['group_bys'] as $grp_id => $val) {
             $validFields = $rules[$grp_id];
             if (in_array($fld_id, $validFields)) {
@@ -671,7 +671,7 @@ LEFT JOIN $this->tempTableRepeat2 {$this->_aliases['civicrm_contribution']}2
     $sql = "{$this->_select} {$this->_from} {$this->_where}";
     $dao = $this->executeReportQuery($sql);
     //store contributions in array 'contact_sums' for comparison
-    $contact_sums = array();
+    $contact_sums = [];
     while ($dao->fetch()) {
       $contact_sums[$dao->contact_civireport_id] = array(
         'contribution1_total_amount_sum' => $dao->contribution1_total_amount_sum,
@@ -753,7 +753,7 @@ GROUP BY    currency
 ";
     $dao = $this->executeReportQuery($sql);
 
-    $amount = $average = $amount2 = $average2 = array();
+    $amount = $average = $amount2 = $average2 = [];
     $count = $count2 = 0;
     while ($dao->fetch()) {
       if ($dao->amount) {
@@ -824,7 +824,7 @@ GROUP BY    currency
     $count = 0;
     $sql = "{$this->_select} {$this->_from} {$this->_where} {$this->_groupBy} {$this->_orderBy} {$this->_limit}";
     $dao = $this->executeReportQuery($sql);
-    $rows = array();
+    $rows = [];
     while ($dao->fetch()) {
       foreach ($this->_columnHeaders as $key => $value) {
         $rows[$count][$key] = $dao->$key;
diff --git a/civicrm/CRM/Report/Form/Event/ParticipantListing.php b/civicrm/CRM/Report/Form/Event/ParticipantListing.php
index 50175c9aaf5bf4454ccf6761d47ac390f054e131..c465fb6f85314a260c74e5fb5c65fe92c1f6915d 100644
--- a/civicrm/CRM/Report/Form/Event/ParticipantListing.php
+++ b/civicrm/CRM/Report/Form/Event/ParticipantListing.php
@@ -404,7 +404,7 @@ WHERE ce.entity_table = 'civicrm_event'
 ORDER BY  cv.label
 ";
     $dao = CRM_Core_DAO::executeQuery($query);
-    $elements = array();
+    $elements = [];
     while ($dao->fetch()) {
       $elements[$dao->id] = "$dao->label\n";
     }
@@ -417,8 +417,8 @@ ORDER BY  cv.label
   }
 
   public function select() {
-    $select = array();
-    $this->_columnHeaders = array();
+    $select = [];
+    $this->_columnHeaders = [];
 
     //add blank column at the Start
     if (array_key_exists('options', $this->_params) &&
@@ -475,7 +475,7 @@ ORDER BY  cv.label
    * @return array
    */
   public static function formRule($fields, $files, $self) {
-    $errors = $grouping = array();
+    $errors = $grouping = [];
     return $errors;
   }
 
@@ -530,7 +530,7 @@ ORDER BY  cv.label
   }
 
   public function where() {
-    $clauses = array();
+    $clauses = [];
     foreach ($this->_columns as $tableName => $table) {
       if (array_key_exists('filters', $table)) {
         foreach ($table['filters'] as $fieldName => $field) {
@@ -673,7 +673,7 @@ ORDER BY  cv.label
         $roleId = $row['civicrm_participant_role_id'];
         if ($roleId) {
           $roles = explode(CRM_Core_DAO::VALUE_SEPARATOR, $roleId);
-          $roleId = array();
+          $roleId = [];
           foreach ($roles as $role) {
             $roleId[$role] = CRM_Event_PseudoConstant::participantRole($role, FALSE);
           }
diff --git a/civicrm/CRM/Report/Utils/Report.php b/civicrm/CRM/Report/Utils/Report.php
index 627c2a97c73c664d110868e6671177f8fb64f059..e2ca8de2139b9c21c1185296d90ee6884e401184 100644
--- a/civicrm/CRM/Report/Utils/Report.php
+++ b/civicrm/CRM/Report/Utils/Report.php
@@ -251,7 +251,7 @@ WHERE  inst.report_id = %1";
         $value = $row[$v] ?? NULL;
         if (isset($value)) {
           // Remove HTML, unencode entities, and escape quotation marks.
-          $value = str_replace('"', '""', html_entity_decode(strip_tags($value)));
+          $value = str_replace('"', '""', html_entity_decode(strip_tags($value), ENT_QUOTES | ENT_HTML401));
 
           if (CRM_Utils_Array::value('type', $form->_columnHeaders[$v]) & 4) {
             if (CRM_Utils_Array::value('group_by', $form->_columnHeaders[$v]) == 'MONTH' ||
diff --git a/civicrm/CRM/Report/xml/Menu/Report.xml b/civicrm/CRM/Report/xml/Menu/Report.xml
index 2e262817d5381653b8633f87daf32e4bb0413133..725bbf8d631bdbcf462a54de72805ce40c14b6c4 100644
--- a/civicrm/CRM/Report/xml/Menu/Report.xml
+++ b/civicrm/CRM/Report/xml/Menu/Report.xml
@@ -51,7 +51,6 @@
      <desc>Component wise listing of all available templates</desc>
      <access_arguments>access CiviCRM,access CiviReport</access_arguments>
      <adminGroup>CiviReport</adminGroup>
-     <icon>admin/small/report_template.gif</icon>
   </item>
   <item>
      <path>civicrm/admin/report/options/report_template</path>
@@ -60,7 +59,6 @@
      <desc>Browse, Edit and Delete the Report templates.</desc>
      <access_arguments>access CiviCRM,access CiviReport</access_arguments>
      <adminGroup>CiviReport</adminGroup>
-     <icon>admin/small/template.png</icon>
   </item>
   <item>
      <path>civicrm/admin/report/list</path>
@@ -68,6 +66,5 @@
      <page_callback>CRM_Report_Page_InstanceList</page_callback>
      <desc>Browse existing report, change report criteria and settings.</desc>
      <adminGroup>CiviReport</adminGroup>
-     <icon>admin/small/report_list.gif</icon>
   </item>
 </menu>
diff --git a/civicrm/CRM/SMS/Page/Provider.php b/civicrm/CRM/SMS/Page/Provider.php
index f8f4ad4e37e1d8597fe4d5433c93e267d8a75daa..9122dca44eed6f51286a5a060dbb55c545284730 100644
--- a/civicrm/CRM/SMS/Page/Provider.php
+++ b/civicrm/CRM/SMS/Page/Provider.php
@@ -112,7 +112,7 @@ class CRM_SMS_Page_Provider extends CRM_Core_Page_Basic {
    */
   public function browse($action = NULL) {
     $providers = CRM_SMS_BAO_Provider::getProviders();
-    $rows = array();
+    $rows = [];
     foreach ($providers as $provider) {
       $action = array_sum(array_keys($this->links()));
       // update enable/disable links.
diff --git a/civicrm/CRM/SMS/Provider.php b/civicrm/CRM/SMS/Provider.php
index 387092876d51067ec63f60e403ff3775bcd9de89..896302ea13eec13b4a9b2f8186e1d75521316fff 100644
--- a/civicrm/CRM/SMS/Provider.php
+++ b/civicrm/CRM/SMS/Provider.php
@@ -22,7 +22,7 @@ abstract class CRM_SMS_Provider {
    *
    * @var object
    */
-  static private $_singleton = array();
+  static private $_singleton = [];
   const MAX_SMS_CHAR = 460;
 
   /**
@@ -34,7 +34,7 @@ abstract class CRM_SMS_Provider {
    * @return object
    * @throws CRM_Core_Exception
    */
-  public static function &singleton($providerParams = array(), $force = FALSE) {
+  public static function &singleton($providerParams = [], $force = FALSE) {
     $mailingID = $providerParams['mailing_id'] ?? NULL;
     $providerID = $providerParams['provider_id'] ?? NULL;
     $providerName = $providerParams['provider'] ?? NULL;
@@ -125,7 +125,7 @@ abstract class CRM_SMS_Provider {
    * @return self|null|object
    * @throws CRM_Core_Exception
    */
-  public function createActivity($apiMsgID, $message, $headers = array(), $jobID = NULL, $userID = NULL) {
+  public function createActivity($apiMsgID, $message, $headers = [], $jobID = NULL, $userID = NULL) {
     if ($jobID) {
       $sql = "
 SELECT scheduled_id FROM civicrm_mailing m
diff --git a/civicrm/CRM/Upgrade/Form.php b/civicrm/CRM/Upgrade/Form.php
index 10c2b542530f11353642c56376995788825b054c..e1f7bcd2def8c7ef36eff251144c621906b63ffb 100644
--- a/civicrm/CRM/Upgrade/Form.php
+++ b/civicrm/CRM/Upgrade/Form.php
@@ -10,11 +10,7 @@
  */
 
 /**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
+ * Class CRM_Upgrade_Form
  */
 class CRM_Upgrade_Form extends CRM_Core_Form {
   const QUEUE_NAME = 'CRM_Upgrade';
diff --git a/civicrm/CRM/Upgrade/Incremental/php/FiveTwentySeven.php b/civicrm/CRM/Upgrade/Incremental/php/FiveTwentySeven.php
new file mode 100644
index 0000000000000000000000000000000000000000..c37d150605297231ef239922026232064a97d8f9
--- /dev/null
+++ b/civicrm/CRM/Upgrade/Incremental/php/FiveTwentySeven.php
@@ -0,0 +1,109 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ * Upgrade logic for FiveTwentySeven */
+class CRM_Upgrade_Incremental_php_FiveTwentySeven extends CRM_Upgrade_Incremental_Base {
+
+  /**
+   * Compute any messages which should be displayed beforeupgrade.
+   *
+   * Note: This function is called iteratively for each upcoming
+   * revision to the database.
+   *
+   * @param string $preUpgradeMessage
+   * @param string $rev
+   *   a version number, e.g. '4.4.alpha1', '4.4.beta3', '4.4.0'.
+   * @param null $currentVer
+   */
+  public function setPreUpgradeMessage(&$preUpgradeMessage, $rev, $currentVer = NULL) {
+    // Example: Generate a pre-upgrade message.
+    // if ($rev == '5.12.34') {
+    //   $preUpgradeMessage .= '<p>' . ts('A new permission, "%1", has been added. This permission is now used to control access to the Manage Tags screen.', array(1 => ts('manage tags'))) . '</p>';
+    // }
+    $preUpgradeMessage .= '<p>' . ts('Starting with version 5.28.0, CiviCRM will
+      require the PHP Internationalization extension (PHP-Intl).  In preparation
+      for this, the system check will show a warning beginning in 5.27.0 if your
+      site lacks this extension.') . '</p>';
+  }
+
+  /**
+   * Compute any messages which should be displayed after upgrade.
+   *
+   * @param string $postUpgradeMessage
+   *   alterable.
+   * @param string $rev
+   *   an intermediate version; note that setPostUpgradeMessage is called repeatedly with different $revs.
+   */
+  public function setPostUpgradeMessage(&$postUpgradeMessage, $rev) {
+    // Example: Generate a post-upgrade message.
+    // if ($rev == '5.12.34') {
+    //   $postUpgradeMessage .= '<br /><br />' . ts("By default, CiviCRM now disables the ability to import directly from SQL. To use this feature, you must explicitly grant permission 'import SQL datasource'.");
+    // }
+  }
+
+  /*
+   * Important! All upgrade functions MUST add a 'runSql' task.
+   * Uncomment and use the following template for a new upgrade version
+   * (change the x in the function name):
+   */
+
+  /**
+   * Upgrade function.
+   *
+   * @param string $rev
+   */
+  public function upgrade_5_27_alpha1($rev) {
+    // Add column before running sql which populates the column's values
+    $this->addTask('Add serialize column to civicrm_custom_field', 'addColumn',
+      'civicrm_custom_field', 'serialize', "int unsigned DEFAULT NULL COMMENT 'Serialization method - a non-null value indicates a multi-valued field.'"
+    );
+    $this->addTask('Make the label field required on price field value', 'priceFieldValueLabelRequired');
+    $this->addTask('Make the name field required on civicrm_membership_type', 'nameMembershipTypeRequired');
+    $this->addTask('Rebuild Multilingal Schema', 'rebuildMultilingalSchema', '5.27.alpha1');
+    $this->addTask(ts('Upgrade DB to %1: SQL', [1 => $rev]), 'runSql', $rev);
+  }
+
+  public function priceFieldValueLabelRequired($ctx) {
+    $domain = new CRM_Core_DAO_Domain();
+    $domain->find(TRUE);
+    if ($domain->locales) {
+      $locales = explode(CRM_Core_DAO::VALUE_SEPARATOR, $domain->locales);
+      foreach ($locales as $locale) {
+        CRM_Core_DAO::executeQuery("UPDATE civicrm_price_field_value SET label_{$locale} = '' WHERE label_{$locale} IS NULL", [], TRUE, NULL, FALSE, FALSE);
+        CRM_Core_DAO::executeQuery("ALTER TABLE civicrm_price_field_value CHANGE `label_{$locale}` `label_{$locale}` varchar(255) NOT NULL   COMMENT 'Price field option label'", [], TRUE, NULL, FALSE, FALSE);
+      }
+    }
+    else {
+      CRM_Core_DAO::executeQuery("UPDATE civicrm_price_field_value SET label = '' WHERE label IS NULL");
+      CRM_Core_DAO::executeQuery("ALTER TABLE civicrm_price_field_value CHANGE `label` `label` varchar(255) NOT NULL   COMMENT 'Price field option label'", [], TRUE, NULL, FALSE, FALSE);
+    }
+    return TRUE;
+  }
+
+  public function nameMembershipTypeRequired($ctx) {
+    $domain = new CRM_Core_DAO_Domain();
+    $domain->find(TRUE);
+    if ($domain->locales) {
+      $locales = explode(CRM_Core_DAO::VALUE_SEPARATOR, $domain->locales);
+      foreach ($locales as $locale) {
+        CRM_Core_DAO::executeQuery("UPDATE civicrm_membership_type SET name_{$locale} = '' WHERE name_{$locale} IS NULL", [], TRUE, NULL, FALSE, FALSE);
+        CRM_Core_DAO::executeQuery("ALTER TABLE civicrm_membership_type CHANGE `name_{$locale}` `name_{$locale}` varchar(128) NOT NULL   COMMENT 'Name of Membership Type'", [], TRUE, NULL, FALSE, FALSE);
+      }
+    }
+    else {
+      CRM_Core_DAO::executeQuery("UPDATE civicrm_membership_type SET name = '' WHERE name IS NULL");
+      CRM_Core_DAO::executeQuery("ALTER TABLE civicrm_membership_type CHANGE `name` `name` varchar(128) NOT NULL   COMMENT 'Name of Membership Type'", [], TRUE, NULL, FALSE, FALSE);
+    }
+    return TRUE;
+  }
+
+}
diff --git a/civicrm/CRM/Upgrade/Incremental/sql/5.26.0.mysql.tpl b/civicrm/CRM/Upgrade/Incremental/sql/5.26.0.mysql.tpl
deleted file mode 100644
index 481ff00a5a3cf4cc863b09eebf674037e94a3434..0000000000000000000000000000000000000000
--- a/civicrm/CRM/Upgrade/Incremental/sql/5.26.0.mysql.tpl
+++ /dev/null
@@ -1 +0,0 @@
-{* file to handle db changes in 5.26.0 during upgrade *}
diff --git a/civicrm/CRM/Upgrade/Incremental/sql/5.26.2.mysql.tpl b/civicrm/CRM/Upgrade/Incremental/sql/5.26.2.mysql.tpl
deleted file mode 100644
index 614c9d2d2bc0809da044d91ad74a0fe90f5d1e33..0000000000000000000000000000000000000000
--- a/civicrm/CRM/Upgrade/Incremental/sql/5.26.2.mysql.tpl
+++ /dev/null
@@ -1 +0,0 @@
-{* file to handle db changes in 5.26.2 during upgrade *}
diff --git a/civicrm/CRM/Upgrade/Incremental/sql/5.27.0.mysql.tpl b/civicrm/CRM/Upgrade/Incremental/sql/5.27.0.mysql.tpl
new file mode 100644
index 0000000000000000000000000000000000000000..a39da322338704830bccc38daece099d4b6b18dc
--- /dev/null
+++ b/civicrm/CRM/Upgrade/Incremental/sql/5.27.0.mysql.tpl
@@ -0,0 +1 @@
+{* file to handle db changes in 5.27.0 during upgrade *}
diff --git a/civicrm/CRM/Upgrade/Incremental/sql/5.26.1.mysql.tpl b/civicrm/CRM/Upgrade/Incremental/sql/5.27.alpha1.mysql.tpl
similarity index 60%
rename from civicrm/CRM/Upgrade/Incremental/sql/5.26.1.mysql.tpl
rename to civicrm/CRM/Upgrade/Incremental/sql/5.27.alpha1.mysql.tpl
index e75b09525588e23d473def959d50d2526b2c7b02..b2364e975c2c654924ed02a5cd20728eb451e84e 100644
--- a/civicrm/CRM/Upgrade/Incremental/sql/5.26.1.mysql.tpl
+++ b/civicrm/CRM/Upgrade/Incremental/sql/5.27.alpha1.mysql.tpl
@@ -1,5 +1,10 @@
 {* file to handle db changes in 5.27.alpha1 during upgrade *}
 
+UPDATE civicrm_custom_field SET serialize = 1, html_type = REPLACE(html_type, 'Multi-', '')
+WHERE html_type LIKE 'Multi-%' OR html_type = 'CheckBox';
+
+ALTER TABLE `civicrm_contribution_recur` CHANGE `amount` `amount` DECIMAL( 20,2 ) COMMENT 'Amount to be collected (including any sales tax) by payment processor each recurrence.';
+
 -- dev/core/-/issues/1794
 ALTER TABLE `civicrm_custom_group` CHANGE `collapse_adv_display` `collapse_adv_display` TINYINT(4) UNSIGNED NULL DEFAULT '0' COMMENT 'Will this group be in collapsed or expanded mode on advanced search display ?';
 ALTER TABLE `civicrm_custom_group` CHANGE `collapse_display` `collapse_display` TINYINT(4) UNSIGNED NULL DEFAULT '0' COMMENT 'Will this group be in collapsed or expanded mode on initial display ?';
diff --git a/civicrm/CRM/Upgrade/Incremental/sql/5.27.beta1.mysql.tpl b/civicrm/CRM/Upgrade/Incremental/sql/5.27.beta1.mysql.tpl
new file mode 100644
index 0000000000000000000000000000000000000000..699d61878c7bcc8fe8010053b0a0db2bad72423c
--- /dev/null
+++ b/civicrm/CRM/Upgrade/Incremental/sql/5.27.beta1.mysql.tpl
@@ -0,0 +1,8 @@
+{* file to handle db changes in 5.27.beta1 during upgrade *}
+ALTER TABLE civicrm_option_value MODIFY COLUMN `filter` int unsigned DEFAULT 0 COMMENT 'Bitwise logic can be used to create subsets of options within an option_group for different uses.';
+
+-- To think about: This will update ones where someone has explicitly set it to NULL for their own purposes and they don't care about the dropdowns. How likely is that? How can we tell if it's one they created since 5.26 and didn't intend to set it to NULL?
+UPDATE civicrm_option_value ov
+INNER JOIN civicrm_option_group og ON (ov.option_group_id = og.id AND og.name='activity_type')
+SET ov.filter = 0
+WHERE ov.filter IS NULL;
diff --git a/civicrm/CRM/Utils/API/MatchOption.php b/civicrm/CRM/Utils/API/MatchOption.php
index f92723c33726ff4845aeda86e1b5f8fd8fc708f1..fa87a1cf8b03de2b87eb7741b1e317ca4b6edfd1 100644
--- a/civicrm/CRM/Utils/API/MatchOption.php
+++ b/civicrm/CRM/Utils/API/MatchOption.php
@@ -21,7 +21,7 @@
  *   - "match-mandatory" will generate an error
  *   - "match" will allow action to proceed -- thus inserting a new record
  *
- * @code
+ * ```
  * $result = civicrm_api('contact', 'create', array(
  *   'options' => array(
  *     'match' => array('last_name', 'first_name')
@@ -30,7 +30,7 @@
  *   'last_name' => 'Lebowski',
  *   'nick_name' => 'The Dude',
  * ));
- * @endcode
+ * ```
  *
  * @package CRM
  * @copyright CiviCRM LLC https://civicrm.org/licensing
diff --git a/civicrm/CRM/Utils/API/ReloadOption.php b/civicrm/CRM/Utils/API/ReloadOption.php
index 764d81bf80c5cc153792e3ae14ce87dae2ae7ee0..3e320d5c1408d94c277b966765c4603f41fbafb4 100644
--- a/civicrm/CRM/Utils/API/ReloadOption.php
+++ b/civicrm/CRM/Utils/API/ReloadOption.php
@@ -13,13 +13,13 @@
  * Implement the "reload" option. This option can be used with "create" to force
  * the API to reload a clean copy of the entity before returning the result.
  *
- * @code
+ * ```
  * $clean = civicrm_api('myentity', 'create', array(
  *   'options' => array(
  *     'reload' => 1
  *   ),
  * ));
- * @endcode
+ * ```
  *
  * @package CRM
  * @copyright CiviCRM LLC https://civicrm.org/licensing
diff --git a/civicrm/CRM/Utils/Address/USPS.php b/civicrm/CRM/Utils/Address/USPS.php
index bba6d337a9bc3e2fd2a03a60804f0135c169deec..721b2e70e079b5bccf6e0875048ab165da95489a 100644
--- a/civicrm/CRM/Utils/Address/USPS.php
+++ b/civicrm/CRM/Utils/Address/USPS.php
@@ -68,19 +68,17 @@ class CRM_Utils_Address_USPS {
 
     $XMLQuery = '<AddressValidateRequest USERID="' . $userID . '"><Address ID="0"><Address1>' . CRM_Utils_Array::value('supplemental_address_1', $values, '') . '</Address1><Address2>' . $address2 . '</Address2><City>' . $values['city'] . '</City><State>' . $values['state_province'] . '</State><Zip5>' . $values['postal_code'] . '</Zip5><Zip4>' . CRM_Utils_Array::value('postal_code_suffix', $values, '') . '</Zip4></Address></AddressValidateRequest>';
 
-    require_once 'HTTP/Request.php';
-    $request = new HTTP_Request();
-
-    $request->setURL($url);
-
-    $request->addQueryString('API', 'Verify');
-    $request->addQueryString('XML', $XMLQuery);
-
-    $response = $request->sendRequest();
+    $client = new GuzzleHttp\Client();
+    $request = $client->request('GET', $url, [
+      'query' => [
+        'API' => 'Verify',
+        'XML' => $XMLQuery,
+      ],
+    ]);
 
     $session = CRM_Core_Session::singleton();
 
-    $code = $request->getResponseCode();
+    $code = $request->getStatusCode();
     if ($code != 200) {
       $session->setStatus(ts('USPS Address Lookup Failed with HTTP status code: %1',
         [1 => $code]
@@ -88,7 +86,7 @@ class CRM_Utils_Address_USPS {
       return FALSE;
     }
 
-    $responseBody = $request->getResponseBody();
+    $responseBody = $request->getBody();
 
     $xml = simplexml_load_string($responseBody);
 
diff --git a/civicrm/CRM/Utils/AutoClean.php b/civicrm/CRM/Utils/AutoClean.php
index 48e0cf9e2df2da96e23b618e45fd25b7d4995099..1be38b0ece20ac11d069018901a3d69458914395 100644
--- a/civicrm/CRM/Utils/AutoClean.php
+++ b/civicrm/CRM/Utils/AutoClean.php
@@ -29,14 +29,14 @@ class CRM_Utils_AutoClean {
   /**
    * Call a cleanup function when the current context shuts down.
    *
-   * @code
+   * ```
    * function doStuff() {
    *   $ac = CRM_Utils_AutoClean::with(function(){
    *     MyCleanup::doIt();
    *   });
    *   ...
    * }
-   * @endcode
+   * ```
    *
    * @param mixed $callback
    * @return CRM_Utils_AutoClean
@@ -52,12 +52,12 @@ class CRM_Utils_AutoClean {
    * Temporarily swap values using callback functions, and cleanup
    * when the current context shuts down.
    *
-   * @code
+   * ```
    * function doStuff() {
    *   $ac = CRM_Utils_AutoClean::swap('My::get', 'My::set', 'tmpValue');
    *   ...
    * }
-   * @endcode
+   * ```
    *
    * @param mixed $getter
    *   Function to lookup current value.
diff --git a/civicrm/CRM/Utils/Cache/SqlGroup.php b/civicrm/CRM/Utils/Cache/SqlGroup.php
index 3f499aa93022ff201cc8c61be5d797772f240c9d..8d0205a8e666d42d601c1440a307c301bc1b91ce 100644
--- a/civicrm/CRM/Utils/Cache/SqlGroup.php
+++ b/civicrm/CRM/Utils/Cache/SqlGroup.php
@@ -99,7 +99,12 @@ class CRM_Utils_Cache_SqlGroup implements CRM_Utils_Cache_Interface {
    * @param string $key
    * @param mixed $value
    * @param null|int|\DateInterval $ttl
+   *
    * @return bool
+   *
+   * @throws \CRM_Core_Exception
+   * @throws \CRM_Utils_Cache_CacheException
+   * @throws \CRM_Utils_Cache_InvalidArgumentException
    */
   public function set($key, $value, $ttl = NULL) {
     CRM_Utils_Cache::assertValidKey($key);
@@ -127,18 +132,18 @@ class CRM_Utils_Cache_SqlGroup implements CRM_Utils_Cache_Interface {
         2 => [time(), 'Positive'],
         3 => [$expires, 'Positive'],
       ];
-      $dao = CRM_Core_DAO::executeQuery($sql, $args, FALSE, NULL, FALSE, FALSE);
+      CRM_Core_DAO::executeQuery($sql, $args, TRUE, NULL, FALSE, FALSE);
     }
     else {
       $sql = "INSERT INTO {$this->table} (group_name,path,data,created_date,expired_date) VALUES (%1,%2,%3,FROM_UNIXTIME(%4),FROM_UNIXTIME(%5))";
       $args = [
-        1 => [$this->group, 'String'],
+        1 => [(string) $this->group, 'String'],
         2 => [$key, 'String'],
         3 => [$dataSerialized, 'String'],
         4 => [time(), 'Positive'],
         5 => [$expires, 'Positive'],
       ];
-      $dao = CRM_Core_DAO::executeQuery($sql, $args, FALSE, NULL, FALSE, FALSE);
+      CRM_Core_DAO::executeQuery($sql, $args, TRUE, NULL, FALSE, FALSE);
     }
 
     $lock->release();
@@ -153,6 +158,8 @@ class CRM_Utils_Cache_SqlGroup implements CRM_Utils_Cache_Interface {
    * @param mixed $default
    *
    * @return mixed
+   *
+   * @throws \CRM_Utils_Cache_InvalidArgumentException
    */
   public function get($key, $default = NULL) {
     CRM_Utils_Cache::assertValidKey($key);
@@ -167,12 +174,17 @@ class CRM_Utils_Cache_SqlGroup implements CRM_Utils_Cache_Interface {
     return (isset($this->expiresCache[$key]) && time() < $this->expiresCache[$key]) ? $this->reobjectify($this->valueCache[$key]) : $default;
   }
 
+  /**
+   * @param mixed $value
+   *
+   * @return object
+   */
   private function reobjectify($value) {
     return is_object($value) ? unserialize(serialize($value)) : $value;
   }
 
   /**
-   * @param $key
+   * @param string $key
    * @param null $default
    *
    * @return mixed
@@ -193,7 +205,9 @@ class CRM_Utils_Cache_SqlGroup implements CRM_Utils_Cache_Interface {
 
   /**
    * @param string $key
+   *
    * @return bool
+   * @throws \CRM_Utils_Cache_InvalidArgumentException
    */
   public function delete($key) {
     CRM_Utils_Cache::assertValidKey($key);
diff --git a/civicrm/CRM/Utils/Check/Component/Env.php b/civicrm/CRM/Utils/Check/Component/Env.php
index 1cbf084c60ac59717627dc0e4cb39c00f55e6c94..1f63ec83e7d2c507ea5edb72c46fc5c1e1326d07 100644
--- a/civicrm/CRM/Utils/Check/Component/Env.php
+++ b/civicrm/CRM/Utils/Check/Component/Env.php
@@ -739,10 +739,8 @@ class CRM_Utils_Check_Component_Env extends CRM_Utils_Check_Component {
       );
     }
     else {
-      $codeVersion = CRM_Utils_System::version();
-
       // if db.ver < code.ver, time to upgrade
-      if (version_compare($dbVersion, $codeVersion) < 0) {
+      if (CRM_Core_BAO_Domain::isDBUpdateRequired()) {
         $messages[] = new CRM_Utils_Check_Message(
           __FUNCTION__,
           ts('New codebase version detected. You must visit <a href=\'%1\'>upgrade screen</a> to upgrade the database.', [1 => $upgradeUrl]),
@@ -753,7 +751,7 @@ class CRM_Utils_Check_Component_Env extends CRM_Utils_Check_Component {
       }
 
       // if db.ver > code.ver, sth really wrong
-      if (version_compare($dbVersion, $codeVersion) > 0) {
+      if (version_compare($dbVersion, CRM_Utils_System::version()) > 0) {
         $messages[] = new CRM_Utils_Check_Message(
           __FUNCTION__,
           ts('Your database is marked with an unexpected version number: %1. The v%2 codebase may not be compatible with your database state.
@@ -986,4 +984,18 @@ class CRM_Utils_Check_Component_Env extends CRM_Utils_Check_Component {
     return $messages;
   }
 
+  public function checkPHPIntlExists() {
+    $messages = [];
+    if (!extension_loaded('intl')) {
+      $messages[] = new CRM_Utils_Check_Message(
+        __FUNCTION__,
+        ts('This system currently does not have the PHP-Intl extension enabled.  Please contact your system administrator about getting the extension enabled.'),
+        ts('Missing PHP Extension: INTL'),
+        \Psr\Log\LogLevel::WARNING,
+        'fa-server'
+      );
+    }
+    return $messages;
+  }
+
 }
diff --git a/civicrm/CRM/Utils/Check/Component/Schema.php b/civicrm/CRM/Utils/Check/Component/Schema.php
index 12d17d22030f4550b143b01e5604b54b50ed397f..c1333dfa051e83ae91314d66366b5eaa268fcd0a 100644
--- a/civicrm/CRM/Utils/Check/Component/Schema.php
+++ b/civicrm/CRM/Utils/Check/Component/Schema.php
@@ -94,6 +94,10 @@ class CRM_Utils_Check_Component_Schema extends CRM_Utils_Check_Component {
    * @return array
    */
   public function checkSmartGroupCustomFieldCriteria() {
+    if (CRM_Core_BAO_Domain::isDBUpdateRequired()) {
+      // Do not run this check when the db has not been updated as it might fail on non-updated schema issues.
+      return [];
+    }
     $messages = $problematicSG = [];
     $customFieldIds = array_keys(CRM_Core_BAO_CustomField::getFields('ANY', FALSE, FALSE, NULL, NULL, FALSE, FALSE, FALSE));
     try {
@@ -153,8 +157,8 @@ class CRM_Utils_Check_Component_Schema extends CRM_Utils_Check_Component {
             $fieldName = ' <span style="color:red"> - Deleted - </span> ';
           }
         }
-        $groupEdit = '<a href="' . CRM_Utils_System::url('civicrm/contact/search/advanced', "?reset=1&ssID={$field['ssid']}", TRUE) . '" title="' . ts('Edit search criteria') . '"> <i class="crm-i fa-pencil"></i> </a>';
-        $groupConfig = '<a href="' . CRM_Utils_System::url('civicrm/group', "?reset=1&action=update&id={$id}", TRUE) . '" title="' . ts('Group settings') . '"> <i class="crm-i fa-gear"></i> </a>';
+        $groupEdit = '<a href="' . CRM_Utils_System::url('civicrm/contact/search/advanced', "?reset=1&ssID={$field['ssid']}", TRUE) . '" title="' . ts('Edit search criteria') . '"> <i class="crm-i fa-pencil" aria-hidden="true"></i> </a>';
+        $groupConfig = '<a href="' . CRM_Utils_System::url('civicrm/group', "?reset=1&action=update&id={$id}", TRUE) . '" title="' . ts('Group settings') . '"> <i class="crm-i fa-gear" aria-hidden="true"></i> </a>';
         $html .= "<tr><td>{$id} - {$field['title']} </td><td>{$groupEdit} {$groupConfig}</td><td class='disabled'>{$fieldName}</td>";
       }
 
@@ -183,8 +187,11 @@ class CRM_Utils_Check_Component_Schema extends CRM_Utils_Check_Component {
     if (CRM_Core_Config::singleton()->moneyvalueformat !== '%!i') {
       $msg = new CRM_Utils_Check_Message(
         __FUNCTION__,
-        ts('<p>The Money Value format stored is deprecated please report your configuration on <a href="https://lab.civicrm.org/dev/core/-/issues/1494">Gitlab Issue</a>'),
-        ts('Deprectad money value format configuration'),
+        ts(
+          '<p>The Monetary Value Display format is a deprecated setting, and this site has a non-standard format. Please report your configuration on <a href="%1">this Gitlab issue</a>.',
+          [1 => 'https://lab.civicrm.org/dev/core/-/issues/1494']
+        ),
+        ts('Deprecated monetary value display format configuration'),
         \Psr\Log\LogLevel::WARNING,
         'fa-server'
       );
diff --git a/civicrm/CRM/Utils/ConsoleTee.php b/civicrm/CRM/Utils/ConsoleTee.php
index 75cb7eaec82df2092869cdc4cd54c5b259badfbc..f7adb10695b1c6a6272bfe0fec1d3cc707a91752 100644
--- a/civicrm/CRM/Utils/ConsoleTee.php
+++ b/civicrm/CRM/Utils/ConsoleTee.php
@@ -12,12 +12,12 @@
 /**
  * Capture the output from the console, copy it to a file, and pass it on.
  *
- * @code
+ * ```
  * $tee = CRM_Utils_ConsoleTee::create()->start();
  * echo "hello world";
  * $tee->stop();
  * assertEquals("hello world", file_get_contents($tee->getFileName()));
- * @endCode
+ * ```
  *
  * Loosely speaking, it serves a similar purpose to Unix `tee`.
  *
diff --git a/civicrm/CRM/Utils/FakeObject.php b/civicrm/CRM/Utils/FakeObject.php
index fef296edbcdcf0a548534ef565e97a66f2ec38bc..a20246fe6b200ecc87f11514c4e31c375de1e637 100644
--- a/civicrm/CRM/Utils/FakeObject.php
+++ b/civicrm/CRM/Utils/FakeObject.php
@@ -19,12 +19,12 @@
  * This is a quick-and-dirty way to define a vaguely-class-ish structure. It's non-performant, abnormal,
  * and not a complete OOP system. Only use for testing/mocking.
  *
- * @code
+ * ```
  * $object = new CRM_Utils_FakeObject(array(
  *   'doIt' => function() {  print "It!\n"; }
  * ));
  * $object->doIt();
- * @endcode
+ * ```
  */
 class CRM_Utils_FakeObject {
 
diff --git a/civicrm/CRM/Utils/Geocode/Google.php b/civicrm/CRM/Utils/Geocode/Google.php
index 1cb0c847ca5610ab83b7885dcf5ca717742853a5..6ab1397fea8d8be13c67b0ca51c570f4dff30798 100644
--- a/civicrm/CRM/Utils/Geocode/Google.php
+++ b/civicrm/CRM/Utils/Geocode/Google.php
@@ -105,10 +105,9 @@ class CRM_Utils_Geocode_Google {
 
     $query = 'https://' . self::$_server . self::$_uri . $add;
 
-    require_once 'HTTP/Request.php';
-    $request = new HTTP_Request($query);
-    $request->sendRequest();
-    $string = $request->getResponseBody();
+    $client = new GuzzleHttp\Client();
+    $request = $client->request('GET', $query);
+    $string = $request->getBody();
 
     libxml_use_internal_errors(TRUE);
     $xml = @simplexml_load_string($string);
diff --git a/civicrm/CRM/Utils/GlobalStack.php b/civicrm/CRM/Utils/GlobalStack.php
index 047de04c3c7d1038f12fe87ea6408546468626c5..50403d9546ea2018b1ca2cd62b91dc849918c4fb 100644
--- a/civicrm/CRM/Utils/GlobalStack.php
+++ b/civicrm/CRM/Utils/GlobalStack.php
@@ -17,7 +17,7 @@
 /**
  * Temporarily change a global variable.
  *
- * @code
+ * ```
  * $globals = CRM_Utils_GlobalStack::singleton();
  * $globals->push(array(
  *   '_GET' => array(
@@ -26,7 +26,7 @@
  * ));
  * ...do stuff...
  * $globals->pop();
- * @endcode
+ * ```
  *
  * Note: for purposes of this class, we'll refer to the array passed into
  * push() as a frame.
diff --git a/civicrm/CRM/Utils/Hook.php b/civicrm/CRM/Utils/Hook.php
index 6e8a7b59100384c26592473f327d15b69daddd1a..32f01c2f3ff3dca8cd8bb4b12909a3fe0174678e 100644
--- a/civicrm/CRM/Utils/Hook.php
+++ b/civicrm/CRM/Utils/Hook.php
@@ -1162,7 +1162,7 @@ abstract class CRM_Utils_Hook {
    *   See discussion in CRM-16224 as to whether $paymentObj should be passed by reference.
    * @param array &$rawParams
    *    array of params as passed to to the processor
-   * @param array &$cookedParams
+   * @param array|\Civi\Payment\PropertyBag &$cookedParams
    *     params after the processor code has translated them into its own key/value pairs
    *
    * @return mixed
@@ -2234,10 +2234,8 @@ abstract class CRM_Utils_Hook {
    *      If omitted, default to "array('civicrm/a')" for backward compat.
    *      For a utility that should only be loaded on-demand, use "array()".
    *      For a utility that should be loaded in all pages use, "array('*')".
-   * @return null
-   *   the return value is ignored
    *
-   * @code
+   * ```
    * function mymod_civicrm_angularModules(&$angularModules) {
    *   $angularModules['myAngularModule'] = array(
    *     'ext' => 'org.example.mymod',
@@ -2252,7 +2250,10 @@ abstract class CRM_Utils_Hook {
    *     'basePages' => array('civicrm/a'),
    *   );
    * }
-   * @endcode
+   * ```
+   *
+   * @return null
+   *   the return value is ignored
    */
   public static function angularModules(&$angularModules) {
     return self::singleton()->invoke(['angularModules'], $angularModules,
@@ -2266,7 +2267,7 @@ abstract class CRM_Utils_Hook {
    *
    * @param \Civi\Angular\Manager $angular
    *
-   * @code
+   * ```
    * function example_civicrm_alterAngular($angular) {
    *   $changeSet = \Civi\Angular\ChangeSet::create('mychanges')
    *     ->alterHtml('~/crmMailing/EditMailingCtrl/2step.html', function(phpQueryObject $doc) {
@@ -2275,7 +2276,7 @@ abstract class CRM_Utils_Hook {
    *   );
    *   $angular->add($changeSet);
    * }
-   * @endCode
+   * ```
    */
   public static function alterAngular($angular) {
     $event = \Civi\Core\Event\GenericHookEvent::create([
@@ -2365,7 +2366,7 @@ abstract class CRM_Utils_Hook {
   /**
    * Modify the CiviCRM container - add new services, parameters, extensions, etc.
    *
-   * @code
+   * ```
    * use Symfony\Component\Config\Resource\FileResource;
    * use Symfony\Component\DependencyInjection\Definition;
    *
@@ -2373,7 +2374,7 @@ abstract class CRM_Utils_Hook {
    *   $container->addResource(new FileResource(__FILE__));
    *   $container->setDefinition('mysvc', new Definition('My\Class', array()));
    * }
-   * @endcode
+   * ```
    *
    * Tip: The container configuration will be compiled/cached. The default cache
    * behavior is aggressive. When you first implement the hook, be sure to
diff --git a/civicrm/CRM/Utils/Migrate/Export.php b/civicrm/CRM/Utils/Migrate/Export.php
index 76a440e26a2cd2bbc6dc3e7b07701db8e9ee1337..7dc6b17d7f67b6babea222205437a2a21f9414bb 100644
--- a/civicrm/CRM/Utils/Migrate/Export.php
+++ b/civicrm/CRM/Utils/Migrate/Export.php
@@ -22,7 +22,7 @@ class CRM_Utils_Migrate_Export {
    * @var array
    * Description of export field mapping
    *
-   * @code
+   * ```
    * 'exampleEntityMappingName' => array(
    *   'data' => array(),                     // placeholder; this will get filled-in during execution
    *   'name' => 'CustomGroup',               // per-item XML tag name
@@ -31,7 +31,7 @@ class CRM_Utils_Migrate_Export {
    *   'idNameFields' => array('id', 'name'), // name of the (local/autogenerated) "id" and (portable) "name" columns
    *   'idNameMap' => array(),                // placeholder; this will get filled-in during execution
    * ),
-   * @endcode
+   * ```
    */
   protected $_xml;
 
diff --git a/civicrm/CRM/Utils/PagerAToZ.php b/civicrm/CRM/Utils/PagerAToZ.php
index c9e15a636acb3f916ccaa53e95eb801e2f16bcd3..eeed2e961ab2fbb5d83038c1db29e593b919b75f 100644
--- a/civicrm/CRM/Utils/PagerAToZ.php
+++ b/civicrm/CRM/Utils/PagerAToZ.php
@@ -135,9 +135,7 @@ class CRM_Utils_PagerAToZ {
       $qfKey = $query->_formValues['qfKey'] ?? NULL;
     }
     if (empty($qfKey)) {
-      // CRM-20943 Can only pass variables by reference and also cannot use $this so using $empty setting to NULL which is default.
-      $emptyVariable = NULL;
-      $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String', $emptyVariable, FALSE, NULL, $_REQUEST);
+      $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String');
     }
 
     $aToZBar = [];
diff --git a/civicrm/CRM/Utils/SQL/BaseParamQuery.php b/civicrm/CRM/Utils/SQL/BaseParamQuery.php
index 159f56a5fad60f2832034fc54a100a36023f2d06..20125db2bdad4d4f45e991306069888f5655abd0 100644
--- a/civicrm/CRM/Utils/SQL/BaseParamQuery.php
+++ b/civicrm/CRM/Utils/SQL/BaseParamQuery.php
@@ -191,11 +191,11 @@ class CRM_Utils_SQL_BaseParamQuery implements ArrayAccess {
   /**
    * Get the value of a SQL parameter.
    *
-   * @code
+   * ```
    *   $select['cid'] = 123;
    *   $select->where('contact.id = #cid');
    *   echo $select['cid'];
-   * @endCode
+   * ```
    *
    * @param string $offset
    * @return mixed
@@ -209,11 +209,11 @@ class CRM_Utils_SQL_BaseParamQuery implements ArrayAccess {
   /**
    * Set the value of a SQL parameter.
    *
-   * @code
+   * ```
    *   $select['cid'] = 123;
    *   $select->where('contact.id = #cid');
    *   echo $select['cid'];
-   * @endCode
+   * ```
    *
    * @param string $offset
    * @param mixed $value
diff --git a/civicrm/CRM/Utils/SQL/Delete.php b/civicrm/CRM/Utils/SQL/Delete.php
index 6bbc076691c71146d757d848c3d1fdddd0ee7bdc..fcb5443030cc6afe153a3548f977cad9dba35cb3 100644
--- a/civicrm/CRM/Utils/SQL/Delete.php
+++ b/civicrm/CRM/Utils/SQL/Delete.php
@@ -13,7 +13,7 @@
  * Dear God Why Do I Have To Write This (Dumb SQL Builder)
  *
  * Usage:
- * @code
+ * ```
  * $del = CRM_Utils_SQL_Delete::from('civicrm_activity act')
  *     ->where('activity_type_id = #type', array('type' => 234))
  *     ->where('status_id IN (#statuses)', array('statuses' => array(1,2,3))
@@ -24,7 +24,7 @@
  *        'value' => $form['foo']
  *      ))
  * echo $del->toSQL();
- * @endcode
+ * ```
  *
  * Design principles:
  *  - Portable
@@ -48,7 +48,7 @@
  * xor output. The notations for input and output interpolation are a bit different,
  * and they may not be mixed.
  *
- * @code
+ * ```
  * // Interpolate on input. Set params when using them.
  * $select->where('activity_type_id = #type', array(
  *   'type' => 234,
@@ -58,7 +58,7 @@
  * $select
  *     ->where('activity_type_id = #type')
  *     ->param('type', 234),
- * @endcode
+ * ```
  *
  * @package CRM
  * @copyright CiviCRM LLC https://civicrm.org/licensing
diff --git a/civicrm/CRM/Utils/SQL/Select.php b/civicrm/CRM/Utils/SQL/Select.php
index 47707a316fd8727a4d31f95eddcd6e064325e8e7..4aa6b7e5166974cd334df51fb6b1f27c0dd986b5 100644
--- a/civicrm/CRM/Utils/SQL/Select.php
+++ b/civicrm/CRM/Utils/SQL/Select.php
@@ -13,7 +13,7 @@
  * Dear God Why Do I Have To Write This (Dumb SQL Builder)
  *
  * Usage:
- * @code
+ * ```
  * $select = CRM_Utils_SQL_Select::from('civicrm_activity act')
  *     ->join('absence', 'inner join civicrm_activity absence on absence.id = act.source_record_id')
  *     ->where('activity_type_id = #type', array('type' => 234))
@@ -25,7 +25,7 @@
  *        'value' => $form['foo']
  *      ))
  * echo $select->toSQL();
- * @endcode
+ * ```
  *
  * Design principles:
  *  - Portable
@@ -49,7 +49,7 @@
  * xor output. The notations for input and output interpolation are a bit different,
  * and they may not be mixed.
  *
- * @code
+ * ```
  * // Interpolate on input. Set params when using them.
  * $select->where('activity_type_id = #type', array(
  *   'type' => 234,
@@ -59,7 +59,7 @@
  * $select
  *     ->where('activity_type_id = #type')
  *     ->param('type', 234),
- * @endcode
+ * ```
  *
  * @package CRM
  * @copyright CiviCRM LLC https://civicrm.org/licensing
diff --git a/civicrm/CRM/Utils/Signer.php b/civicrm/CRM/Utils/Signer.php
index eb902bc18949f199adac6968755bad5d42c4c706..2b6491f9d0b778b7d1bc931a045a1c3393cf68d6 100644
--- a/civicrm/CRM/Utils/Signer.php
+++ b/civicrm/CRM/Utils/Signer.php
@@ -20,7 +20,7 @@
  *
  * FIXME: Add TTL support?
  *
- * @code
+ * ```
  * $signer = new CRM_Utils_Signer('myprivatekey', array('param1','param2'));
  * $params = array(
  *   'param1' => 'hello',
@@ -29,7 +29,7 @@
  * $token = $signer->sign($params);
  * ...
  * assertTrue($signer->validate($token, $params));
- * @endcode
+ * ```
  */
 class CRM_Utils_Signer {
   /**
diff --git a/civicrm/CRM/Utils/String.php b/civicrm/CRM/Utils/String.php
index 18d4904b1f161c5c99df730b435eb535a86c57e4..e0cac9a763ba167433a6c45677831032d4f5e4cf 100644
--- a/civicrm/CRM/Utils/String.php
+++ b/civicrm/CRM/Utils/String.php
@@ -86,37 +86,17 @@ class CRM_Utils_String {
   }
 
   /**
-   * Convert possibly underscore separated words to camel case with special handling for 'UF'
-   * e.g membership_payment returns MembershipPayment
-   *
-   * @param string $string
+   * Convert possibly underscore separated words to camel case.
    *
+   * @param string $str
+   * @param bool $ucFirst
+   *   Should the first letter be capitalized like `CamelCase` or lower like `camelCase`
    * @return string
    */
-  public static function convertStringToCamel($string) {
-    $map = [
-      'acl' => 'Acl',
-      'ACL' => 'Acl',
-      'im' => 'Im',
-      'IM' => 'Im',
-    ];
-    if (isset($map[$string])) {
-      return $map[$string];
-    }
-
-    $fragments = explode('_', $string);
-    foreach ($fragments as & $fragment) {
-      $fragment = ucfirst($fragment);
-      // Special case: UFGroup, UFJoin, UFMatch, UFField (if passed in without underscores)
-      if (strpos($fragment, 'Uf') === 0 && strlen($string) > 2) {
-        $fragment = 'UF' . ucfirst(substr($fragment, 2));
-      }
-    }
-    // Special case: UFGroup, UFJoin, UFMatch, UFField (if passed in underscore-separated)
-    if ($fragments[0] === 'Uf') {
-      $fragments[0] = 'UF';
-    }
-    return implode('', $fragments);
+  public static function convertStringToCamel($str, $ucFirst = TRUE) {
+    $fragments = explode('_', $str);
+    $camel = implode('', array_map('ucfirst', $fragments));
+    return $ucFirst ? $camel : lcfirst($camel);
   }
 
   /**
diff --git a/civicrm/CRM/Utils/System.php b/civicrm/CRM/Utils/System.php
index 8713cf27fad5a827a5ad58dfa4a742fafc0da92a..11e9eb1710a36614b1eac5004c53087cc1545eba 100644
--- a/civicrm/CRM/Utils/System.php
+++ b/civicrm/CRM/Utils/System.php
@@ -318,6 +318,41 @@ class CRM_Utils_System {
     return urldecode(CRM_Utils_Url::unparseUrl($event->url));
   }
 
+  /**
+   * Perform any current conversions/migrations on the extern URL.
+   *
+   * @param \Civi\Core\Event\GenericHookEvent $e
+   * @see CRM_Utils_Hook::alterExternUrl
+   */
+  public static function migrateExternUrl(\Civi\Core\Event\GenericHookEvent $e) {
+
+    /**
+     * $mkRouteUri is a small adapter to return generated URL as a "UriInterface".
+     * @param string $path
+     * @param string $query
+     * @return \Psr\Http\Message\UriInterface
+     */
+    $mkRouteUri = function ($path, $query) use ($e) {
+      $urlTxt = CRM_Utils_System::url($path, $query, $e->absolute, $e->fragment, FALSE);
+      if ($e->isSSL || ($e->isSSL === NULL && \CRM_Utils_System::isSSL())) {
+        $urlTxt = str_replace('http://', 'https://', $urlTxt);
+      }
+      return CRM_Utils_Url::parseUrl($urlTxt);
+    };
+
+    switch (Civi::settings()->get('defaultExternUrl') . ':' . $e->path) {
+      case 'router:extern/open':
+        $e->url = $mkRouteUri('civicrm/mailing/open', preg_replace('/(^|&)q=/', '\1qid=', $e->query));
+        break;
+
+      case 'router:extern/url':
+        $e->url = $mkRouteUri('civicrm/mailing/url', $e->query);
+        break;
+
+      // Otherwise, keep the default.
+    }
+  }
+
   /**
    * @deprecated
    * @see \CRM_Utils_System::currentPath
diff --git a/civicrm/CRM/Utils/System/Drupal.php b/civicrm/CRM/Utils/System/Drupal.php
index f0c8cdbc7de24004bd2f7fcf594ca6a4ff0cee0f..bf4f8693cf5c72af1dbdeed7f5f3dd6121516c6e 100644
--- a/civicrm/CRM/Utils/System/Drupal.php
+++ b/civicrm/CRM/Utils/System/Drupal.php
@@ -72,6 +72,19 @@ class CRM_Utils_System_Drupal extends CRM_Utils_System_DrupalBase {
     return $form_state['user']->uid;
   }
 
+  /**
+   * Appends a Drupal 7 Javascript file when the CRM Menubar Javascript file has
+   * been included. The file is added before the menu bar so we can properly listen
+   * for the menu bar ready event.
+   */
+  public function appendCoreResources(\Civi\Core\Event\GenericHookEvent $event) {
+    $menuBarFileIndex = array_search('js/crm.menubar.js', $event->list);
+
+    if ($menuBarFileIndex !== FALSE) {
+      array_splice($event->list, $menuBarFileIndex, 0, ['js/crm.drupal7.js']);
+    }
+  }
+
   /**
    * @inheritDoc
    */
diff --git a/civicrm/CRM/Utils/System/Drupal8.php b/civicrm/CRM/Utils/System/Drupal8.php
index 82fe4d85e47fd4f8b6442937310e08d081b29574..6e42f703b8814c1f36bd5ecf6372bcea2ff57ae9 100644
--- a/civicrm/CRM/Utils/System/Drupal8.php
+++ b/civicrm/CRM/Utils/System/Drupal8.php
@@ -654,13 +654,13 @@ class CRM_Utils_System_Drupal8 extends CRM_Utils_System_DrupalBase {
    *
    * For example, 'civicrm/contact/view?reset=1&cid=66' will be returned as:
    *
-   * @code
+   * ```
    * array(
    *   'path' => 'civicrm/contact/view',
    *   'route' => 'civicrm.civicrm_contact_view',
    *   'query' => array('reset' => '1', 'cid' => '66'),
    * );
-   * @endcode
+   * ```
    *
    * @param string $url
    *   The url to parse.
diff --git a/civicrm/CRM/Utils/System/WordPress.php b/civicrm/CRM/Utils/System/WordPress.php
index 873020ce537cd96b2fb6c4a5252ef89872be99c0..d844e98b45aa880c8cf8c244224ff6da5698ed93 100644
--- a/civicrm/CRM/Utils/System/WordPress.php
+++ b/civicrm/CRM/Utils/System/WordPress.php
@@ -35,6 +35,87 @@ class CRM_Utils_System_WordPress extends CRM_Utils_System_Base {
     $this->is_wordpress = TRUE;
   }
 
+  public function initialize() {
+    parent::initialize();
+    $this->registerPathVars();
+  }
+
+  /**
+   * Specify the default computation for various paths/URLs.
+   */
+  protected function registerPathVars():void {
+    $isNormalBoot = function_exists('get_option');
+    if ($isNormalBoot) {
+      // Normal mode - CMS boots first, then calls Civi. "Normal" web pages and newer extern routes.
+      // To simplify the code-paths, some items are re-registered with WP-specific functions.
+      $cmsRoot = function() {
+        return [
+          'path' => untrailingslashit(ABSPATH),
+          'url' => home_url(),
+        ];
+      };
+      Civi::paths()->register('cms', $cmsRoot);
+      Civi::paths()->register('cms.root', $cmsRoot);
+      Civi::paths()->register('civicrm.files', function () {
+        $upload_dir = wp_get_upload_dir();
+        return [
+          'path' => $upload_dir['basedir'] . DIRECTORY_SEPARATOR . 'civicrm' . DIRECTORY_SEPARATOR,
+          'url' => $upload_dir['baseurl'] . '/civicrm/',
+        ];
+      });
+      Civi::paths()->register('civicrm.root', function () {
+        return [
+          'path' => CIVICRM_PLUGIN_DIR . 'civicrm' . DIRECTORY_SEPARATOR,
+          'url' => CIVICRM_PLUGIN_URL . 'civicrm/',
+        ];
+      });
+      Civi::paths()->register('wp.frontend.base', function () {
+        return [
+          'url' => home_url('/'),
+        ];
+      });
+      Civi::paths()->register('wp.frontend', function () {
+        $config = CRM_Core_Config::singleton();
+        $basepage = get_page_by_path($config->wpBasePage);
+        return [
+          'url' => get_permalink($basepage->ID),
+        ];
+      });
+      Civi::paths()->register('wp.backend.base', function () {
+        return [
+          'url' => admin_url(),
+        ];
+      });
+      Civi::paths()->register('wp.backend', function() {
+        return [
+          'url' => admin_url('admin.php'),
+        ];
+      });
+    }
+    else {
+      // Legacy support - only relevant for older extern routes.
+      Civi::paths()
+        ->register('wp.frontend.base', function () {
+          return ['url' => rtrim(CIVICRM_UF_BASEURL, '/') . '/'];
+        })
+        ->register('wp.frontend', function () {
+          $config = \CRM_Core_Config::singleton();
+          $suffix = defined('CIVICRM_UF_WP_BASEPAGE') ? CIVICRM_UF_WP_BASEPAGE : $config->wpBasePage;
+          return [
+            'url' => Civi::paths()->getVariable('wp.frontend.base', 'url') . $suffix,
+          ];
+        })
+        ->register('wp.backend.base', function () {
+          return ['url' => rtrim(CIVICRM_UF_BASEURL, '/') . '/wp-admin/'];
+        })
+        ->register('wp.backend', function () {
+          return [
+            'url' => Civi::paths()->getVariable('wp.backend.base', 'url') . 'admin.php',
+          ];
+        });
+    }
+  }
+
   /**
    * @inheritDoc
    */
diff --git a/civicrm/CRM/Utils/Time.php b/civicrm/CRM/Utils/Time.php
index dd4b4e2cca6100321cfc29eeb81d8eda11617a31..b700e572071092ee7c9f1462b914613b876c406f 100644
--- a/civicrm/CRM/Utils/Time.php
+++ b/civicrm/CRM/Utils/Time.php
@@ -21,10 +21,14 @@
 class CRM_Utils_Time {
 
   /**
-   * @var int
-   *   the seconds offset from the real world time
+   * A function which determines the current time.
+   * Only used during testing (with mocked time).
+   *
+   * The normal value, NULL, indicates the use of real time.
+   *
+   * @var callable|null
    */
-  static private $_delta = 0;
+  static private $callback = NULL;
 
   /**
    * Get the time.
@@ -45,7 +49,7 @@ class CRM_Utils_Time {
    *   seconds since epoch
    */
   public static function getTimeRaw() {
-    return time() + self::$_delta;
+    return self::$callback === NULL ? time() : call_user_func(self::$callback);
   }
 
   /**
@@ -56,10 +60,61 @@ class CRM_Utils_Time {
    * @param string $returnFormat
    *   Format in which date is to be retrieved.
    *
+   * Note: The progression of time will be influenced by TIME_FUNC, which may be:
+   *   - 'frozen' (time does not move)
+   *   - 'natural' (time moves naturally)
+   *   - 'linear:XXX' (time moves in increments of XXX milliseconds - with every lookup)
+   *   - 'prng:XXX' (time moves by random increments, between 0 and XXX milliseconds)
    * @return string
    */
   public static function setTime($newDateTime, $returnFormat = 'YmdHis') {
-    self::$_delta = strtotime($newDateTime) - time();
+    $mode = getenv('TIME_FUNC') ? getenv('TIME_FUNC') : 'natural';
+
+    list ($modeName, $modeNum) = explode(":", "$mode:");
+
+    switch ($modeName) {
+      case 'frozen':
+        // Every getTime() will produce the same value (ie $newDateTime).
+        $now = strtotime($newDateTime);
+        self::$callback = function () use ($now) {
+          return $now;
+        };
+        break;
+
+      case 'natural':
+        // Time changes to $newDateTime and then proceeds naturally.
+        $delta = strtotime($newDateTime) - time();
+        self::$callback = function () use ($delta) {
+          return time() + $delta;
+        };
+        break;
+
+      case 'linear':
+        // Time changes to $newDateTime and then proceeds in fixed increments ($modeNum milliseconds).
+        $incr = ($modeNum / 1000.0);
+        $now = (float) strtotime($newDateTime) - $incr;
+        self::$callback = function () use (&$now, $incr) {
+          $now += $incr;
+          return floor($now);
+        };
+        break;
+
+      case 'prng':
+        // Time changes to $newDateTime and then proceeds using deterministic pseudorandom increments (of up to $modeNum milliseconds).
+        $seed = md5($newDateTime . chr(0) . $mode, TRUE);
+        $now = (float) strtotime($newDateTime);
+        self::$callback = function () use (&$seed, &$now, $modeNum) {
+          $mod = gmp_strval(gmp_mod(gmp_import($seed), "$modeNum"));
+          $seed = md5($seed . $now, TRUE);
+          $now = $now + ($mod / 1000.0);
+          return floor($now);
+        };
+        break;
+
+      default:
+        throw new \RuntimeException("Unrecognized TIME_FUNC ($mode)");
+    }
+
     return self::getTime($returnFormat);
   }
 
@@ -67,7 +122,7 @@ class CRM_Utils_Time {
    * Remove any time overrides.
    */
   public static function resetTime() {
-    self::$_delta = 0;
+    self::$callback = NULL;
   }
 
   /**
diff --git a/civicrm/CRM/Utils/Url.php b/civicrm/CRM/Utils/Url.php
index 1b73c1dc7e3cef38f4fcfb5530444f36e728949f..51340c3f723159843053f720f4ac01fd27236649 100644
--- a/civicrm/CRM/Utils/Url.php
+++ b/civicrm/CRM/Utils/Url.php
@@ -19,7 +19,7 @@ class CRM_Utils_Url {
    *
    * @param string $url
    *
-   * @return \GuzzleHttp\Psr7\UriInterface
+   * @return \Psr\Http\Message\UriInterface
    */
   public static function parseUrl($url) {
     return new Uri($url);
@@ -28,7 +28,7 @@ class CRM_Utils_Url {
   /**
    * Unparse url back to a string.
    *
-   * @param \GuzzleHttp\Psr7\UriInterface $parsed
+   * @param \Psr\Http\Message\UriInterface $parsed
    *
    * @return string
    */
diff --git a/civicrm/Civi.php b/civicrm/Civi.php
index ebd09e8afec737e387d067a398a7a6c79e51f439..95009e35c3fd3df36ba03e43dca7b844d78b9818 100644
--- a/civicrm/Civi.php
+++ b/civicrm/Civi.php
@@ -19,11 +19,11 @@ class Civi {
   /**
    * A central location for static variable storage.
    * @var array
-   * @code
+   * ```
    * `Civi::$statics[__CLASS__]['foo'] = 'bar';
-   * @endcode
+   * ```
    */
-  public static $statics = array();
+  public static $statics = [];
 
   /**
    * Retrieve a named cache instance.
@@ -106,7 +106,7 @@ class Civi {
    * singletons, containers.
    */
   public static function reset() {
-    self::$statics = array();
+    self::$statics = [];
     Civi\Core\Container::singleton();
   }
 
diff --git a/civicrm/Civi/API/Request.php b/civicrm/Civi/API/Request.php
index 22fa382a7104c953555db1fa02cde56b6e8dc1ca..5d0457ef7d2933bed191870593027c0349824ff5 100644
--- a/civicrm/Civi/API/Request.php
+++ b/civicrm/Civi/API/Request.php
@@ -74,7 +74,7 @@ class Request {
    * @return string
    */
   public static function normalizeEntityName($entity) {
-    return \CRM_Utils_String::convertStringToCamel(\CRM_Utils_String::munge($entity));
+    return \CRM_Core_DAO_AllCoreTables::convertEntityNameToCamel(\CRM_Utils_String::munge($entity), TRUE);
   }
 
   /**
diff --git a/civicrm/Civi/API/SelectQuery.php b/civicrm/Civi/API/SelectQuery.php
index 92e09ad8d1c977ecf446e7988ac464c6da6ded3b..97fac0a1333ea2c435a57780f5bc5e6432314114 100644
--- a/civicrm/Civi/API/SelectQuery.php
+++ b/civicrm/Civi/API/SelectQuery.php
@@ -63,7 +63,7 @@ abstract class SelectQuery {
   /**
    * @var array
    */
-  protected $entityFieldNames;
+  protected $entityFieldNames = [];
   /**
    * @var array
    */
@@ -361,11 +361,11 @@ abstract class SelectQuery {
    * Get acl clause for an entity
    *
    * @param string $tableAlias
-   * @param string $baoName
+   * @param \CRM_Core_DAO|string $baoName
    * @param array $stack
    * @return array
    */
-  protected function getAclClause($tableAlias, $baoName, $stack = []) {
+  public function getAclClause($tableAlias, $baoName, $stack = []) {
     if (!$this->checkPermissions) {
       return [];
     }
diff --git a/civicrm/Civi/API/Subscriber/ChainSubscriber.php b/civicrm/Civi/API/Subscriber/ChainSubscriber.php
index ff4f79256e1a23456f107aa9c9febf708240991a..e76220ca0dc0785a4723b000073865fbb0760e93 100644
--- a/civicrm/Civi/API/Subscriber/ChainSubscriber.php
+++ b/civicrm/Civi/API/Subscriber/ChainSubscriber.php
@@ -18,7 +18,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  * The ChainSubscriber looks for API parameters which specify a nested or
  * chained API call. For example:
  *
- * @code
+ * ```
  * $result = civicrm_api('Contact', 'create', array(
  *   'version' => 3,
  *   'first_name' => 'Amy',
@@ -27,7 +27,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  *     'location_type_id' => 123,
  *   ),
  * ));
- * @endcode
+ * ```
  *
  * The ChainSubscriber looks for any parameters of the form "api.Email.create";
  * if found, it issues the nested API call (and passes some extra context --
diff --git a/civicrm/Civi/API/Subscriber/DynamicFKAuthorization.php b/civicrm/Civi/API/Subscriber/DynamicFKAuthorization.php
index 6a19cded035058c8a9d8ac931e7cfd27e95cda45..3aaa1a89c62ff064b2f8a74a01b7e03f6c87bc08 100644
--- a/civicrm/Civi/API/Subscriber/DynamicFKAuthorization.php
+++ b/civicrm/Civi/API/Subscriber/DynamicFKAuthorization.php
@@ -12,6 +12,7 @@
 namespace Civi\API\Subscriber;
 
 use Civi\API\Events;
+use CRM_Core_DAO_AllCoreTables as AllCoreTables;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
@@ -123,7 +124,7 @@ class DynamicFKAuthorization implements EventSubscriberInterface {
    */
   public function __construct($kernel, $entityName, $actions, $lookupDelegateSql, $lookupCustomFieldSql, $allowedDelegates = NULL) {
     $this->kernel = $kernel;
-    $this->entityName = \CRM_Utils_String::convertStringToCamel($entityName);
+    $this->entityName = AllCoreTables::convertEntityNameToCamel($entityName, TRUE);
     $this->actions = $actions;
     $this->lookupDelegateSql = $lookupDelegateSql;
     $this->lookupCustomFieldSql = $lookupCustomFieldSql;
@@ -138,7 +139,7 @@ class DynamicFKAuthorization implements EventSubscriberInterface {
    */
   public function onApiAuthorize(\Civi\API\Event\AuthorizeEvent $event) {
     $apiRequest = $event->getApiRequest();
-    if ($apiRequest['version'] == 3 && \CRM_Utils_String::convertStringToCamel($apiRequest['entity']) == $this->entityName && in_array(strtolower($apiRequest['action']), $this->actions)) {
+    if ($apiRequest['version'] == 3 && AllCoreTables::convertEntityNameToCamel($apiRequest['entity'], TRUE) == $this->entityName && in_array(strtolower($apiRequest['action']), $this->actions)) {
       if (isset($apiRequest['params']['field_name'])) {
         $fldIdx = \CRM_Utils_Array::index(['field_name'], $this->getCustomFields());
         if (empty($fldIdx[$apiRequest['params']['field_name']])) {
diff --git a/civicrm/Civi/API/WhitelistRule.php b/civicrm/Civi/API/WhitelistRule.php
index c17b41cb2e67ecc082c141589e44edb78cfee215..5150b362f82827bbb946d5dbe1bc8cc990aef636 100644
--- a/civicrm/Civi/API/WhitelistRule.php
+++ b/civicrm/Civi/API/WhitelistRule.php
@@ -14,14 +14,14 @@ namespace Civi\API;
  * A WhitelistRule is used to determine if an API call is authorized.
  * For example:
  *
- * @code
+ * ```
  * new WhitelistRule(array(
  *   'entity' => 'Contact',
  *   'actions' => array('get','getsingle'),
  *   'required' => array('contact_type' => 'Organization'),
  *   'fields' => array('id', 'display_name', 'sort_name', 'created_date'),
  * ));
- * @endcode
+ * ```
  *
  * This rule would allow API requests that attempt to get contacts of type "Organization",
  * but only a handful of fields ('id', 'display_name', 'sort_name', 'created_date')
diff --git a/civicrm/Civi/ActionSchedule/Event/MailingQueryEvent.php b/civicrm/Civi/ActionSchedule/Event/MailingQueryEvent.php
index 0158b41e7d43b2440d4f824b3104d010576aca9b..873951416995823b18a972b6d6f636c5f59d0e35 100644
--- a/civicrm/Civi/ActionSchedule/Event/MailingQueryEvent.php
+++ b/civicrm/Civi/ActionSchedule/Event/MailingQueryEvent.php
@@ -13,20 +13,20 @@ use Symfony\Component\EventDispatcher\Event;
  *
  * The basic mailing query looks a bit like this (depending on configuration):
  *
- * @code
+ * ```
  * SELECT reminder.id AS reminderID, reminder.contact_id as contactID, ...
  * FROM `civicrm_action_log` reminder
  * ... JOIN `target_entity` e ON e.id = reminder.entity_id ...
  * WHERE reminder.action_schedule_id = #casActionScheduleId
- * @endcode
+ * ```
  *
  * Listeners may modify the query. For example, suppose we want to load
  * additional fields from the related 'foo' entity:
  *
- * @code
+ * ```
  * $event->query->join('foo', '!casMailingJoinType civicrm_foo foo ON foo.myentity_id = e.id')
  *   ->select('foo.bar_value AS bar');
- * @endcode
+ * ```
  *
  * There are several parameters pre-set for use in queries:
  *  - 'casActionScheduleId'
diff --git a/civicrm/Civi/ActionSchedule/RecipientBuilder.php b/civicrm/Civi/ActionSchedule/RecipientBuilder.php
index a54bca3b83ea1ccb150c94f8218498c7f14c4fd9..c8a32dd97e3756e8125fb2e31b881a12f0fe4b0d 100644
--- a/civicrm/Civi/ActionSchedule/RecipientBuilder.php
+++ b/civicrm/Civi/ActionSchedule/RecipientBuilder.php
@@ -37,7 +37,7 @@ namespace Civi\ActionSchedule;
  * to fire the reminders X days after the registration date. The
  * MappingInterface::createQuery() could return a query like:
  *
- * @code
+ * ```
  * CRM_Utils_SQL_Select::from('civicrm_participant e')
  *   ->join('event', 'INNER JOIN civicrm_event event ON e.event_id = event.id')
  *   ->where('e.is_pay_later = 1')
@@ -46,7 +46,7 @@ namespace Civi\ActionSchedule;
  *   ->param('casDateField', 'e.register_date')
  *   ->param($defaultParams)
  *   ...etc...
- * @endcode
+ * ```
  *
  * In the RELATION_FIRST phase, RecipientBuilder adds a LEFT-JOIN+WHERE to find
  * participants who have *not* yet received any reminder, and filters those
diff --git a/civicrm/Civi/Angular/AngularLoader.php b/civicrm/Civi/Angular/AngularLoader.php
index 58e69987004adee665fd985cc26657b44038c291..1dca6bcd9e1d9c61bb30e4620ee8381675620d1e 100644
--- a/civicrm/Civi/Angular/AngularLoader.php
+++ b/civicrm/Civi/Angular/AngularLoader.php
@@ -8,12 +8,12 @@ namespace Civi\Angular;
  * The AngularLoader stops short of bootstrapping AngularJS. You may
  * need to `<div ng-app="..."></div>` or `angular.bootstrap(...)`.
  *
- * @code
+ * ```
  * $loader = new AngularLoader();
  * $loader->setPageName('civicrm/case/a');
  * $loader->setModules(array('crmApp'));
  * $loader->load();
- * @endCode
+ * ```
  *
  * @link https://docs.angularjs.org/guide/bootstrap
  */
diff --git a/civicrm/Civi/Api4/Action/CustomValue/GetFields.php b/civicrm/Civi/Api4/Action/CustomValue/GetFields.php
index c2ca32a1a84296c3ff0e9de51c2409aef60ebaa9..9ba2981dd801a7a92a2fb73ec2942dc40e91f521 100644
--- a/civicrm/Civi/Api4/Action/CustomValue/GetFields.php
+++ b/civicrm/Civi/Api4/Action/CustomValue/GetFields.php
@@ -10,14 +10,6 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
 namespace Civi\Api4\Action\CustomValue;
 
 use Civi\Api4\Service\Spec\SpecFormatter;
diff --git a/civicrm/Civi/Api4/Action/Entity/GetLinks.php b/civicrm/Civi/Api4/Action/Entity/GetLinks.php
index a5c6c53fbd2e75a7fb6241b85cef65169ccdf63b..5a4bc69785b2cda6c3ef6b95d37f505a6b2e3d7e 100644
--- a/civicrm/Civi/Api4/Action/Entity/GetLinks.php
+++ b/civicrm/Civi/Api4/Action/Entity/GetLinks.php
@@ -33,16 +33,14 @@ class GetLinks extends \Civi\Api4\Generic\BasicGetAction {
     foreach ($schema->getTables() as $table) {
       $entity = CoreUtil::getApiNameFromTableName($table->getName());
       // Since this is an api function, exclude tables that don't have an api
-      if (class_exists('\Civi\Api4\\' . $entity)) {
+      if (strpos($entity, 'Custom_') === 0 || class_exists('\Civi\Api4\\' . $entity)) {
         $item = [
           'entity' => $entity,
           'table' => $table->getName(),
           'links' => [],
         ];
         foreach ($table->getTableLinks() as $link) {
-          $link = $link->toArray();
-          $link['entity'] = CoreUtil::getApiNameFromTableName($link['targetTable']);
-          $item['links'][] = $link;
+          $item['links'][] = $link->toArray();
         }
         $result[] = $item;
       }
diff --git a/civicrm/Civi/Api4/Action/GetActions.php b/civicrm/Civi/Api4/Action/GetActions.php
index bb82f93555cb32c0041265b57b53f985576ffe69..fbab1f057db8e3bc15749372b2b02e13dc50c241 100644
--- a/civicrm/Civi/Api4/Action/GetActions.php
+++ b/civicrm/Civi/Api4/Action/GetActions.php
@@ -10,15 +10,6 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
-
 namespace Civi\Api4\Action;
 
 use Civi\API\Exception\NotImplementedException;
diff --git a/civicrm/Civi/Api4/Action/GroupContact/Save.php b/civicrm/Civi/Api4/Action/GroupContact/Save.php
index b695ae63f709aa46e2fb00405590d0e6b064e4e9..bb19580b68de0570a024632f44cecec71ce2e7cc 100644
--- a/civicrm/Civi/Api4/Action/GroupContact/Save.php
+++ b/civicrm/Civi/Api4/Action/GroupContact/Save.php
@@ -10,15 +10,6 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
-
 namespace Civi\Api4\Action\GroupContact;
 
 /**
diff --git a/civicrm/Civi/Api4/Action/Relationship/Get.php b/civicrm/Civi/Api4/Action/Relationship/Get.php
index 71d51b4d830f1ca23694727aca42c86056d8b7b4..e9460283c44bb471c856cb2548b1fa560fce79ba 100644
--- a/civicrm/Civi/Api4/Action/Relationship/Get.php
+++ b/civicrm/Civi/Api4/Action/Relationship/Get.php
@@ -10,14 +10,6 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
 namespace Civi\Api4\Action\Relationship;
 
 /**
diff --git a/civicrm/Civi/Api4/Action/Setting/AbstractSettingAction.php b/civicrm/Civi/Api4/Action/Setting/AbstractSettingAction.php
index d4a7a7dbe5a96add9a8f7a6b1e1256bebbec4fb7..31e228968224a9a95282ddd8d6b07de28fddbb31 100644
--- a/civicrm/Civi/Api4/Action/Setting/AbstractSettingAction.php
+++ b/civicrm/Civi/Api4/Action/Setting/AbstractSettingAction.php
@@ -10,14 +10,6 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
 namespace Civi\Api4\Action\Setting;
 
 use Civi\Api4\Domain;
diff --git a/civicrm/Civi/Api4/Action/Setting/Get.php b/civicrm/Civi/Api4/Action/Setting/Get.php
index fd4a0ce3f0370124d61c07624b087d55f318bf68..9a6539de665fbdb292ea97d93f149a9922b6d380 100644
--- a/civicrm/Civi/Api4/Action/Setting/Get.php
+++ b/civicrm/Civi/Api4/Action/Setting/Get.php
@@ -10,14 +10,6 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
 namespace Civi\Api4\Action\Setting;
 
 use Civi\Api4\Generic\Result;
diff --git a/civicrm/Civi/Api4/Action/Setting/GetFields.php b/civicrm/Civi/Api4/Action/Setting/GetFields.php
index bc6c9d68a1ed64f2c80e7f4c2e39253d5c5ba8d6..44711ef4c82680fdce5e1b20e5c53c78c7274310 100644
--- a/civicrm/Civi/Api4/Action/Setting/GetFields.php
+++ b/civicrm/Civi/Api4/Action/Setting/GetFields.php
@@ -10,14 +10,6 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
 namespace Civi\Api4\Action\Setting;
 
 /**
diff --git a/civicrm/Civi/Api4/Action/Setting/Revert.php b/civicrm/Civi/Api4/Action/Setting/Revert.php
index 11abc64db40ff51a2259fb2ac0bea4e14e8628c2..f6a2f3800356c5879df9a959a7e0e78b25b0d58f 100644
--- a/civicrm/Civi/Api4/Action/Setting/Revert.php
+++ b/civicrm/Civi/Api4/Action/Setting/Revert.php
@@ -10,14 +10,6 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
 namespace Civi\Api4\Action\Setting;
 
 use Civi\Api4\Generic\Result;
diff --git a/civicrm/Civi/Api4/Action/Setting/Set.php b/civicrm/Civi/Api4/Action/Setting/Set.php
index 9353436c82fc2e7536459fdf0727d04daf58d319..cd8413331f7341b059ac3332c27b0c1b3400bb8d 100644
--- a/civicrm/Civi/Api4/Action/Setting/Set.php
+++ b/civicrm/Civi/Api4/Action/Setting/Set.php
@@ -10,14 +10,6 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
 namespace Civi\Api4\Action\Setting;
 
 use Civi\Api4\Generic\Result;
diff --git a/civicrm/Civi/Api4/Action/System/Check.php b/civicrm/Civi/Api4/Action/System/Check.php
index 9b44021cad5d32e0c74951c8f76d2521b97b87fe..9080f60a8d4ec7ab28c8c1f196fd1575d957575e 100644
--- a/civicrm/Civi/Api4/Action/System/Check.php
+++ b/civicrm/Civi/Api4/Action/System/Check.php
@@ -10,14 +10,6 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
 namespace Civi\Api4\Action\System;
 
 /**
diff --git a/civicrm/Civi/Api4/Action/System/Flush.php b/civicrm/Civi/Api4/Action/System/Flush.php
index 8d5ab9119e61996d50b336a5155494d7c5100ed6..5eefb63cae41c334dff45801ec136078056c43a2 100644
--- a/civicrm/Civi/Api4/Action/System/Flush.php
+++ b/civicrm/Civi/Api4/Action/System/Flush.php
@@ -10,14 +10,6 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
 namespace Civi\Api4\Action\System;
 
 /**
diff --git a/civicrm/Civi/Api4/Contribution.php b/civicrm/Civi/Api4/Contribution.php
index fe0fe24647078fd8ca07120f0e1c8fbe4db722b6..99743ba0154a58075d803451998e676e0a5b67a6 100644
--- a/civicrm/Civi/Api4/Contribution.php
+++ b/civicrm/Civi/Api4/Contribution.php
@@ -10,13 +10,6 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- */
-
-
 namespace Civi\Api4;
 
 /**
diff --git a/civicrm/Civi/Api4/ContributionPage.php b/civicrm/Civi/Api4/ContributionPage.php
index 17f1c6a0ef62672209981163f1c7889cdbd0ec72..cfafb5b018d33ff731b97847f52afd8f5876ac13 100644
--- a/civicrm/Civi/Api4/ContributionPage.php
+++ b/civicrm/Civi/Api4/ContributionPage.php
@@ -10,13 +10,6 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- */
-
-
 namespace Civi\Api4;
 
 /**
diff --git a/civicrm/Civi/Api4/ContributionRecur.php b/civicrm/Civi/Api4/ContributionRecur.php
new file mode 100644
index 0000000000000000000000000000000000000000..4d3884d15f27dde4f9499c66ce22dfd496e10c51
--- /dev/null
+++ b/civicrm/Civi/Api4/ContributionRecur.php
@@ -0,0 +1,22 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4;
+
+/**
+ * ContributionRecur entity.
+ *
+ * @package Civi\Api4
+ */
+class ContributionRecur extends Generic\DAOEntity {
+
+}
diff --git a/civicrm/Civi/Api4/Event/Events.php b/civicrm/Civi/Api4/Event/Events.php
index bb866e287b636ca03b3b3b3ebc2f4d7b54b7b6aa..c5247695c5fbe391acc5709e592b57c9100608d0 100644
--- a/civicrm/Civi/Api4/Event/Events.php
+++ b/civicrm/Civi/Api4/Event/Events.php
@@ -23,21 +23,14 @@ namespace Civi\Api4\Event;
 
 class Events {
 
-  /**
-   * Prepare the specification for a request. Fired from within a request to
-   * get fields.
-   *
-   * @see \Civi\Api4\Event\GetSpecEvent
-   */
-  const GET_SPEC = 'civi.api.get_spec';
-
   /**
    * Build the database schema, allow adding of custom joins and tables.
    */
   const SCHEMA_MAP_BUILD = 'api.schema_map.build';
 
   /**
-   * Alter query results of APIv4 select query
+   * Add back POST_SELECT_QUERY const due to Joomla upgrade failure
+   * https://lab.civicrm.org/dev/joomla/-/issues/28#note_39487
    */
   const POST_SELECT_QUERY = 'api.select_query.post';
 
diff --git a/civicrm/Civi/Api4/Event/Subscriber/ContactSchemaMapSubscriber.php b/civicrm/Civi/Api4/Event/Subscriber/ContactSchemaMapSubscriber.php
deleted file mode 100644
index d41ab9f14053de29b969693f97d721d3b890fd3c..0000000000000000000000000000000000000000
--- a/civicrm/Civi/Api4/Event/Subscriber/ContactSchemaMapSubscriber.php
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-
-/*
- +--------------------------------------------------------------------+
- | Copyright CiviCRM LLC. All rights reserved.                        |
- |                                                                    |
- | This work is published under the GNU AGPLv3 license with some      |
- | permitted exceptions and without any warranty. For full license    |
- | and copyright information, see https://civicrm.org/licensing       |
- +--------------------------------------------------------------------+
- */
-
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
-
-namespace Civi\Api4\Event\Subscriber;
-
-use Civi\Api4\Event\Events;
-use Civi\Api4\Event\SchemaMapBuildEvent;
-use Civi\Api4\Service\Schema\Joinable\Joinable;
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-
-class ContactSchemaMapSubscriber implements EventSubscriberInterface {
-
-  /**
-   * @return array
-   */
-  public static function getSubscribedEvents() {
-    return [
-      Events::SCHEMA_MAP_BUILD => 'onSchemaBuild',
-    ];
-  }
-
-  /**
-   * @param \Civi\Api4\Event\SchemaMapBuildEvent $event
-   */
-  public function onSchemaBuild(SchemaMapBuildEvent $event) {
-    $schema = $event->getSchemaMap();
-    $table = $schema->getTableByName('civicrm_contact');
-    $this->addCreatedActivitiesLink($table);
-    $this->fixPreferredLanguageAlias($table);
-  }
-
-  /**
-   * @param \Civi\Api4\Service\Schema\Table $table
-   */
-  private function addCreatedActivitiesLink($table) {
-    $alias = 'created_activities';
-    $joinable = new Joinable('civicrm_activity_contact', 'contact_id', $alias);
-    $joinable->addCondition($alias . '.record_type_id = 1');
-    $joinable->setJoinType($joinable::JOIN_TYPE_ONE_TO_MANY);
-    $table->addTableLink('id', $joinable);
-  }
-
-  /**
-   * @param \Civi\Api4\Service\Schema\Table $table
-   */
-  private function fixPreferredLanguageAlias($table) {
-    foreach ($table->getExternalLinks() as $link) {
-      if ($link->getAlias() === 'languages') {
-        $link->setAlias('preferred_language');
-        return;
-      }
-    }
-  }
-
-}
diff --git a/civicrm/Civi/Api4/Event/Subscriber/PostSelectQuerySubscriber.php b/civicrm/Civi/Api4/Event/Subscriber/PostSelectQuerySubscriber.php
deleted file mode 100644
index e21c2dd381a18d6446560bb293077606e959d618..0000000000000000000000000000000000000000
--- a/civicrm/Civi/Api4/Event/Subscriber/PostSelectQuerySubscriber.php
+++ /dev/null
@@ -1,335 +0,0 @@
-<?php
-
-/*
- +--------------------------------------------------------------------+
- | Copyright CiviCRM LLC. All rights reserved.                        |
- |                                                                    |
- | This work is published under the GNU AGPLv3 license with some      |
- | permitted exceptions and without any warranty. For full license    |
- | and copyright information, see https://civicrm.org/licensing       |
- +--------------------------------------------------------------------+
- */
-
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
-
-namespace Civi\Api4\Event\Subscriber;
-
-use Civi\Api4\Event\Events;
-use Civi\Api4\Event\PostSelectQueryEvent;
-use Civi\Api4\Query\Api4SelectQuery;
-use Civi\Api4\Utils\ArrayInsertionUtil;
-use Civi\Api4\Utils\FormattingUtil;
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-
-/**
- * Changes the results of a select query, doing 1-n joins and unserializing data
- */
-class PostSelectQuerySubscriber implements EventSubscriberInterface {
-
-  /**
-   * @inheritDoc
-   */
-  public static function getSubscribedEvents() {
-    return [
-      Events::POST_SELECT_QUERY => 'onPostQuery',
-    ];
-  }
-
-  /**
-   * @param \Civi\Api4\Event\PostSelectQueryEvent $event
-   */
-  public function onPostQuery(PostSelectQueryEvent $event) {
-    $results = $event->getResults();
-    $event->setResults($this->postRun($results, $event->getQuery()));
-  }
-
-  /**
-   * @param array $results
-   * @param \Civi\Api4\Query\Api4SelectQuery $query
-   *
-   * @return array
-   */
-  protected function postRun(array $results, Api4SelectQuery $query) {
-    if (empty($results)) {
-      return $results;
-    }
-
-    FormattingUtil::formatOutputValues($results, $query->getApiFieldSpec(), $query->getEntity());
-
-    // Group the selects to avoid queries for each field
-    $groupedSelects = $this->getNtoManyJoinSelects($query);
-    foreach ($groupedSelects as $finalAlias => $selects) {
-      $joinPath = $query->getPathJoinTypes($selects[0]);
-      $selects = $this->formatSelects($finalAlias, $selects, $query);
-      $joinResults = $this->getJoinResults($query, $finalAlias, $selects);
-      $this->formatJoinResults($joinResults, $query, $finalAlias);
-
-      // Insert join results into original result
-      foreach ($results as &$primaryResult) {
-        $baseId = $primaryResult['id'];
-        $filtered = array_filter($joinResults, function ($res) use ($baseId) {
-          return ($res['_base_id'] == $baseId);
-        });
-        $filtered = array_values($filtered);
-        ArrayInsertionUtil::insert($primaryResult, $joinPath, $filtered);
-      }
-    }
-
-    return array_values($results);
-  }
-
-  /**
-   * @param array $joinResults
-   * @param \Civi\Api4\Query\Api4SelectQuery $query
-   * @param string $alias
-   */
-  private function formatJoinResults(&$joinResults, $query, $alias) {
-    $join = $query->getJoinedTable($alias);
-    $fields = [];
-    foreach ($join->getEntityFields() as $field) {
-      $name = explode('.', $field->getName());
-      $fields[array_pop($name)] = $field->toArray();
-    }
-    if ($fields) {
-      FormattingUtil::formatOutputValues($joinResults, $fields, $join->getEntity());
-    }
-  }
-
-  /**
-   * Find only those joins that need to be handled by a separate query and weren't done in the main query.
-   *
-   * @param \Civi\Api4\Query\Api4SelectQuery $query
-   *
-   * @return array
-   */
-  private function getNtoManyJoinSelects(Api4SelectQuery $query) {
-    $joinedDotSelects = array_filter(
-      $query->getSelect(),
-      function ($select) use ($query) {
-        return strpos($select, '.') && array_filter($query->getPathJoinTypes($select));
-      }
-    );
-
-    $selects = [];
-    // group related selects by alias so they can be executed in one query
-    foreach ($joinedDotSelects as $select) {
-      $parts = explode('.', $select);
-      $finalAlias = $parts[count($parts) - 2];
-      $selects[$finalAlias][] = $select;
-    }
-
-    // sort by depth, e.g. email selects should be done before email.location
-    uasort($selects, function ($a, $b) {
-      $aFirst = $a[0];
-      $bFirst = $b[0];
-      return substr_count($aFirst, '.') > substr_count($bFirst, '.');
-    });
-
-    return $selects;
-  }
-
-  /**
-   * @param array $selects
-   * @param $serializationType
-   * @param \Civi\Api4\Query\Api4SelectQuery $query
-   *
-   * @return array
-   */
-  private function getResultsForSerializedField(
-    array $selects,
-    $serializationType,
-    Api4SelectQuery $query
-  ) {
-    // Get the alias (Selects are grouped and all target the same table)
-    $sampleField = current($selects);
-    $alias = strstr($sampleField, '.', TRUE);
-
-    // Fetch the results with the serialized field
-    $selects['serialized'] = $query::MAIN_TABLE_ALIAS . '.' . $alias;
-    $serializedResults = $this->runWithNewSelects($selects, $query);
-    $newResults = [];
-
-    // Create a new results array, with a separate entry for each option value
-    foreach ($serializedResults as $result) {
-      $optionValues = \CRM_Core_DAO::unSerializeField(
-        $result['serialized'],
-        $serializationType
-      );
-      unset($result['serialized']);
-      foreach ($optionValues as $value) {
-        $newResults[] = array_merge($result, ['value' => $value]);
-      }
-    }
-
-    $optionValueValues = array_unique(array_column($newResults, 'value'));
-    $optionValues = $this->getOptionValuesFromValues(
-      $selects,
-      $query,
-      $optionValueValues
-    );
-    $valueField = $alias . '.value';
-
-    // Index by value
-    foreach ($optionValues as $key => $subResult) {
-      $optionValues[$subResult['value']] = $subResult;
-      unset($subResult[$key]);
-
-      // Exclude 'value' if not in original selects
-      if (!in_array($valueField, $selects)) {
-        unset($optionValues[$subResult['value']]['value']);
-      }
-    }
-
-    // Replace serialized with the sub-select results
-    foreach ($newResults as &$result) {
-      $result = array_merge($result, $optionValues[$result['value']]);
-      unset($result['value']);
-    }
-
-    return $newResults;
-  }
-
-  /**
-   * Prepares selects for the subquery to fetch join results
-   *
-   * @param string $alias
-   * @param array $selects
-   * @param \Civi\Api4\Query\Api4SelectQuery $query
-   *
-   * @return array
-   */
-  private function formatSelects($alias, $selects, Api4SelectQuery $query) {
-    $mainAlias = $query::MAIN_TABLE_ALIAS;
-    $selectFields = [];
-
-    foreach ($selects as $select) {
-      $selectAlias = str_replace('`', '', $query->getField($select)['sql_name']);
-      $fieldAlias = substr($select, strrpos($select, '.') + 1);
-      $selectFields[$fieldAlias] = $selectAlias;
-    }
-
-    $firstSelect = $selects[0];
-    $pathParts = explode('.', $firstSelect);
-    $numParts = count($pathParts);
-    $parentAlias = $numParts > 2 ? $pathParts[$numParts - 3] : $mainAlias;
-
-    $selectFields['id'] = sprintf('%s.id', $alias);
-    $selectFields['_parent_id'] = $parentAlias . '.id';
-    $selectFields['_base_id'] = $mainAlias . '.id';
-
-    return $selectFields;
-  }
-
-  /**
-   * @param array $selects
-   * @param \Civi\Api4\Query\Api4SelectQuery $query
-   *
-   * @return array
-   */
-  private function runWithNewSelects(array $selects, Api4SelectQuery $query) {
-    $aliasedSelects = array_map(function ($field, $alias) {
-      return sprintf('%s as "%s"', $field, $alias);
-    }, $selects, array_keys($selects));
-
-    $newSelect = sprintf('SELECT DISTINCT %s', implode(", ", $aliasedSelects));
-    $sql = $query->getQuery()->toSQL();
-    // Replace the "SELECT" clause
-    $sql = $newSelect . substr($sql, strpos($sql, "\nFROM"));
-
-    if (is_array($query->debugOutput)) {
-      $query->debugOutput['sql'][] = $sql;
-    }
-
-    $relatedResults = [];
-    $resultDAO = \CRM_Core_DAO::executeQuery($sql);
-    while ($resultDAO->fetch()) {
-      $relatedResult = [];
-      foreach ($selects as $alias => $column) {
-        $returnName = $alias;
-        $alias = str_replace('.', '_', $alias);
-        if (property_exists($resultDAO, $alias)) {
-          $relatedResult[$returnName] = $resultDAO->$alias;
-        }
-      };
-      $relatedResults[] = $relatedResult;
-    }
-
-    return $relatedResults;
-  }
-
-  /**
-   * @param \Civi\Api4\Query\Api4SelectQuery $query
-   * @param $alias
-   * @param $selects
-   * @return array
-   */
-  protected function getJoinResults(Api4SelectQuery $query, $alias, $selects) {
-    $apiFieldSpec = $query->getApiFieldSpec();
-    if (!empty($apiFieldSpec[$alias]['serialize'])) {
-      $type = $apiFieldSpec[$alias]['serialize'];
-      $joinResults = $this->getResultsForSerializedField($selects, $type, $query);
-    }
-    else {
-      $joinResults = $this->runWithNewSelects($selects, $query);
-    }
-
-    // Remove results with no matching entries
-    $joinResults = array_filter($joinResults, function ($result) {
-      return !empty($result['id']);
-    });
-
-    return $joinResults;
-  }
-
-  /**
-   * Get all the option_value values required in the query
-   *
-   * @param array $selects
-   * @param \Civi\Api4\Query\Api4SelectQuery $query
-   * @param array $values
-   *
-   * @return array
-   */
-  private function getOptionValuesFromValues(
-    array $selects,
-    Api4SelectQuery $query,
-    array $values
-  ) {
-    $sampleField = current($selects);
-    $alias = strstr($sampleField, '.', TRUE);
-
-    // Get the option value table that was joined
-    $relatedTable = NULL;
-    foreach ($query->getJoinedTables() as $joinedTable) {
-      if ($joinedTable->getAlias() === $alias) {
-        $relatedTable = $joinedTable;
-      }
-    }
-
-    // We only want subselects related to the joined table
-    $subSelects = array_filter($selects, function ($select) use ($alias) {
-      return strpos($select, $alias) === 0;
-    });
-
-    // Fetch all related option_value entries
-    $valueField = $alias . '.value';
-    $subSelects[] = $valueField;
-    $tableName = $relatedTable->getTargetTable();
-    $conditions = $relatedTable->getExtraJoinConditions();
-    $conditions[] = $valueField . ' IN ("' . implode('", "', $values) . '")';
-    $subQuery = new \CRM_Utils_SQL_Select($tableName . ' ' . $alias);
-    $subQuery->where($conditions);
-    $subQuery->select($subSelects);
-    $subResults = $subQuery->execute()->fetchAll();
-
-    return $subResults;
-  }
-
-}
diff --git a/civicrm/Civi/Api4/Generic/AbstractEntity.php b/civicrm/Civi/Api4/Generic/AbstractEntity.php
index 74d28bfb33041ef5f1e90596131253369c08c52f..8a1ed4e86286838b887f5b35a54644ea97c34a4d 100644
--- a/civicrm/Civi/Api4/Generic/AbstractEntity.php
+++ b/civicrm/Civi/Api4/Generic/AbstractEntity.php
@@ -66,10 +66,9 @@ abstract class AbstractEntity {
     $permissions = \CRM_Core_Permission::getEntityActionPermissions();
 
     // For legacy reasons the permissions are keyed by lowercase entity name
-    // Note: Convert to camel & back in order to circumvent all the api3 naming oddities
-    $lcentity = _civicrm_api_get_entity_name_from_camel(\CRM_Utils_String::convertStringToCamel(self::getEntityName()));
+    $lcentity = \CRM_Core_DAO_AllCoreTables::convertEntityNameToLower(self::getEntityName());
     // Merge permissions for this entity with the defaults
-    return \CRM_Utils_Array::value($lcentity, $permissions, []) + $permissions['default'];
+    return ($permissions[$lcentity] ?? []) + $permissions['default'];
   }
 
   /**
diff --git a/civicrm/Civi/Api4/Generic/BasicGetAction.php b/civicrm/Civi/Api4/Generic/BasicGetAction.php
index 20cfe1821a4941f73848f15262dfad6ac3698481..f1d34d237f068426dcfee8fc8a86772861b19908 100644
--- a/civicrm/Civi/Api4/Generic/BasicGetAction.php
+++ b/civicrm/Civi/Api4/Generic/BasicGetAction.php
@@ -115,7 +115,7 @@ class BasicGetAction extends AbstractGetAction {
     foreach ($records as &$values) {
       foreach ($this->entityFields() as $field) {
         if (!empty($field['options'])) {
-          foreach (array_keys(FormattingUtil::$pseudoConstantContexts) as $suffix) {
+          foreach (FormattingUtil::$pseudoConstantSuffixes as $suffix) {
             $pseudofield = $field['name'] . ':' . $suffix;
             if (!isset($values[$pseudofield]) && isset($values[$field['name']]) && $this->_isFieldSelected($pseudofield)) {
               $values[$pseudofield] = $values[$field['name']];
diff --git a/civicrm/Civi/Api4/Generic/DAOEntity.php b/civicrm/Civi/Api4/Generic/DAOEntity.php
index 38888227c6accfd44cc809a14d1e71ff364c7583..f63c87ba62199f9ae92248d2faec7956a33169c2 100644
--- a/civicrm/Civi/Api4/Generic/DAOEntity.php
+++ b/civicrm/Civi/Api4/Generic/DAOEntity.php
@@ -10,15 +10,6 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
-
 namespace Civi\Api4\Generic;
 
 /**
@@ -28,6 +19,8 @@ abstract class DAOEntity extends AbstractEntity {
 
   /**
    * @return DAOGetAction
+   *
+   * @throws \API_Exception
    */
   public static function get() {
     return new DAOGetAction(static::class, __FUNCTION__);
@@ -49,6 +42,8 @@ abstract class DAOEntity extends AbstractEntity {
 
   /**
    * @return DAOCreateAction
+   *
+   * @throws \API_Exception
    */
   public static function create() {
     return new DAOCreateAction(static::class, __FUNCTION__);
diff --git a/civicrm/Civi/Api4/Generic/DAOGetAction.php b/civicrm/Civi/Api4/Generic/DAOGetAction.php
index 73098f5a5cf0f6d809bc8758355b66752a155e1c..d922781ada2e049574a2d065aec703437733213e 100644
--- a/civicrm/Civi/Api4/Generic/DAOGetAction.php
+++ b/civicrm/Civi/Api4/Generic/DAOGetAction.php
@@ -45,6 +45,13 @@ class DAOGetAction extends AbstractGetAction {
    */
   protected $select = [];
 
+  /**
+   * Joins to other entities.
+   *
+   * @var array
+   */
+  protected $join = [];
+
   /**
    * Field(s) by which to group the results.
    *
@@ -120,4 +127,32 @@ class DAOGetAction extends AbstractGetAction {
     return $this;
   }
 
+  /**
+   * @param string $entity
+   * @param bool $required
+   * @param array ...$conditions
+   * @return DAOGetAction
+   */
+  public function addJoin(string $entity, bool $required = FALSE, ...$conditions): DAOGetAction {
+    array_unshift($conditions, $entity, $required);
+    $this->join[] = $conditions;
+    return $this;
+  }
+
+  /**
+   * @param array $join
+   * @return DAOGetAction
+   */
+  public function setJoin(array $join): DAOGetAction {
+    $this->join = $join;
+    return $this;
+  }
+
+  /**
+   * @return array
+   */
+  public function getJoin(): array {
+    return $this->join;
+  }
+
 }
diff --git a/civicrm/Civi/Api4/Generic/Traits/CustomValueActionTrait.php b/civicrm/Civi/Api4/Generic/Traits/CustomValueActionTrait.php
index 236cb3fc4ddc4fc6e958c5534a039aac58015f55..acf90cdcf5398ccfa862f3d9a4cc584104ecbbd6 100644
--- a/civicrm/Civi/Api4/Generic/Traits/CustomValueActionTrait.php
+++ b/civicrm/Civi/Api4/Generic/Traits/CustomValueActionTrait.php
@@ -76,7 +76,7 @@ trait CustomValueActionTrait {
    * @inheritDoc
    */
   protected function deleteObjects($items) {
-    $customTable = CoreUtil::getCustomTableByName($this->getCustomGroup());
+    $customTable = CoreUtil::getTableName($this->getEntityName());
     $ids = [];
     foreach ($items as $item) {
       \CRM_Utils_Hook::pre('delete', $this->getEntityName(), $item['id'], \CRM_Core_DAO::$_nullArray);
diff --git a/civicrm/Civi/Api4/Generic/Traits/DAOActionTrait.php b/civicrm/Civi/Api4/Generic/Traits/DAOActionTrait.php
index 45b572a2526e6ce3ff5ba5fe28d4801ab2aecc3d..85ac173f5e6ce96dfdb8fe98a9204958bacac5c2 100644
--- a/civicrm/Civi/Api4/Generic/Traits/DAOActionTrait.php
+++ b/civicrm/Civi/Api4/Generic/Traits/DAOActionTrait.php
@@ -10,21 +10,13 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
 namespace Civi\Api4\Generic\Traits;
 
 use Civi\Api4\Utils\FormattingUtil;
 
 /**
  * @method string getLanguage()
- * @method setLanguage(string $language)
+ * @method $this setLanguage(string $language)
  */
 trait DAOActionTrait {
 
@@ -103,9 +95,11 @@ trait DAOActionTrait {
    *
    * @param array $items
    *   The records to write to the DB.
+   *
    * @return array
    *   The records after being written to the DB (e.g. including newly assigned "id").
    * @throws \API_Exception
+   * @throws \CRM_Core_Exception
    */
   protected function writeObjects($items) {
     $baoName = $this->getBaoName();
@@ -130,7 +124,7 @@ trait DAOActionTrait {
       $item['check_permissions'] = $this->getCheckPermissions();
 
       // For some reason the contact bao requires this
-      if ($entityId && $this->getEntityName() == 'Contact') {
+      if ($entityId && $this->getEntityName() === 'Contact') {
         $item['contact_id'] = $entityId;
       }
 
@@ -138,7 +132,7 @@ trait DAOActionTrait {
         $this->checkContactPermissions($baoName, $item);
       }
 
-      if ($this->getEntityName() == 'Address') {
+      if ($this->getEntityName() === 'Address') {
         $createResult = $baoName::add($item, $this->fixAddress);
       }
       elseif (method_exists($baoName, $method)) {
@@ -162,7 +156,11 @@ trait DAOActionTrait {
   /**
    * @param array $params
    * @param int $entityId
+   *
    * @return mixed
+   *
+   * @throws \API_Exception
+   * @throws \CRM_Core_Exception
    */
   protected function formatCustomParams(&$params, $entityId) {
     $customParams = [];
@@ -204,7 +202,7 @@ trait DAOActionTrait {
           $value = FormattingUtil::replacePseudoconstant($options, $value, TRUE);
         }
 
-        if ($customFieldType == 'CheckBox') {
+        if ($customFieldType === 'CheckBox') {
           // this function should be part of a class
           formatCheckBoxField($value, 'custom_' . $customFieldId, $this->getEntityName());
         }
@@ -232,13 +230,14 @@ trait DAOActionTrait {
   /**
    * Check edit/delete permissions for contacts and related entities.
    *
-   * @param $baoName
-   * @param $item
+   * @param string $baoName
+   * @param array $item
+   *
    * @throws \Civi\API\Exception\UnauthorizedException
    */
   protected function checkContactPermissions($baoName, $item) {
-    if ($baoName == 'CRM_Contact_BAO_Contact' && !empty($item['id'])) {
-      $permission = $this->getActionName() == 'delete' ? \CRM_Core_Permission::DELETE : \CRM_Core_Permission::EDIT;
+    if ($baoName === 'CRM_Contact_BAO_Contact' && !empty($item['id'])) {
+      $permission = $this->getActionName() === 'delete' ? \CRM_Core_Permission::DELETE : \CRM_Core_Permission::EDIT;
       if (!\CRM_Contact_BAO_Contact_Permission::allow($item['id'], $permission)) {
         throw new \Civi\API\Exception\UnauthorizedException('Permission denied to modify contact record');
       }
diff --git a/civicrm/Civi/Api4/LocationType.php b/civicrm/Civi/Api4/LocationType.php
index 836e59463f7384cd00a71f76c5773a4b8f2ba416..cef52216207fadbf192241e151e0f3085b595192 100644
--- a/civicrm/Civi/Api4/LocationType.php
+++ b/civicrm/Civi/Api4/LocationType.php
@@ -10,15 +10,6 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
-
 namespace Civi\Api4;
 
 /**
diff --git a/civicrm/Civi/Api4/MembershipType.php b/civicrm/Civi/Api4/MembershipType.php
new file mode 100644
index 0000000000000000000000000000000000000000..293c9602c1f6b1dfcb04eaefe52619b30312bad6
--- /dev/null
+++ b/civicrm/Civi/Api4/MembershipType.php
@@ -0,0 +1,22 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4;
+
+/**
+ * MembershipType entity.
+ *
+ * @package Civi\Api4
+ */
+class MembershipType extends Generic\DAOEntity {
+
+}
diff --git a/civicrm/Civi/Api4/PriceField.php b/civicrm/Civi/Api4/PriceField.php
new file mode 100644
index 0000000000000000000000000000000000000000..66aaa4428a70a3cf46ef8dd9b87f37778c12e0b3
--- /dev/null
+++ b/civicrm/Civi/Api4/PriceField.php
@@ -0,0 +1,22 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4;
+
+/**
+ * PriceField entity.
+ *
+ * @package Civi\Api4
+ */
+class PriceField extends Generic\DAOEntity {
+
+}
diff --git a/civicrm/Civi/Api4/PriceFieldValue.php b/civicrm/Civi/Api4/PriceFieldValue.php
new file mode 100644
index 0000000000000000000000000000000000000000..851abbcf6ed8b484908f86c412c22ef6c12a409b
--- /dev/null
+++ b/civicrm/Civi/Api4/PriceFieldValue.php
@@ -0,0 +1,22 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4;
+
+/**
+ * PriceFieldValue entity.
+ *
+ * @package Civi\Api4
+ */
+class PriceFieldValue extends Generic\DAOEntity {
+
+}
diff --git a/civicrm/Civi/Api4/PriceSet.php b/civicrm/Civi/Api4/PriceSet.php
new file mode 100644
index 0000000000000000000000000000000000000000..91a7e27d91059837852282911ec8223ed5a5dade
--- /dev/null
+++ b/civicrm/Civi/Api4/PriceSet.php
@@ -0,0 +1,22 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4;
+
+/**
+ * PriceSet entity.
+ *
+ * @package Civi\Api4
+ */
+class PriceSet extends Generic\DAOEntity {
+
+}
diff --git a/civicrm/Civi/Api4/Query/Api4SelectQuery.php b/civicrm/Civi/Api4/Query/Api4SelectQuery.php
index d7e6da44c8138e75ead7e3248d389dce45ee1be1..794df9580a1150b5a0b0da76d5182ea157a97175 100644
--- a/civicrm/Civi/Api4/Query/Api4SelectQuery.php
+++ b/civicrm/Civi/Api4/Query/Api4SelectQuery.php
@@ -12,15 +12,10 @@
 namespace Civi\Api4\Query;
 
 use Civi\API\SelectQuery;
-use Civi\Api4\Event\Events;
-use Civi\Api4\Event\PostSelectQueryEvent;
 use Civi\Api4\Service\Schema\Joinable\CustomGroupJoinable;
-use Civi\Api4\Service\Schema\Joinable\Joinable;
-use Civi\Api4\Service\Schema\Joinable\OptionValueJoinable;
 use Civi\Api4\Utils\FormattingUtil;
 use Civi\Api4\Utils\CoreUtil;
 use Civi\Api4\Utils\SelectUtil;
-use CRM_Core_DAO_AllCoreTables as AllCoreTables;
 
 /**
  * A query `node` may be in one of three formats:
@@ -42,12 +37,6 @@ class Api4SelectQuery extends SelectQuery {
    */
   protected $apiVersion = 4;
 
-  /**
-   * @var \Civi\Api4\Service\Schema\Joinable\Joinable[]
-   *   The joinable tables that have been joined so far
-   */
-  protected $joinedTables = [];
-
   /**
    * @var array
    * [alias => expr][]
@@ -91,17 +80,20 @@ class Api4SelectQuery extends SelectQuery {
     if ($apiGet->getDebug()) {
       $this->debugOutput =& $apiGet->_debugOutput;
     }
-    $baoName = CoreUtil::getBAOFromApiName($this->entity);
-    $this->entityFieldNames = array_column($baoName::fields(), 'name');
-    foreach ($apiGet->entityFields() as $path => $field) {
+    foreach ($apiGet->entityFields() as $field) {
+      $this->entityFieldNames[] = $field['name'];
       $field['sql_name'] = '`' . self::MAIN_TABLE_ALIAS . '`.`' . $field['column_name'] . '`';
-      $this->addSpecField($path, $field);
+      $this->addSpecField($field['name'], $field);
     }
 
-    $this->constructQueryObject($baoName);
+    $baoName = CoreUtil::getBAOFromApiName($this->entity);
+    $this->constructQueryObject();
 
     // Add ACLs first to avoid redundant subclauses
     $this->query->where($this->getAclClause(self::MAIN_TABLE_ALIAS, $baoName));
+
+    // Add explicit joins. Other joins implied by dot notation may be added later
+    $this->addExplicitJoins($apiGet->getJoin());
   }
 
   /**
@@ -134,24 +126,21 @@ class Api4SelectQuery extends SelectQuery {
       $this->debugOutput['sql'][] = $sql;
     }
     $query = \CRM_Core_DAO::executeQuery($sql);
-    $i = 0;
     while ($query->fetch()) {
-      $id = $query->id ?? $i++;
       if (in_array('row_count', $this->select)) {
         $results[]['row_count'] = (int) $query->c;
         break;
       }
-      $results[$id] = [];
+      $result = [];
       foreach ($this->selectAliases as $alias => $expr) {
         $returnName = $alias;
         $alias = str_replace('.', '_', $alias);
-        $results[$id][$returnName] = property_exists($query, $alias) ? $query->$alias : NULL;
+        $result[$returnName] = property_exists($query, $alias) ? $query->$alias : NULL;
       }
+      $results[] = $result;
     }
-    $event = new PostSelectQueryEvent($results, $this);
-    \Civi::dispatcher()->dispatch(Events::POST_SELECT_QUERY, $event);
-
-    return $event->getResults();
+    FormattingUtil::formatOutputValues($results, $this->getApiFieldSpec(), $this->getEntity());
+    return $results;
   }
 
   protected function buildSelectClause() {
@@ -174,7 +163,7 @@ class Api4SelectQuery extends SelectQuery {
       });
       foreach ($wildFields as $item) {
         $pos = array_search($item, array_values($this->select));
-        $this->joinFK($item);
+        $this->autoJoinFK($item);
         $matches = SelectUtil::getMatchingFields($item, array_keys($this->apiFieldSpec));
         array_splice($this->select, $pos, 1, $matches);
       }
@@ -193,9 +182,6 @@ class Api4SelectQuery extends SelectQuery {
           }
           $valid = FALSE;
         }
-        elseif ($field['is_many']) {
-          $valid = FALSE;
-        }
       }
       if ($valid) {
         $alias = $expr->getAlias();
@@ -236,11 +222,20 @@ class Api4SelectQuery extends SelectQuery {
       if ($dir !== 'ASC' && $dir !== 'DESC') {
         throw new \API_Exception("Invalid sort direction. Cannot order by $item $dir");
       }
-      $expr = SqlExpression::convert($item);
-      foreach ($expr->getFields() as $fieldName) {
-        $this->getField($fieldName, TRUE);
+      $expr = $this->getExpression($item);
+      $column = $expr->render($this->apiFieldSpec);
+
+      // Use FIELD() function to sort on pseudoconstant values
+      $suffix = strstr($item, ':');
+      if ($suffix && $expr->getType() === 'SqlField') {
+        $field = $this->getField($item);
+        $options = FormattingUtil::getPseudoconstantList($field['entity'], $field['name'], substr($suffix, 1));
+        if ($options) {
+          asort($options);
+          $column = "FIELD($column,'" . implode("','", array_keys($options)) . "')";
+        }
       }
-      $this->query->orderBy($expr->render($this->apiFieldSpec) . " $dir");
+      $this->query->orderBy("$column $dir");
     }
   }
 
@@ -259,11 +254,7 @@ class Api4SelectQuery extends SelectQuery {
    */
   protected function buildGroupBy() {
     foreach ($this->groupBy as $item) {
-      $expr = SqlExpression::convert($item);
-      foreach ($expr->getFields() as $fieldName) {
-        $this->getField($fieldName, TRUE);
-      }
-      $this->query->groupBy($expr->render($this->apiFieldSpec));
+      $this->query->groupBy($this->getExpression($item)->render($this->apiFieldSpec));
     }
   }
 
@@ -272,7 +263,7 @@ class Api4SelectQuery extends SelectQuery {
    *
    * @param array $clause
    * @param string $type
-   *   WHERE|HAVING
+   *   WHERE|HAVING|ON
    * @return string SQL where clause
    *
    * @throws \API_Exception
@@ -311,7 +302,7 @@ class Api4SelectQuery extends SelectQuery {
    * Validate and transform a leaf clause array to SQL.
    * @param array $clause [$fieldName, $operator, $criteria]
    * @param string $type
-   *   WHERE|HAVING
+   *   WHERE|HAVING|ON
    * @return string SQL
    * @throws \API_Exception
    * @throws \Exception
@@ -319,6 +310,9 @@ class Api4SelectQuery extends SelectQuery {
   protected function composeClause(array $clause, string $type) {
     // Pad array for unary operators
     list($expr, $operator, $value) = array_pad($clause, 3, NULL);
+    if (!in_array($operator, \CRM_Core_DAO::acceptedSQLOperators(), TRUE)) {
+      throw new \API_Exception('Illegal operator');
+    }
 
     // For WHERE clause, expr must be the name of a field.
     if ($type === 'WHERE') {
@@ -327,7 +321,7 @@ class Api4SelectQuery extends SelectQuery {
       $fieldAlias = $field['sql_name'];
     }
     // For HAVING, expr must be an item in the SELECT clause
-    else {
+    elseif ($type === 'HAVING') {
       // Expr references a fieldName or alias
       if (isset($this->selectAliases[$expr])) {
         $fieldAlias = $expr;
@@ -357,6 +351,21 @@ class Api4SelectQuery extends SelectQuery {
       }
       $fieldAlias = '`' . $fieldAlias . '`';
     }
+    elseif ($type === 'ON') {
+      $expr = $this->getExpression($expr);
+      $fieldName = count($expr->getFields()) === 1 ? $expr->getFields()[0] : NULL;
+      $fieldAlias = $expr->render($this->apiFieldSpec);
+      if (is_string($value)) {
+        $valExpr = $this->getExpression($value);
+        if ($fieldName && $valExpr->getType() === 'SqlString') {
+          FormattingUtil::formatInputValue($valExpr->expr, $fieldName, $this->apiFieldSpec[$fieldName]);
+        }
+        return sprintf('%s %s %s', $fieldAlias, $operator, $valExpr->render($this->apiFieldSpec));
+      }
+      elseif ($fieldName) {
+        FormattingUtil::formatInputValue($value, $fieldName, $this->apiFieldSpec[$fieldName]);
+      }
+    }
 
     $sql_clause = \CRM_Core_DAO::createSQLFilter($fieldAlias, [$operator => $value]);
     if ($sql_clause === NULL) {
@@ -365,6 +374,19 @@ class Api4SelectQuery extends SelectQuery {
     return $sql_clause;
   }
 
+  /**
+   * @param string $expr
+   * @return SqlExpression
+   * @throws \API_Exception
+   */
+  protected function getExpression(string $expr) {
+    $sqlExpr = SqlExpression::convert($expr);
+    foreach ($sqlExpr->getFields() as $fieldName) {
+      $this->getField($fieldName, TRUE);
+    }
+    return $sqlExpr;
+  }
+
   /**
    * @inheritDoc
    */
@@ -389,7 +411,7 @@ class Api4SelectQuery extends SelectQuery {
     $fieldName = $col ? substr($expr, 0, $col) : $expr;
     // Perform join if field not yet available - this will add it to apiFieldSpec
     if (!isset($this->apiFieldSpec[$fieldName]) && strpos($fieldName, '.')) {
-      $this->joinFK($fieldName);
+      $this->autoJoinFK($fieldName);
     }
     $field = $this->apiFieldSpec[$fieldName] ?? NULL;
     if ($strict && !$field) {
@@ -400,13 +422,78 @@ class Api4SelectQuery extends SelectQuery {
   }
 
   /**
-   * Joins a path and adds all fields in the joined eneity to apiFieldSpec
+   * Join onto other entities as specified by the api call.
+   *
+   * @param $joins
+   * @throws \API_Exception
+   * @throws \Civi\API\Exception\NotImplementedException
+   */
+  private function addExplicitJoins($joins) {
+    foreach ($joins as $join) {
+      // First item in the array is the entity name
+      $entity = array_shift($join);
+      // Which might contain an alias. Split on the keyword "AS"
+      list($entity, $alias) = array_pad(explode(' AS ', $entity), 2, NULL);
+      // Ensure alias is a safe string, and supply default if not given
+      $alias = $alias ? \CRM_Utils_String::munge($alias) : strtolower($entity);
+      // First item in the array is a boolean indicating if the join is required (aka INNER or LEFT).
+      // The rest are join conditions.
+      $side = array_shift($join) ? 'INNER' : 'LEFT';
+      $joinEntityGet = \Civi\API\Request::create($entity, 'get', ['version' => 4, 'checkPermissions' => $this->checkPermissions]);
+      foreach ($joinEntityGet->entityFields() as $field) {
+        $field['sql_name'] = '`' . $alias . '`.`' . $field['column_name'] . '`';
+        $field['is_join'] = TRUE;
+        $this->addSpecField($alias . '.' . $field['name'], $field);
+      }
+      $conditions = $this->getJoinConditions($entity, $alias);
+      foreach (array_filter($join) as $clause) {
+        $conditions[] = $this->treeWalkClauses($clause, 'ON');
+      }
+      $tableName = CoreUtil::getTableName($entity);
+      $this->join($side, $tableName, $alias, $conditions);
+    }
+  }
+
+  /**
+   * Supply conditions for an explicit join.
+   *
+   * @param $entity
+   * @param $alias
+   * @return array
+   */
+  private function getJoinConditions($entity, $alias) {
+    $conditions = [];
+    // getAclClause() expects a stack of 1-to-1 join fields to help it dedupe, but this is more flexible,
+    // so unless this is a direct 1-to-1 join with the main entity, we'll just hack it
+    // with a padded empty stack to bypass its deduping.
+    $stack = [NULL, NULL];
+    foreach ($this->apiFieldSpec as $name => $field) {
+      if ($field['entity'] !== $entity && $field['fk_entity'] === $entity) {
+        $conditions[] = $this->treeWalkClauses([$name, '=', "$alias.id"], 'ON');
+      }
+      elseif (strpos($name, "$alias.") === 0 && substr_count($name, '.') === 1 &&  $field['fk_entity'] === $this->entity) {
+        $conditions[] = $this->treeWalkClauses([$name, '=', 'id'], 'ON');
+        $stack = ['id'];
+      }
+    }
+    // Hmm, if we came up with > 1 condition, then it's ambiguous how it should be joined so we won't return anything but the generic ACLs
+    if (count($conditions) > 1) {
+      $stack = [NULL, NULL];
+      $conditions = [];
+    }
+    $baoName = CoreUtil::getBAOFromApiName($entity);
+    $acls = array_values($this->getAclClause($alias, $baoName, $stack));
+    return array_merge($acls, $conditions);
+  }
+
+  /**
+   * Joins a path and adds all fields in the joined entity to apiFieldSpec
    *
    * @param $key
    * @throws \API_Exception
    * @throws \Exception
    */
-  protected function joinFK($key) {
+  protected function autoJoinFK($key) {
     if (isset($this->apiFieldSpec[$key])) {
       return;
     }
@@ -419,23 +506,12 @@ class Api4SelectQuery extends SelectQuery {
     array_pop($pathArray);
     $pathString = implode('.', $pathArray);
 
-    if (!$joiner->canJoin($this, $pathString)) {
+    if (!$joiner->canAutoJoin($this->getFrom(), $pathString)) {
       return;
     }
 
     $joinPath = $joiner->join($this, $pathString);
 
-    $isMany = FALSE;
-    foreach ($joinPath as $joinable) {
-      if ($joinable->getJoinType() === Joinable::JOIN_TYPE_ONE_TO_MANY) {
-        $isMany = TRUE;
-      }
-      if ($joinable instanceof OptionValueJoinable) {
-        \Civi::log()->warning('Use API pseudoconstant suffix like :name or :label instead of join.', ['civi.tag' => 'deprecated']);
-      }
-    }
-
-    /** @var \Civi\Api4\Service\Schema\Joinable\Joinable $lastLink */
     $lastLink = array_pop($joinPath);
 
     // Custom field names are already prefixed
@@ -445,33 +521,20 @@ class Api4SelectQuery extends SelectQuery {
     }
     $prefix = $pathArray ? implode('.', $pathArray) . '.' : '';
     // Cache field info for retrieval by $this->getField()
-    $joinEntity = $lastLink->getEntity();
     foreach ($lastLink->getEntityFields() as $fieldObject) {
-      $fieldArray = ['entity' => $joinEntity] + $fieldObject->toArray();
+      $fieldArray = $fieldObject->toArray();
       $fieldArray['sql_name'] = '`' . $lastLink->getAlias() . '`.`' . $fieldArray['column_name'] . '`';
       $fieldArray['is_custom'] = $isCustom;
       $fieldArray['is_join'] = TRUE;
-      $fieldArray['is_many'] = $isMany;
       $this->addSpecField($prefix . $fieldArray['name'], $fieldArray);
     }
   }
 
-  /**
-   * @param \Civi\Api4\Service\Schema\Joinable\Joinable $joinable
-   *
-   * @return $this
-   */
-  public function addJoinedTable(Joinable $joinable) {
-    $this->joinedTables[] = $joinable;
-
-    return $this;
-  }
-
   /**
    * @return FALSE|string
    */
   public function getFrom() {
-    return AllCoreTables::getTableForClass(AllCoreTables::getFullName($this->entity));
+    return CoreUtil::getTableName($this->entity);
   }
 
   /**
@@ -523,13 +586,6 @@ class Api4SelectQuery extends SelectQuery {
     return $this->selectFields;
   }
 
-  /**
-   * @return bool
-   */
-  public function isFillUniqueFields() {
-    return $this->isFillUniqueFields;
-  }
-
   /**
    * @return \CRM_Utils_SQL_Select
    */
@@ -579,85 +635,14 @@ class Api4SelectQuery extends SelectQuery {
     return $this->apiVersion;
   }
 
-  /**
-   * @return \Civi\Api4\Service\Schema\Joinable\Joinable[]
-   */
-  public function getJoinedTables() {
-    return $this->joinedTables;
-  }
-
-  /**
-   * @return \Civi\Api4\Service\Schema\Joinable\Joinable
-   */
-  public function getJoinedTable($alias) {
-    foreach ($this->joinedTables as $join) {
-      if ($join->getAlias() == $alias) {
-        return $join;
-      }
-    }
-  }
-
   /**
    * Get table name on basis of entity
    *
-   * @param string $baoName
-   *
    * @return void
    */
-  public function constructQueryObject($baoName) {
-    if (strstr($this->entity, 'Custom_')) {
-      $this->query = \CRM_Utils_SQL_Select::from(CoreUtil::getCustomTableByName(str_replace('Custom_', '', $this->entity)) . ' ' . self::MAIN_TABLE_ALIAS);
-      $this->entityFieldNames = array_keys($this->apiFieldSpec);
-    }
-    else {
-      $bao = new $baoName();
-      $this->query = \CRM_Utils_SQL_Select::from($bao->tableName() . ' ' . self::MAIN_TABLE_ALIAS);
-    }
-  }
-
-  /**
-   * Checks if a field either belongs to the main entity or is joinable 1-to-1.
-   *
-   * Used to determine if a field can be added to the SELECT of the main query,
-   * or if it must be fetched post-query.
-   *
-   * @param string $fieldPath
-   * @return bool
-   */
-  public function isOneToOneField(string $fieldPath) {
-    return strpos($fieldPath, '.') === FALSE || !array_filter($this->getPathJoinTypes($fieldPath));
-  }
-
-  /**
-   * Separates a string like 'emails.location_type.label' into an array, where
-   * each value in the array tells whether it is 1-1 or 1-n join type
-   *
-   * @param string $pathString
-   *   Dot separated path to the field
-   *
-   * @return array
-   *   Index is table alias and value is boolean whether is 1-to-many join
-   */
-  public function getPathJoinTypes($pathString) {
-    $pathParts = explode('.', $pathString);
-    // remove field
-    array_pop($pathParts);
-    $path = [];
-    $query = $this;
-    $isMultipleChecker = function($alias) use ($query) {
-      foreach ($query->getJoinedTables() as $table) {
-        if ($table->getAlias() === $alias) {
-          return $table->getJoinType() === Joinable::JOIN_TYPE_ONE_TO_MANY;
-        }
-      }
-      return FALSE;
-    };
-
-    foreach ($pathParts as $part) {
-      $path[$part] = $isMultipleChecker($part);
-    }
-
-    return $path;
+  public function constructQueryObject() {
+    $tableName = CoreUtil::getTableName($this->entity);
+    $this->query = \CRM_Utils_SQL_Select::from($tableName . ' ' . self::MAIN_TABLE_ALIAS);
   }
 
   /**
@@ -671,7 +656,7 @@ class Api4SelectQuery extends SelectQuery {
       return;
     }
     $defaults = [];
-    $defaults['is_custom'] = $defaults['is_join'] = $defaults['is_many'] = FALSE;
+    $defaults['is_custom'] = $defaults['is_join'] = FALSE;
     $field += $defaults;
     $this->apiFieldSpec[$path] = $field;
   }
diff --git a/civicrm/Civi/Api4/Query/SqlExpression.php b/civicrm/Civi/Api4/Query/SqlExpression.php
index e21f3880f4fb72d601ef070a24d7936b1b391dff..2759d3baff0a72b28302685f7f119815dc81cc79 100644
--- a/civicrm/Civi/Api4/Query/SqlExpression.php
+++ b/civicrm/Civi/Api4/Query/SqlExpression.php
@@ -35,7 +35,7 @@ abstract class SqlExpression {
    * The raw expression, minus the alias.
    * @var string
    */
-  protected $expr = '';
+  public $expr = '';
 
   /**
    * SqlFunction constructor.
@@ -148,4 +148,14 @@ abstract class SqlExpression {
     return $this->alias ?? $this->fields[0] ?? \CRM_Utils_String::munge($this->expr);
   }
 
+  /**
+   * Returns the name of this sql expression class.
+   *
+   * @return string
+   */
+  public function getType(): string {
+    $className = get_class($this);
+    return substr($className, strrpos($className, '\\') + 1);
+  }
+
 }
diff --git a/civicrm/Civi/Api4/Result/ReplaceResult.php b/civicrm/Civi/Api4/Result/ReplaceResult.php
index c1d6522f860dda76f85a53218b78da01b9f587f8..890a2a87e00484eaf5d63feb8eb29103df8be44f 100644
--- a/civicrm/Civi/Api4/Result/ReplaceResult.php
+++ b/civicrm/Civi/Api4/Result/ReplaceResult.php
@@ -10,16 +10,13 @@
  +--------------------------------------------------------------------+
  */
 
+namespace Civi\Api4\Result;
+
 /**
+ * Class ReplaceResult
  *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
+ * @package Civi\Api4\Result
  */
-
-namespace Civi\Api4\Result;
-
 class ReplaceResult extends \Civi\Api4\Generic\Result {
   /**
    * @var array
diff --git a/civicrm/Civi/Api4/Service/Schema/Joinable/CustomGroupJoinable.php b/civicrm/Civi/Api4/Service/Schema/Joinable/CustomGroupJoinable.php
index fd5ab30f9ddf9652bec69dfab6b2758422c730e2..61b38dccd14471c5f721d7cf1b043b314a7b7aa2 100644
--- a/civicrm/Civi/Api4/Service/Schema/Joinable/CustomGroupJoinable.php
+++ b/civicrm/Civi/Api4/Service/Schema/Joinable/CustomGroupJoinable.php
@@ -41,15 +41,17 @@ class CustomGroupJoinable extends Joinable {
    * @param $targetTable
    * @param $alias
    * @param bool $isMultiRecord
-   * @param string $entity
    * @param string $columns
    */
-  public function __construct($targetTable, $alias, $isMultiRecord, $entity, $columns) {
-    $this->entity = $entity;
+  public function __construct($targetTable, $alias, $isMultiRecord, $columns) {
     $this->columns = $columns;
     parent::__construct($targetTable, 'entity_id', $alias);
     $this->joinType = $isMultiRecord ?
       self::JOIN_TYPE_ONE_TO_MANY : self::JOIN_TYPE_ONE_TO_ONE;
+    // Only multi-record groups are considered an api "entity"
+    if (!$isMultiRecord) {
+      $this->entity = NULL;
+    }
   }
 
   /**
@@ -59,11 +61,11 @@ class CustomGroupJoinable extends Joinable {
     if (!$this->entityFields) {
       $fields = CustomField::get()
         ->setCheckPermissions(FALSE)
-        ->setSelect(['custom_group.name', '*'])
+        ->setSelect(['custom_group.name', 'custom_group.extends', 'custom_group.table_name', '*'])
         ->addWhere('custom_group.table_name', '=', $this->getTargetTable())
         ->execute();
       foreach ($fields as $field) {
-        $this->entityFields[] = \Civi\Api4\Service\Spec\SpecFormatter::arrayToField($field, $this->getEntity());
+        $this->entityFields[] = \Civi\Api4\Service\Spec\SpecFormatter::arrayToField($field, $this->getEntityFromExtends($field['custom_group.extends']));
       }
     }
     return $this->entityFields;
@@ -92,4 +94,25 @@ class CustomGroupJoinable extends Joinable {
     return $this->columns[$fieldName];
   }
 
+  /**
+   * Translate custom_group.extends to entity name.
+   *
+   * Custom_group.extends pretty much maps 1-1 with entity names, except for a couple oddballs.
+   * @see \CRM_Core_SelectValues::customGroupExtends
+   *
+   * @param $extends
+   * @return string
+   * @throws \API_Exception
+   * @throws \Civi\API\Exception\UnauthorizedException
+   */
+  private function getEntityFromExtends($extends) {
+    if (strpos($extends, 'Participant') === 0) {
+      return 'Participant';
+    }
+    if ($extends === 'Contact' || in_array($extends, \CRM_Contact_BAO_ContactType::basicTypes(TRUE))) {
+      return 'Contact';
+    }
+    return $extends;
+  }
+
 }
diff --git a/civicrm/Civi/Api4/Service/Schema/Joinable/OptionValueJoinable.php b/civicrm/Civi/Api4/Service/Schema/Joinable/OptionValueJoinable.php
deleted file mode 100644
index 6b9b539faab45988f3f75096c25291ee3988a441..0000000000000000000000000000000000000000
--- a/civicrm/Civi/Api4/Service/Schema/Joinable/OptionValueJoinable.php
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-
-/*
- +--------------------------------------------------------------------+
- | Copyright CiviCRM LLC. All rights reserved.                        |
- |                                                                    |
- | This work is published under the GNU AGPLv3 license with some      |
- | permitted exceptions and without any warranty. For full license    |
- | and copyright information, see https://civicrm.org/licensing       |
- +--------------------------------------------------------------------+
- */
-
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
-
-namespace Civi\Api4\Service\Schema\Joinable;
-
-class OptionValueJoinable extends Joinable {
-  /**
-   * @var string
-   */
-  protected $optionGroupName;
-
-  /**
-   * @param string $optionGroup
-   *   Can be either the option group name or ID
-   * @param string|null $alias
-   *   The join alias
-   * @param string $keyColumn
-   *   Which column to use to join, defaults to "value"
-   */
-  public function __construct($optionGroup, $alias = NULL, $keyColumn = 'value') {
-    $this->optionGroupName = $optionGroup;
-    $optionValueTable = 'civicrm_option_value';
-
-    // default join alias to option group name, e.g. activity_type
-    if (!$alias && !is_numeric($optionGroup)) {
-      $alias = $optionGroup;
-    }
-
-    parent::__construct($optionValueTable, $keyColumn, $alias);
-
-    if (!is_numeric($optionGroup)) {
-      $subSelect = 'SELECT id FROM civicrm_option_group WHERE name = "%s"';
-      $subQuery = sprintf($subSelect, $optionGroup);
-      $condition = sprintf('%s.option_group_id = (%s)', $alias, $subQuery);
-    }
-    else {
-      $condition = sprintf('%s.option_group_id = %d', $alias, $optionGroup);
-    }
-
-    $this->addCondition($condition);
-  }
-
-  /**
-   * The existing condition must also be re-aliased
-   *
-   * @param string $alias
-   *
-   * @return $this
-   */
-  public function setAlias($alias) {
-    foreach ($this->conditions as $index => $condition) {
-      $search = $this->alias . '.';
-      $replace = $alias . '.';
-      $this->conditions[$index] = str_replace($search, $replace, $condition);
-    }
-
-    parent::setAlias($alias);
-
-    return $this;
-  }
-
-}
diff --git a/civicrm/Civi/Api4/Service/Schema/Joiner.php b/civicrm/Civi/Api4/Service/Schema/Joiner.php
index 6333e14a779e743b0a0de847b0eae59692e9f527..4bd59de1715015b4a1749ca52e1658f066f7f449 100644
--- a/civicrm/Civi/Api4/Service/Schema/Joiner.php
+++ b/civicrm/Civi/Api4/Service/Schema/Joiner.php
@@ -60,10 +60,14 @@ class Joiner {
     foreach ($fullPath as $link) {
       $target = $link->getTargetTable();
       $alias = $link->getAlias();
+      $bao = \CRM_Core_DAO_AllCoreTables::getBAOClassName(\CRM_Core_DAO_AllCoreTables::getClassForTable($target));
       $conditions = $link->getConditionsForJoin($baseTable);
+      // Custom fields do not have a bao, and currently do not have field-specific ACLs
+      if ($bao) {
+        $conditions = array_merge($conditions, $query->getAclClause($alias, $bao, explode('.', $joinPath)));
+      }
 
       $query->join($side, $target, $alias, $conditions);
-      $query->addJoinedTable($link);
 
       $baseTable = $link->getAlias();
     }
@@ -72,20 +76,33 @@ class Joiner {
   }
 
   /**
-   * @param \Civi\Api4\Query\Api4SelectQuery $query
+   * Determines if path string points to a simple n-1 join that can be automatically added
+   *
+   * @param string $baseTable
    * @param $joinPath
    *
    * @return bool
    */
-  public function canJoin(Api4SelectQuery $query, $joinPath) {
-    return !empty($this->getPath($query->getFrom(), $joinPath));
+  public function canAutoJoin($baseTable, $joinPath) {
+    try {
+      $path = $this->getPath($baseTable, $joinPath);
+      foreach ($path as $joinable) {
+        if ($joinable->getJoinType() === $joinable::JOIN_TYPE_ONE_TO_MANY) {
+          return FALSE;
+        }
+      }
+      return TRUE;
+    }
+    catch (\Exception $e) {
+      return FALSE;
+    }
   }
 
   /**
    * @param string $baseTable
    * @param string $joinPath
    *
-   * @return array
+   * @return \Civi\Api4\Service\Schema\Joinable\Joinable[]
    * @throws \Exception
    */
   protected function getPath($baseTable, $joinPath) {
diff --git a/civicrm/Civi/Api4/Service/Schema/SchemaMapBuilder.php b/civicrm/Civi/Api4/Service/Schema/SchemaMapBuilder.php
index 74dc0f34536906561efd7fc71539554f9c8bda1c..afecc01056ade66658293062293a826477856fba 100644
--- a/civicrm/Civi/Api4/Service/Schema/SchemaMapBuilder.php
+++ b/civicrm/Civi/Api4/Service/Schema/SchemaMapBuilder.php
@@ -27,7 +27,6 @@ use Civi\Api4\Event\SchemaMapBuildEvent;
 use Civi\Api4\Service\Schema\Joinable\CustomGroupJoinable;
 use Civi\Api4\Service\Schema\Joinable\Joinable;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-use Civi\Api4\Service\Schema\Joinable\OptionValueJoinable;
 use CRM_Core_DAO_AllCoreTables as AllCoreTables;
 
 class SchemaMapBuilder {
@@ -99,41 +98,6 @@ class SchemaMapBuilder {
       $joinable->setJoinType($joinable::JOIN_TYPE_MANY_TO_ONE);
       $table->addTableLink($field, $joinable);
     }
-    elseif (!empty($data['pseudoconstant'])) {
-      $this->addPseudoConstantJoin($table, $field, $data);
-    }
-  }
-
-  /**
-   * @param Table $table
-   * @param string $field
-   * @param array $data
-   */
-  private function addPseudoConstantJoin(Table $table, $field, array $data) {
-    $pseudoConstant = $data['pseudoconstant'] ?? NULL;
-    $tableName = $pseudoConstant['table'] ?? NULL;
-    $optionGroupName = $pseudoConstant['optionGroupName'] ?? NULL;
-    $keyColumn = $pseudoConstant['keyColumn'] ?? 'id';
-
-    if ($tableName) {
-      $alias = str_replace('civicrm_', '', $tableName);
-      $joinable = new Joinable($tableName, $keyColumn, $alias);
-      $condition = $pseudoConstant['condition'] ?? NULL;
-      if ($condition) {
-        $joinable->addCondition($condition);
-      }
-      $table->addTableLink($field, $joinable);
-    }
-    elseif ($optionGroupName) {
-      $keyColumn = $pseudoConstant['keyColumn'] ?? 'value';
-      $joinable = new OptionValueJoinable($optionGroupName, NULL, $keyColumn);
-
-      if (!empty($data['serialize'])) {
-        $joinable->setJoinType($joinable::JOIN_TYPE_ONE_TO_MANY);
-      }
-
-      $table->addTableLink($field, $joinable);
-    }
   }
 
   /**
@@ -144,17 +108,15 @@ class SchemaMapBuilder {
   private function addBackReferences(SchemaMap $map) {
     foreach ($map->getTables() as $table) {
       foreach ($table->getTableLinks() as $link) {
-        // there are too many possible joins from option value so skip
-        if ($link instanceof OptionValueJoinable) {
-          continue;
-        }
-
         $target = $map->getTableByName($link->getTargetTable());
         $tableName = $link->getBaseTable();
-        $plural = str_replace('civicrm_', '', $this->getPlural($tableName));
-        $joinable = new Joinable($tableName, $link->getBaseColumn(), $plural);
-        $joinable->setJoinType($joinable::JOIN_TYPE_ONE_TO_MANY);
-        $target->addTableLink($link->getTargetColumn(), $joinable);
+        // Exclude custom field tables
+        if (strpos($link->getTargetTable(), 'civicrm_value_') !== 0 && strpos($link->getBaseTable(), 'civicrm_value_') !== 0) {
+          $plural = str_replace('civicrm_', '', $this->getPlural($tableName));
+          $joinable = new Joinable($tableName, $link->getBaseColumn(), $plural);
+          $joinable->setJoinType($joinable::JOIN_TYPE_ONE_TO_MANY);
+          $target->addTableLink($link->getTargetColumn(), $joinable);
+        }
       }
     }
   }
@@ -213,21 +175,22 @@ class SchemaMapBuilder {
         $customTable = new Table($tableName);
       }
 
-      if (!empty($fieldData->option_group_id)) {
-        $optionValueJoinable = new OptionValueJoinable($fieldData->option_group_id, $fieldData->label);
-        $customTable->addTableLink($fieldData->column_name, $optionValueJoinable);
-      }
-
       $map->addTable($customTable);
 
       $alias = $fieldData->custom_group_name;
       $links[$alias]['tableName'] = $tableName;
       $links[$alias]['isMultiple'] = !empty($fieldData->is_multiple);
       $links[$alias]['columns'][$fieldData->name] = $fieldData->column_name;
+
+      // Add backreference
+      if (!empty($fieldData->is_multiple)) {
+        $joinable = new Joinable($baseTable->getName(), 'id', AllCoreTables::convertEntityNameToLower($entity));
+        $customTable->addTableLink('entity_id', $joinable);
+      }
     }
 
     foreach ($links as $alias => $link) {
-      $joinable = new CustomGroupJoinable($link['tableName'], $alias, $link['isMultiple'], $entity, $link['columns']);
+      $joinable = new CustomGroupJoinable($link['tableName'], $alias, $link['isMultiple'], $link['columns']);
       $baseTable->addTableLink('id', $joinable);
     }
   }
diff --git a/civicrm/Civi/Api4/Service/Spec/Provider/ContactTypeCreationSpecProvider.php b/civicrm/Civi/Api4/Service/Spec/Provider/ContactTypeCreationSpecProvider.php
index 1e45728d8c874d58da886b9b0ce6f3993e40703c..891fe67920e862ca8e07e331d6ab7df671bf7d6f 100644
--- a/civicrm/Civi/Api4/Service/Spec/Provider/ContactTypeCreationSpecProvider.php
+++ b/civicrm/Civi/Api4/Service/Spec/Provider/ContactTypeCreationSpecProvider.php
@@ -10,19 +10,15 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
-
 namespace Civi\Api4\Service\Spec\Provider;
 
 use Civi\Api4\Service\Spec\RequestSpec;
 
+/**
+ * Class ContactTypeCreationSpecProvider
+ *
+ * @package Civi\Api4\Service\Spec\Provider
+ */
 class ContactTypeCreationSpecProvider implements Generic\SpecProviderInterface {
 
   /**
diff --git a/civicrm/Civi/Api4/Service/Spec/Provider/ContributionCreationSpecProvider.php b/civicrm/Civi/Api4/Service/Spec/Provider/ContributionCreationSpecProvider.php
index 1a370b34492479c5b9386a18db6a825b7427b51a..a7095a0ba30d6e405ab32d4e503ece8a9c352ca3 100644
--- a/civicrm/Civi/Api4/Service/Spec/Provider/ContributionCreationSpecProvider.php
+++ b/civicrm/Civi/Api4/Service/Spec/Provider/ContributionCreationSpecProvider.php
@@ -10,19 +10,15 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
-
 namespace Civi\Api4\Service\Spec\Provider;
 
 use Civi\Api4\Service\Spec\RequestSpec;
 
+/**
+ * Class ContributionCreationSpecProvider
+ *
+ * @package Civi\Api4\Service\Spec\Provider
+ */
 class ContributionCreationSpecProvider implements Generic\SpecProviderInterface {
 
   /**
diff --git a/civicrm/Civi/Api4/Service/Spec/Provider/ContributionRecurCreationSpecProvider.php b/civicrm/Civi/Api4/Service/Spec/Provider/ContributionRecurCreationSpecProvider.php
new file mode 100644
index 0000000000000000000000000000000000000000..6d38d6a9c02a2368f69491a94a584689c52ddd22
--- /dev/null
+++ b/civicrm/Civi/Api4/Service/Spec/Provider/ContributionRecurCreationSpecProvider.php
@@ -0,0 +1,39 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+
+namespace Civi\Api4\Service\Spec\Provider;
+
+use Civi\Api4\Service\Spec\RequestSpec;
+
+/**
+ * Class ContributionRecurCreationSpecProvider
+ *
+ * @package Civi\Api4\Service\Spec\Provider
+ */
+class ContributionRecurCreationSpecProvider implements Generic\SpecProviderInterface {
+
+  /**
+   * @inheritDoc
+   */
+  public function modifySpec(RequestSpec $spec) {
+    $spec->getFieldByName('create_date')->setDefaultValue('now');
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function applies($entity, $action) {
+    return $entity === 'ContributionRecur' && $action === 'create';
+  }
+
+}
diff --git a/civicrm/Civi/Api4/Service/Spec/Provider/DomainCreationSpecProvider.php b/civicrm/Civi/Api4/Service/Spec/Provider/DomainCreationSpecProvider.php
index 061595f6cfa07fa1973aa0c15df27326f1932d8c..b417dbad7297eb3df15de1fbb3a4cfe7741baee6 100644
--- a/civicrm/Civi/Api4/Service/Spec/Provider/DomainCreationSpecProvider.php
+++ b/civicrm/Civi/Api4/Service/Spec/Provider/DomainCreationSpecProvider.php
@@ -10,15 +10,6 @@
  +--------------------------------------------------------------------+
  */
 
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
-
 namespace Civi\Api4\Service\Spec\Provider;
 
 use Civi\Api4\Service\Spec\RequestSpec;
diff --git a/civicrm/Civi/Api4/Event/GetSpecEvent.php b/civicrm/Civi/Api4/Service/Spec/Provider/PriceFieldValueCreationSpecProvider.php
similarity index 50%
rename from civicrm/Civi/Api4/Event/GetSpecEvent.php
rename to civicrm/Civi/Api4/Service/Spec/Provider/PriceFieldValueCreationSpecProvider.php
index 16e1e84ba277c29c77d0f96fbe69b822475123bc..00c6b19d1e6c8952e0cb0ce32d98cecbd077b5ea 100644
--- a/civicrm/Civi/Api4/Event/GetSpecEvent.php
+++ b/civicrm/Civi/Api4/Service/Spec/Provider/PriceFieldValueCreationSpecProvider.php
@@ -14,41 +14,28 @@
  *
  * @package CRM
  * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
  */
 
 
-namespace Civi\Api4\Event;
-
-use Civi\Api4\Generic\AbstractAction;
-use Symfony\Component\EventDispatcher\Event as BaseEvent;
+namespace Civi\Api4\Service\Spec\Provider;
 
-class GetSpecEvent extends BaseEvent {
-  /**
-   * @var \Civi\Api4\Generic\AbstractAction
-   */
-  protected $request;
+use Civi\Api4\Service\Spec\RequestSpec;
 
-  /**
-   * @param \Civi\Api4\Generic\AbstractAction $request
-   */
-  public function __construct(AbstractAction $request) {
-    $this->request = $request;
-  }
+class PriceFieldValueCreationSpecProvider implements Generic\SpecProviderInterface {
 
   /**
-   * @return \Civi\Api4\Generic\AbstractAction
+   * @inheritDoc
    */
-  public function getRequest() {
-    return $this->request;
+  public function modifySpec(RequestSpec $spec) {
+    // Name will be auto-generated from label if not supplied
+    $spec->getFieldByName('name')->setRequired(FALSE);
   }
 
   /**
-   * @param $request
+   * @inheritDoc
    */
-  public function setRequest(AbstractAction $request) {
-    $this->request = $request;
+  public function applies($entity, $action) {
+    return $entity === 'PriceFieldValue' && $action === 'create';
   }
 
 }
diff --git a/civicrm/Civi/Api4/Service/Spec/SpecFormatter.php b/civicrm/Civi/Api4/Service/Spec/SpecFormatter.php
index c6d884f1bec9c16aa230d61278ab312fce8724a0..1da26d1520a984492b829f9ad8da81a70e3a1ec2 100644
--- a/civicrm/Civi/Api4/Service/Spec/SpecFormatter.php
+++ b/civicrm/Civi/Api4/Service/Spec/SpecFormatter.php
@@ -67,9 +67,6 @@ class SpecFormatter {
       $field->setHelpPre($data['help_pre'] ?? NULL);
       $field->setHelpPost($data['help_post'] ?? NULL);
       $field->setOptions(self::customFieldHasOptions($data));
-      if (\CRM_Core_BAO_CustomField::isSerialized($data)) {
-        $field->setSerialize(\CRM_Core_DAO::SERIALIZE_SEPARATOR_BOOKEND);
-      }
     }
     else {
       $name = $data['name'] ?? NULL;
@@ -77,9 +74,8 @@ class SpecFormatter {
       $field->setRequired(!empty($data['required']));
       $field->setTitle($data['title'] ?? NULL);
       $field->setOptions(!empty($data['pseudoconstant']));
-      $field->setSerialize($data['serialize'] ?? NULL);
     }
-
+    $field->setSerialize($data['serialize'] ?? NULL);
     $field->setDefaultValue($data['default'] ?? NULL);
     $field->setDescription($data['description'] ?? NULL);
     self::setInputTypeAndAttrs($field, $data, $dataTypeName);
@@ -145,10 +141,6 @@ class SpecFormatter {
     $inputAttrs = $data['html'] ?? [];
     unset($inputAttrs['type']);
 
-    if (strstr($inputType, 'Multi-Select') || ($inputType == 'Select' && !empty($data['serialize']))) {
-      $inputAttrs['multiple'] = TRUE;
-      $inputType = 'Select';
-    }
     $map = [
       'Select State/Province' => 'Select',
       'Select Country' => 'Select',
@@ -156,6 +148,9 @@ class SpecFormatter {
       'Link' => 'Url',
     ];
     $inputType = $map[$inputType] ?? $inputType;
+    if ($inputType == 'Select' && !empty($data['serialize'])) {
+      $inputAttrs['multiple'] = TRUE;
+    }
     if ($inputType == 'Date' && !empty($inputAttrs['formatType'])) {
       self::setLegacyDateFormat($inputAttrs);
     }
diff --git a/civicrm/Civi/Api4/Service/Spec/SpecGatherer.php b/civicrm/Civi/Api4/Service/Spec/SpecGatherer.php
index d9909c477d2a784a51312a5de07c12733f1cc719..00fa09b5032e1b1a74fd5022229b053804385ebf 100644
--- a/civicrm/Civi/Api4/Service/Spec/SpecGatherer.php
+++ b/civicrm/Civi/Api4/Service/Spec/SpecGatherer.php
@@ -14,7 +14,6 @@
  *
  * @package CRM
  * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
  *
  */
 
@@ -32,13 +31,6 @@ class SpecGatherer {
    */
   protected $specProviders = [];
 
-  /**
-   * A cache of DAOs based on entity
-   *
-   * @var \CRM_Core_DAO[]
-   */
-  protected $DAONames;
-
   /**
    * Returns a RequestSpec with all the fields available. Uses spec providers
    * to add or modify field specifications.
@@ -117,15 +109,24 @@ class SpecGatherer {
   }
 
   /**
+   * Get custom fields that extend this entity
+   *
+   * @see \CRM_Core_SelectValues::customGroupExtends
+   *
    * @param string $entity
    * @param \Civi\Api4\Service\Spec\RequestSpec $specification
    * @param array $values
    * @throws \API_Exception
    */
   private function addCustomFields($entity, RequestSpec $specification, $values = []) {
+    // Custom_group.extends pretty much maps 1-1 with entity names, except for a couple oddballs (Contact, Participant).
     $extends = [$entity];
     if ($entity === 'Contact') {
-      $extends = !empty($values['contact_type']) ? [$values['contact_type'], 'Contact'] : ['Contact', 'Individual', 'Organization', 'Household'];
+      $contactType = !empty($values['contact_type']) ? [$values['contact_type']] : \CRM_Contact_BAO_ContactType::basicTypes();
+      $extends = array_merge(['Contact'], $contactType);
+    }
+    if ($entity === 'Participant') {
+      $extends = ['Participant', 'ParticipantRole', 'ParticipantEventName', 'ParticipantEventType'];
     }
     $customFields = CustomField::get()
       ->setCheckPermissions(FALSE)
@@ -146,6 +147,7 @@ class SpecGatherer {
    */
   private function getCustomGroupFields($customGroup, RequestSpec $specification) {
     $customFields = CustomField::get()
+      ->setCheckPermissions(FALSE)
       ->addWhere('custom_group.name', '=', $customGroup)
       ->setSelect(['custom_group.name', 'custom_group.table_name', '*'])
       ->execute();
diff --git a/civicrm/Civi/Api4/Utils/ArrayInsertionUtil.php b/civicrm/Civi/Api4/Utils/ArrayInsertionUtil.php
index 4c2d2f6b2d53470d21fe202e6219bbc4a9401a43..951f769790a0691416646bc5f67005138dd84191 100644
--- a/civicrm/Civi/Api4/Utils/ArrayInsertionUtil.php
+++ b/civicrm/Civi/Api4/Utils/ArrayInsertionUtil.php
@@ -10,17 +10,13 @@
  +--------------------------------------------------------------------+
  */
 
+namespace Civi\Api4\Utils;
+
 /**
+ * Class ArrayInsertionUtil
  *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
+ * @package Civi\Api4\Utils
  */
-
-
-namespace Civi\Api4\Utils;
-
 class ArrayInsertionUtil {
 
   /**
diff --git a/civicrm/Civi/Api4/Utils/CoreUtil.php b/civicrm/Civi/Api4/Utils/CoreUtil.php
index 9055a63149667070edbfdaf59b87b8c5787bd9f2..1f4c7766a4b44f9a925d79e81b5a55082785c544 100644
--- a/civicrm/Civi/Api4/Utils/CoreUtil.php
+++ b/civicrm/Civi/Api4/Utils/CoreUtil.php
@@ -21,7 +21,6 @@
 
 namespace Civi\Api4\Utils;
 
-use Civi\Api4\CustomGroup;
 use CRM_Core_DAO_AllCoreTables as AllCoreTables;
 
 require_once 'api/v3/utils.php';
@@ -39,34 +38,39 @@ class CoreUtil {
    */
   public static function getBAOFromApiName($entityName) {
     if ($entityName === 'CustomValue' || strpos($entityName, 'Custom_') === 0) {
-      return 'CRM_Contact_BAO_Contact';
+      return 'CRM_Core_BAO_CustomValue';
     }
     return \_civicrm_api3_get_BAO($entityName);
   }
 
   /**
-   * Get table name of given Custom group
+   * Get table name of given entity
    *
-   * @param string $customGroupName
+   * @param string $entityName
    *
    * @return string
    */
-  public static function getCustomTableByName($customGroupName) {
-    return CustomGroup::get()
-      ->addSelect('table_name')
-      ->addWhere('name', '=', $customGroupName)
-      ->execute()
-      ->first()['table_name'];
+  public static function getTableName($entityName) {
+    if (strpos($entityName, 'Custom_') === 0) {
+      $customGroup = substr($entityName, 7);
+      return \CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $customGroup, 'table_name', 'name');
+    }
+    return AllCoreTables::getTableForEntityName($entityName);
   }
 
   /**
    * Given a sql table name, return the name of the api entity.
    *
    * @param $tableName
-   * @return string
+   * @return string|NULL
    */
   public static function getApiNameFromTableName($tableName) {
-    return AllCoreTables::getBriefName(AllCoreTables::getClassForTable($tableName));
+    $entityName = AllCoreTables::getBriefName(AllCoreTables::getClassForTable($tableName));
+    if (!$entityName) {
+      $customGroup = \CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $tableName, 'name', 'table_name');
+      $entityName = $customGroup ? "Custom_$customGroup" : NULL;
+    }
+    return $entityName;
   }
 
 }
diff --git a/civicrm/Civi/Api4/Utils/FormattingUtil.php b/civicrm/Civi/Api4/Utils/FormattingUtil.php
index be91ce41e1991640d2de9e081895456ce9b099f2..77e50db8f5370820fbe158cdf242eb29f9c42ad3 100644
--- a/civicrm/Civi/Api4/Utils/FormattingUtil.php
+++ b/civicrm/Civi/Api4/Utils/FormattingUtil.php
@@ -31,6 +31,8 @@ class FormattingUtil {
     'label' => 'get',
   ];
 
+  public static $pseudoConstantSuffixes = ['name', 'abbr', 'label', 'color', 'description', 'icon'];
+
   /**
    * Massage values into the format the BAO expects for a write operation
    *
@@ -46,7 +48,7 @@ class FormattingUtil {
         if ($value === 'null') {
           $value = 'Null';
         }
-        self::formatInputValue($value, $name, $field);
+        self::formatInputValue($value, $name, $field, 'create');
         // Ensure we have an array for serialized fields
         if (!empty($field['serialize'] && !is_array($value))) {
           $value = (array) $value;
@@ -80,18 +82,21 @@ class FormattingUtil {
    * @param $value
    * @param string $fieldName
    * @param array $fieldSpec
+   * @param string $action
    * @throws \API_Exception
+   * @throws \CRM_Core_Exception
    */
-  public static function formatInputValue(&$value, $fieldName, $fieldSpec) {
+  public static function formatInputValue(&$value, $fieldName, $fieldSpec, $action = 'get') {
     // Evaluate pseudoconstant suffix
     $suffix = strpos($fieldName, ':');
     if ($suffix) {
-      $options = self::getPseudoconstantList($fieldSpec['entity'], $fieldSpec['name'], substr($fieldName, $suffix + 1));
+      $options = self::getPseudoconstantList($fieldSpec['entity'], $fieldSpec['name'], substr($fieldName, $suffix + 1), $action);
       $value = self::replacePseudoconstant($options, $value, TRUE);
+      return;
     }
     elseif (is_array($value)) {
       foreach ($value as &$val) {
-        self::formatInputValue($val, $fieldName, $fieldSpec);
+        self::formatInputValue($val, $fieldName, $fieldSpec, $action);
       }
       return;
     }
@@ -188,17 +193,20 @@ class FormattingUtil {
    */
   public static function getPseudoconstantList($entity, $fieldName, $valueType, $params = [], $action = 'get') {
     $context = self::$pseudoConstantContexts[$valueType] ?? NULL;
-    if (!$context) {
+    // For create actions, only unique identifiers can be used.
+    // For get actions any valid suffix is ok.
+    if (($action === 'create' && !$context) || !in_array($valueType, self::$pseudoConstantSuffixes, TRUE)) {
       throw new \API_Exception('Illegal expression');
     }
-    $baoName = CoreUtil::getBAOFromApiName($entity);
+    $baoName = $context ? CoreUtil::getBAOFromApiName($entity) : NULL;
     // Use BAO::buildOptions if possible
     if ($baoName) {
       $options = $baoName::buildOptions($fieldName, $context, $params);
     }
-    // Fallback for option lists that exist in the api but not the BAO - note: $valueType gets ignored here
+    // Fallback for option lists that exist in the api but not the BAO
     if (!isset($options) || $options === FALSE) {
-      $options = civicrm_api4($entity, 'getFields', ['action' => $action, 'loadOptions' => TRUE, 'where' => [['name', '=', $fieldName]]])[0]['options'] ?? NULL;
+      $options = civicrm_api4($entity, 'getFields', ['action' => $action, 'loadOptions' => ['id', $valueType], 'where' => [['name', '=', $fieldName]]])[0]['options'] ?? NULL;
+      $options = $options ? array_column($options, $valueType, 'id') : $options;
     }
     if (is_array($options)) {
       return $options;
@@ -277,7 +285,7 @@ class FormattingUtil {
           \Civi::$statics[__CLASS__][__FUNCTION__][$contactType][] = $field['name'];
           // Include suffixed variants like prefix_id:label
           if (!empty($field['pseudoconstant'])) {
-            foreach (array_keys(self::$pseudoConstantContexts) as $suffix) {
+            foreach (self::$pseudoConstantSuffixes as $suffix) {
               \Civi::$statics[__CLASS__][__FUNCTION__][$contactType][] = $field['name'] . ':' . $suffix;
             }
           }
diff --git a/civicrm/Civi/Api4/services.xml b/civicrm/Civi/Api4/services.xml
index 3d8a2fcab14b35f4db2aa2c5e45c54c2baf7ae04..630a646326293518eebd9a87c8cefdf7b700b81e 100644
--- a/civicrm/Civi/Api4/services.xml
+++ b/civicrm/Civi/Api4/services.xml
@@ -4,21 +4,21 @@
 
     <services>
 
-        <service id="spec_gatherer" class="Civi\Api4\Service\Spec\SpecGatherer"/>
+        <service id="spec_gatherer" class="Civi\Api4\Service\Spec\SpecGatherer" public="true"/>
 
         <service id="schema_map_builder" class="Civi\Api4\Service\Schema\SchemaMapBuilder" public="false">
             <argument type="service" id="dispatcher" />
         </service>
 
-        <service id="schema_map" class="Civi\Api4\Service\Schema\SchemaMap">
+        <service id="schema_map" class="Civi\Api4\Service\Schema\SchemaMap" public="true">
           <factory service="schema_map_builder" method="build"/>
         </service>
 
-        <service id="joiner" class="Civi\Api4\Service\Schema\Joiner">
+        <service id="joiner" class="Civi\Api4\Service\Schema\Joiner" public="true">
             <argument type="service" id="schema_map"/>
         </service>
 
-        <service id="action_object_provider" class="Civi\Api4\Provider\ActionObjectProvider">
+        <service id="action_object_provider" class="Civi\Api4\Provider\ActionObjectProvider" public="true">
             <tag name="event_subscriber"/>
         </service>
 
diff --git a/civicrm/Civi/Core/AssetBuilder.php b/civicrm/Civi/Core/AssetBuilder.php
index 82e2141461b1e15e8ab74ce669683b9eac168a0a..155c32c77970df4d7c2462c046c23770009e2010 100644
--- a/civicrm/Civi/Core/AssetBuilder.php
+++ b/civicrm/Civi/Core/AssetBuilder.php
@@ -20,7 +20,7 @@ use Civi\Core\Exception\UnknownAssetException;
  * named "api-fields.json" which lists all the fields of
  * all the API entities.
  *
- * @code
+ * ```
  * // Build a URL to `api-fields.json`.
  * $url = \Civi::service('asset_builder')->getUrl('api-fields.json');
  *
@@ -37,7 +37,7 @@ use Civi\Core\Exception\UnknownAssetException;
  *   $mimeType = 'application/json';
  *   $content = json_encode($fields);
  * }
- * @endCode
+ * ```
  *
  * Assets can be parameterized. Each combination of ($asset,$params)
  * will be cached separately. For example, we might want a copy of
@@ -45,7 +45,7 @@ use Civi\Core\Exception\UnknownAssetException;
  * Simply pass the chosen entities into `getUrl()`, then update
  * the definition to use `$params['entities']`, as in:
  *
- * @code
+ * ```
  * // Build a URL to `api-fields.json`.
  * $url = \Civi::service('asset_builder')->getUrl('api-fields.json', array(
  *   'entities' => array('Contact', 'Phone', 'Email', 'Address'),
@@ -63,7 +63,7 @@ use Civi\Core\Exception\UnknownAssetException;
  *   $mimeType = 'application/json';
  *   $content = json_encode($fields);
  * }
- * @endCode
+ * ```
  *
  * Note: These assets are designed to hold non-sensitive data, such as
  * aggregated JS or common metadata. There probably are ways to
diff --git a/civicrm/Civi/Core/CiviEventDispatcher.php b/civicrm/Civi/Core/CiviEventDispatcher.php
index 18d8aa30438ddebef9d8b6bd078d078f54fa1c82..9f3da3215e4fcacc0659bf4836632f05e5313e05 100644
--- a/civicrm/Civi/Core/CiviEventDispatcher.php
+++ b/civicrm/Civi/Core/CiviEventDispatcher.php
@@ -2,7 +2,7 @@
 
 namespace Civi\Core;
 
-use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher;
+use Symfony\Component\EventDispatcher\EventDispatcher;
 use Symfony\Component\EventDispatcher\Event;
 
 /**
@@ -15,7 +15,7 @@ use Symfony\Component\EventDispatcher\Event;
  *
  * @see \CRM_Utils_Hook
  */
-class CiviEventDispatcher extends ContainerAwareEventDispatcher {
+class CiviEventDispatcher extends EventDispatcher {
 
   const DEFAULT_HOOK_PRIORITY = -100;
 
@@ -61,6 +61,34 @@ class CiviEventDispatcher extends ContainerAwareEventDispatcher {
     return (substr($eventName, 0, 5) === 'hook_') && (strpos($eventName, '::') === FALSE);
   }
 
+  /**
+   * Adds a service as event listener.
+   *
+   * This provides partial backwards compatibility with ContainerAwareEventDispatcher.
+   *
+   * @param string $eventName Event for which the listener is added
+   * @param array $callback The service ID of the listener service & the method
+   *                        name that has to be called
+   * @param int $priority The higher this value, the earlier an event listener
+   *                      will be triggered in the chain.
+   *                      Defaults to 0.
+   *
+   * @throws \InvalidArgumentException
+   */
+  public function addListenerService($eventName, $callback, $priority = 0) {
+    if (!\is_array($callback) || 2 !== \count($callback)) {
+      throw new \InvalidArgumentException('Expected an array("service", "method") argument');
+    }
+
+    $this->addListener($eventName, function($event) use ($callback) {
+      static $svc;
+      if ($svc === NULL) {
+        $svc = \Civi::container()->get($callback[0]);
+      }
+      return call_user_func([$svc, $callback[1]], $event);
+    }, $priority);
+  }
+
   /**
    * @inheritDoc
    */
diff --git a/civicrm/Civi/Core/CiviEventInspector.php b/civicrm/Civi/Core/CiviEventInspector.php
index 77d91684e25b2b25426a10e243c8153bd673118b..34514f9bf750dffa23203502a5133a5a22e6d0ed 100644
--- a/civicrm/Civi/Core/CiviEventInspector.php
+++ b/civicrm/Civi/Core/CiviEventInspector.php
@@ -7,10 +7,10 @@ namespace Civi\Core;
  * The event inspector is a development tool which provides metadata about events.
  * It can be used for code-generators and documentation-generators.
  *
- * @code
+ * ```
  * $i = new CiviEventInspector();
  * print_r(CRM_Utils_Array::collect('name', $i->getAll()));
- * @endCode
+ * ```
  *
  * An event definition includes these fields:
  *  - type: string, required. Ex: 'hook' or 'object'
diff --git a/civicrm/Civi/Core/Container.php b/civicrm/Civi/Core/Container.php
index 57ee30916408805770ad50c5f052e6a677b0edab..15c7860a9d64ce522f1b46b2bc029d2f5cfad5f3 100644
--- a/civicrm/Civi/Core/Container.php
+++ b/civicrm/Civi/Core/Container.php
@@ -121,32 +121,32 @@ class Container {
       'Civi\Angular\Manager',
       []
     ))
-      ->setFactory([new Reference(self::SELF), 'createAngularManager']);
+      ->setFactory([new Reference(self::SELF), 'createAngularManager'])->setPublic(TRUE);
 
     $container->setDefinition('dispatcher', new Definition(
       'Civi\Core\CiviEventDispatcher',
       [new Reference('service_container')]
     ))
-      ->setFactory([new Reference(self::SELF), 'createEventDispatcher']);
+      ->setFactory([new Reference(self::SELF), 'createEventDispatcher'])->setPublic(TRUE);
 
     $container->setDefinition('magic_function_provider', new Definition(
       'Civi\API\Provider\MagicFunctionProvider',
       []
-    ));
+    ))->setPublic(TRUE);
 
     $container->setDefinition('civi_api_kernel', new Definition(
       'Civi\API\Kernel',
       [new Reference('dispatcher'), new Reference('magic_function_provider')]
     ))
-      ->setFactory([new Reference(self::SELF), 'createApiKernel']);
+      ->setFactory([new Reference(self::SELF), 'createApiKernel'])->setPublic(TRUE);
 
     $container->setDefinition('cxn_reg_client', new Definition(
       'Civi\Cxn\Rpc\RegistrationClient',
       []
     ))
-      ->setFactory('CRM_Cxn_BAO_Cxn::createRegistrationClient');
+      ->setFactory('CRM_Cxn_BAO_Cxn::createRegistrationClient')->setPublic(TRUE);
 
-    $container->setDefinition('psr_log', new Definition('CRM_Core_Error_Log', []));
+    $container->setDefinition('psr_log', new Definition('CRM_Core_Error_Log', []))->setPublic(TRUE);
 
     $basicCaches = [
       'js_strings' => 'js_strings',
@@ -176,7 +176,7 @@ class Container {
       $container->setDefinition("cache.{$cacheSvc}", new Definition(
         'CRM_Utils_Cache_Interface',
         [$definitionParams]
-      ))->setFactory('CRM_Utils_Cache::create');
+      ))->setFactory('CRM_Utils_Cache::create')->setPublic(TRUE);
     }
 
     // PrevNextCache cannot use memory or array cache at the moment because the
@@ -189,22 +189,22 @@ class Container {
           'type' => ['SqlGroup'],
         ],
       ]
-    ))->setFactory('CRM_Utils_Cache::create');
+    ))->setFactory('CRM_Utils_Cache::create')->setPublic(TRUE);
 
     $container->setDefinition('sql_triggers', new Definition(
       'Civi\Core\SqlTriggers',
       []
-    ));
+    ))->setPublic(TRUE);
 
     $container->setDefinition('asset_builder', new Definition(
       'Civi\Core\AssetBuilder',
       []
-    ));
+    ))->setPublic(TRUE);
 
     $container->setDefinition('themes', new Definition(
       'Civi\Core\Themes',
       []
-    ));
+    ))->setPublic(TRUE);
 
     $container->setDefinition('pear_mail', new Definition('Mail'))
       ->setFactory('CRM_Utils_Mail::createMailer');
@@ -228,47 +228,47 @@ class Container {
       $container->setDefinition($name, new Definition(
         $class
       ))
-        ->setFactory([$class, 'singleton']);
+        ->setFactory([$class, 'singleton'])->setPublic(TRUE);
     }
     $container->setAlias('cache.short', 'cache.default');
 
     $container->setDefinition('resources', new Definition(
       'CRM_Core_Resources',
       [new Reference('service_container')]
-    ))->setFactory([new Reference(self::SELF), 'createResources']);
+    ))->setFactory([new Reference(self::SELF), 'createResources'])->setPublic(TRUE);
 
     $container->setDefinition('prevnext', new Definition(
       'CRM_Core_PrevNextCache_Interface',
       [new Reference('service_container')]
-    ))->setFactory([new Reference(self::SELF), 'createPrevNextCache']);
+    ))->setFactory([new Reference(self::SELF), 'createPrevNextCache'])->setPublic(TRUE);
 
     $container->setDefinition('prevnext.driver.sql', new Definition(
       'CRM_Core_PrevNextCache_Sql',
       []
-    ));
+    ))->setPublic(TRUE);
 
     $container->setDefinition('prevnext.driver.redis', new Definition(
       'CRM_Core_PrevNextCache_Redis',
       [new Reference('cache_config')]
-    ));
+    ))->setPublic(TRUE);
 
     $container->setDefinition('cache_config', new Definition('ArrayObject'))
-      ->setFactory([new Reference(self::SELF), 'createCacheConfig']);
+      ->setFactory([new Reference(self::SELF), 'createCacheConfig'])->setPublic(TRUE);
 
     $container->setDefinition('civi.mailing.triggers', new Definition(
       'Civi\Core\SqlTrigger\TimestampTriggers',
       ['civicrm_mailing', 'Mailing']
-    ))->addTag('kernel.event_listener', ['event' => 'hook_civicrm_triggerInfo', 'method' => 'onTriggerInfo']);
+    ))->addTag('kernel.event_listener', ['event' => 'hook_civicrm_triggerInfo', 'method' => 'onTriggerInfo'])->setPublic(TRUE);
 
     $container->setDefinition('civi.activity.triggers', new Definition(
       'Civi\Core\SqlTrigger\TimestampTriggers',
       ['civicrm_activity', 'Activity']
-    ))->addTag('kernel.event_listener', ['event' => 'hook_civicrm_triggerInfo', 'method' => 'onTriggerInfo']);
+    ))->addTag('kernel.event_listener', ['event' => 'hook_civicrm_triggerInfo', 'method' => 'onTriggerInfo'])->setPublic(TRUE);
 
     $container->setDefinition('civi.case.triggers', new Definition(
       'Civi\Core\SqlTrigger\TimestampTriggers',
       ['civicrm_case', 'Case']
-    ))->addTag('kernel.event_listener', ['event' => 'hook_civicrm_triggerInfo', 'method' => 'onTriggerInfo']);
+    ))->addTag('kernel.event_listener', ['event' => 'hook_civicrm_triggerInfo', 'method' => 'onTriggerInfo'])->setPublic(TRUE);
 
     $container->setDefinition('civi.case.staticTriggers', new Definition(
       'Civi\Core\SqlTrigger\StaticTriggers',
@@ -291,22 +291,22 @@ class Container {
         ],
       ]
     ))
-      ->addTag('kernel.event_listener', ['event' => 'hook_civicrm_triggerInfo', 'method' => 'onTriggerInfo']);
+      ->addTag('kernel.event_listener', ['event' => 'hook_civicrm_triggerInfo', 'method' => 'onTriggerInfo'])->setPublic(TRUE);
 
     $container->setDefinition('civi_token_compat', new Definition(
       'Civi\Token\TokenCompatSubscriber',
       []
-    ))->addTag('kernel.event_subscriber');
+    ))->addTag('kernel.event_subscriber')->setPublic(TRUE);
     $container->setDefinition("crm_mailing_action_tokens", new Definition(
       "CRM_Mailing_ActionTokens",
       []
-    ))->addTag('kernel.event_subscriber');
+    ))->addTag('kernel.event_subscriber')->setPublic(TRUE);
 
     foreach (['Activity', 'Contribute', 'Event', 'Mailing', 'Member'] as $comp) {
       $container->setDefinition("crm_" . strtolower($comp) . "_tokens", new Definition(
         "CRM_{$comp}_Tokens",
         []
-      ))->addTag('kernel.event_subscriber');
+      ))->addTag('kernel.event_subscriber')->setPublic(TRUE);
     }
 
     \CRM_Api4_Services::hook_container($container);
@@ -325,10 +325,10 @@ class Container {
 
   /**
    * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
-   * @return \Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher
+   * @return \Symfony\Component\EventDispatcher\EventDispatcher
    */
   public function createEventDispatcher($container) {
-    $dispatcher = new CiviEventDispatcher($container);
+    $dispatcher = new CiviEventDispatcher();
     if (\CRM_Core_Config::isUpgradeMode()) {
       $dispatcher->setDispatchPolicy(\CRM_Upgrade_DispatchPolicy::get('upgrade.main'));
     }
@@ -354,6 +354,7 @@ class Container {
     $dispatcher->addListener('hook_civicrm_buildAsset', ['\CRM_Core_Resources', 'renderMenubarStylesheet']);
     $dispatcher->addListener('hook_civicrm_coreResourceList', ['\CRM_Utils_System', 'appendCoreResources']);
     $dispatcher->addListener('hook_civicrm_getAssetUrl', ['\CRM_Utils_System', 'alterAssetUrl']);
+    $dispatcher->addListener('hook_civicrm_alterExternUrl', ['\CRM_Utils_System', 'migrateExternUrl'], 1000);
     $dispatcher->addListener('civi.dao.postInsert', ['\CRM_Core_BAO_RecurringEntity', 'triggerInsert']);
     $dispatcher->addListener('civi.dao.postUpdate', ['\CRM_Core_BAO_RecurringEntity', 'triggerUpdate']);
     $dispatcher->addListener('civi.dao.postDelete', ['\CRM_Core_BAO_RecurringEntity', 'triggerDelete']);
diff --git a/civicrm/Civi/Core/Event/GenericHookEvent.php b/civicrm/Civi/Core/Event/GenericHookEvent.php
index 6c62ea498afdf49140190647915545626d45f4d8..1b29ff76e9a9db9e28ef8d1cf32a3e856d407f6f 100644
--- a/civicrm/Civi/Core/Event/GenericHookEvent.php
+++ b/civicrm/Civi/Core/Event/GenericHookEvent.php
@@ -26,7 +26,7 @@ namespace Civi\Core\Event;
  * and methods. This requires some kind of mapping. `GenericHookEvent`
  * maps each parameter to a field (using magic methods):
  *
- * @code
+ * ```
  * // Creating an event object.
  * $event = GenericHookEvent::create(array(
  *   'bar' => 'abc',
@@ -41,7 +41,7 @@ namespace Civi\Core\Event;
  *
  * // Dispatching an event.
  * Civi::dispatcher()->dispatch('hook_civicrm_foo', $event);
- * @endCode
+ * ```
  *
  * Design Discussion:
  *
@@ -56,10 +56,10 @@ namespace Civi\Core\Event;
  * as an array, and all the returned values are merged into one big array.
  * You can add and retrieve return-values using these methods:
  *
- * @code
+ * ```
  * $event->addReturnValues(array(...));
  * foreach ($event->getReturnValues() as $retVal) { ... }
- * @endCode
+ * ```
  */
 class GenericHookEvent extends \Symfony\Component\EventDispatcher\Event {
 
diff --git a/civicrm/Civi/Core/Paths.php b/civicrm/Civi/Core/Paths.php
index d0d15814b3671b2cf202d741ae920c083b611b05..89b433679a90b42125d156ac864be853b0415b07 100644
--- a/civicrm/Civi/Core/Paths.php
+++ b/civicrm/Civi/Core/Paths.php
@@ -30,7 +30,10 @@ class Paths {
    * Class constructor.
    */
   public function __construct() {
-    $paths = $this;
+    // Below is a *default* set of functions to calculate paths/URLs.
+    // Some variables may be overridden as follow:
+    // - The global `$civicrm_paths` may be preset before Civi boots. (Ex: via `civicrm.settings.php`, `settings.php`, or `vendor/autoload.php`)
+    // - Variables may be re-registered. (Ex: via `CRM_Utils_System_WordPress`)
     $this
       ->register('civicrm.root', function () {
         return \CRM_Core_Config::singleton()->userSystem->getCiviSourceStorage();
@@ -84,24 +87,6 @@ class Paths {
           'path' => is_dir($dir) ? $dir : \Civi::paths()->getPath('[civicrm.root]/l10n'),
         ];
       })
-      ->register('wp.frontend.base', function () {
-        return ['url' => rtrim(CIVICRM_UF_BASEURL, '/') . '/'];
-      })
-      ->register('wp.frontend', function () use ($paths) {
-        $config = \CRM_Core_Config::singleton();
-        $suffix = defined('CIVICRM_UF_WP_BASEPAGE') ? CIVICRM_UF_WP_BASEPAGE : $config->wpBasePage;
-        return [
-          'url' => $paths->getVariable('wp.frontend.base', 'url') . $suffix,
-        ];
-      })
-      ->register('wp.backend.base', function () {
-        return ['url' => rtrim(CIVICRM_UF_BASEURL, '/') . '/wp-admin/'];
-      })
-      ->register('wp.backend', function () use ($paths) {
-        return [
-          'url' => $paths->getVariable('wp.backend.base', 'url') . 'admin.php',
-        ];
-      })
       ->register('cms', function () {
         return [
           'path' => \CRM_Core_Config::singleton()->userSystem->cmsRootPath(),
diff --git a/civicrm/Civi/Core/Resolver.php b/civicrm/Civi/Core/Resolver.php
index 379f94d64c3766f609c800f2a8187d72307b045a..a1ebfe2b0fdde0156742fc8276184a5e36ac24db 100644
--- a/civicrm/Civi/Core/Resolver.php
+++ b/civicrm/Civi/Core/Resolver.php
@@ -222,11 +222,11 @@ class ResolverApi {
   /**
    * Recursively interpolate values.
    *
-   * @code
+   * ```
    * $params = array('foo' => '@1');
    * $this->interpolate($params, array('@1'=> $object))
    * assert $data['foo'] == $object;
-   * @endcode
+   * ```
    *
    * @param array $array
    *   Array which may or many not contain a mix of tokens.
diff --git a/civicrm/Civi/Core/Themes.php b/civicrm/Civi/Core/Themes.php
index 8e7dc5988dc4e81904cd713ee6776b22f9b05c58..9156eb55588d081372a7ebf1b35859b9b113001f 100644
--- a/civicrm/Civi/Core/Themes.php
+++ b/civicrm/Civi/Core/Themes.php
@@ -133,7 +133,7 @@ class Themes {
    * @see CRM_Utils_Hook::themes
    */
   public function getAvailable() {
-    $result = array();
+    $result = [];
     foreach ($this->getAll() as $key => $theme) {
       if ($key{0} !== '_') {
         $result[$key] = $theme['title'];
@@ -164,14 +164,14 @@ class Themes {
   public function resolveUrls($active, $cssExt, $cssFile) {
     $all = $this->getAll();
     if (!isset($all[$active])) {
-      return array();
+      return [];
     }
 
     $cssId = $this->cssId($cssExt, $cssFile);
 
     foreach ($all[$active]['search_order'] as $themeKey) {
       if (isset($all[$themeKey]['excludes']) && in_array($cssId, $all[$themeKey]['excludes'])) {
-        $result = array();
+        $result = [];
       }
       else {
         $result = Civi\Core\Resolver::singleton()
diff --git a/civicrm/Civi/Payment/PropertyBag.php b/civicrm/Civi/Payment/PropertyBag.php
index d17c04b84554131bb6edfb046e09f72ace9bb5d2..8878734691078a3cc9ce44189b0c845065dcfc00 100644
--- a/civicrm/Civi/Payment/PropertyBag.php
+++ b/civicrm/Civi/Payment/PropertyBag.php
@@ -72,6 +72,7 @@ class PropertyBag implements \ArrayAccess {
     'transactionID'               => TRUE,
     'transaction_id'              => 'transactionID',
     'trxnResultCode'              => TRUE,
+    'isNotifyProcessorOnCancelRecur' => TRUE,
   ];
 
   /**
@@ -210,6 +211,13 @@ class PropertyBag implements \ArrayAccess {
       // Good, modern name.
       return $prop;
     }
+    // Handling for legacy addition of billing details.
+    if ($newName === NULL && substr($prop, -2) === '-' . \CRM_Core_BAO_LocationType::getBilling()
+      && isset(static::$propMap[substr($prop, 0, -2)])
+    ) {
+      $newName = substr($prop, 0, -2);
+    }
+
     if ($newName === NULL) {
       if ($silent) {
         // Only for use by offsetExists
@@ -380,7 +388,7 @@ class PropertyBag implements \ArrayAccess {
       throw new \InvalidArgumentException("setAmount requires a numeric amount value");
     }
 
-    return $this->set('amount', CRM_Utils_Money::format($value, NULL, NULL, TRUE), $label);
+    return $this->set('amount', $label, \CRM_Utils_Money::format($value, NULL, NULL, TRUE));
   }
 
   /**
@@ -473,6 +481,8 @@ class PropertyBag implements \ArrayAccess {
    *
    * @param string $input
    * @param string $label e.g. 'default'
+   *
+   * @return \Civi\Payment\PropertyBag
    */
   public function setBillingCity($input, $label = 'default') {
     return $this->set('billingCity', $label, (string) $input);
@@ -770,9 +780,12 @@ class PropertyBag implements \ArrayAccess {
    *
    * @param string $label
    *
-   * @return bool|null
+   * @return bool
    */
   public function getIsRecur($label = 'default'):bool {
+    if (!$this->has('isRecur')) {
+      return FALSE;
+    }
     return $this->get('isRecur', $label);
   }
 
@@ -787,6 +800,35 @@ class PropertyBag implements \ArrayAccess {
     return $this->set('isRecur', $label, (bool) $isRecur);
   }
 
+  /**
+   * Set whether the user has selected to notify the processor of a cancellation request.
+   *
+   * When cancelling the user may be presented with an option to notify the processor. The payment
+   * processor can take their response, if present, into account.
+   *
+   * @param bool $value
+   * @param string $label e.g. 'default'
+   *
+   * @return \Civi\Payment\PropertyBag
+   */
+  public function setIsNotifyProcessorOnCancelRecur($value, $label = 'default') {
+    return $this->set('isNotifyProcessorOnCancelRecur', $label, (bool) $value);
+  }
+
+  /**
+   * Get whether the user has selected to notify the processor of a cancellation request.
+   *
+   * When cancelling the user may be presented with an option to notify the processor. The payment
+   * processor can take their response, if present, into account.
+   *
+   * @param string $label e.g. 'default'
+   *
+   * @return \Civi\Payment\PropertyBag
+   */
+  public function getIsNotifyProcessorOnCancelRecur($label = 'default') {
+    return $this->get('isNotifyProcessorOnCancelRecur', $label);
+  }
+
   /**
    * Last name
    *
diff --git a/civicrm/Civi/Test.php b/civicrm/Civi/Test.php
index 50d6ebf31c4a39ee1d97fa1071b29516757eec2d..1d9db12079521969e442e86d704ae96fc8fd90ff 100644
--- a/civicrm/Civi/Test.php
+++ b/civicrm/Civi/Test.php
@@ -100,12 +100,12 @@ class Test {
   /**
    * Create a builder for the headless environment.
    *
-   * @return \Civi\Test\CiviEnvBuilder
-   *
-   * @code
+   * ```
    * \Civi\Test::headless()->apply();
    * \Civi\Test::headless()->sqlFile('ex.sql')->apply();
-   * @endCode
+   * ```
+   *
+   * @return \Civi\Test\CiviEnvBuilder
    */
   public static function headless() {
     $civiRoot = dirname(__DIR__);
@@ -130,12 +130,12 @@ class Test {
   /**
    * Create a builder for end-to-end testing on the live environment.
    *
-   * @return \Civi\Test\CiviEnvBuilder
-   *
-   * @code
+   * ```
    * \Civi\Test::e2e()->apply();
    * \Civi\Test::e2e()->install('foo.bar')->apply();
-   * @endCode
+   * ```
+   *
+   * @return \Civi\Test\CiviEnvBuilder
    */
   public static function e2e() {
     $builder = new \Civi\Test\CiviEnvBuilder('CiviEnvBuilder');
diff --git a/civicrm/Civi/Test/Api3TestTrait.php b/civicrm/Civi/Test/Api3TestTrait.php
index 6d5421053dcb75835c34c503a2007fad3a06a64f..fbe73cd0fe909038df590eceae14fdc313bdae40 100644
--- a/civicrm/Civi/Test/Api3TestTrait.php
+++ b/civicrm/Civi/Test/Api3TestTrait.php
@@ -302,7 +302,7 @@ trait Api3TestTrait {
    * @throws \Exception
    */
   public function runApi4Legacy($v3Entity, $v3Action, $v3Params = []) {
-    $v4Entity = self::convertEntityNameToApi4($v3Entity);
+    $v4Entity = \CRM_Core_DAO_AllCoreTables::convertEntityNameToCamel($v3Entity);
     $v4Action = $v3Action = strtolower($v3Action);
     $v4Params = ['checkPermissions' => isset($v3Params['check_permissions']) ? (bool) $v3Params['check_permissions'] : FALSE];
     $sequential = !empty($v3Params['sequential']);
@@ -641,9 +641,9 @@ trait Api3TestTrait {
 
     // Handle single api call
     list(, $chainEntity, $chainAction) = explode('.', $key);
-    $lcChainEntity = \_civicrm_api_get_entity_name_from_camel($chainEntity);
-    $chainEntity = self::convertEntityNameToApi4($chainEntity);
-    $lcMainEntity = \_civicrm_api_get_entity_name_from_camel($mainEntity);
+    $lcChainEntity = \CRM_Core_DAO_AllCoreTables::convertEntityNameToLower($chainEntity);
+    $chainEntity = \CRM_Core_DAO_AllCoreTables::convertEntityNameToCamel($chainEntity);
+    $lcMainEntity = \CRM_Core_DAO_AllCoreTables::convertEntityNameToLower($mainEntity);
     $params = is_array($params) ? $params : [];
 
     // Api3 expects this to be inherited
@@ -677,19 +677,4 @@ trait Api3TestTrait {
     return $this->runApi4Legacy($chainEntity, $chainAction, $params);
   }
 
-  /**
-   * Fix the naming differences between api3 & api4 entities.
-   *
-   * @param string $legacyName
-   * @return string
-   */
-  public static function convertEntityNameToApi4($legacyName) {
-    $api4Name = \CRM_Utils_String::convertStringToCamel($legacyName);
-    $map = [
-      'Im' => 'IM',
-      'Acl' => 'ACL',
-    ];
-    return $map[$api4Name] ?? $api4Name;
-  }
-
 }
diff --git a/civicrm/Civi/Test/DbTestTrait.php b/civicrm/Civi/Test/DbTestTrait.php
index 74388ada0fb2b10fe76edcd32d661217bc3bc3e0..866984566f7a32cf1c6899c4adb6864c1275a5a6 100644
--- a/civicrm/Civi/Test/DbTestTrait.php
+++ b/civicrm/Civi/Test/DbTestTrait.php
@@ -154,7 +154,7 @@ trait DbTestTrait {
    */
   public function assertDBCompareValues($daoName, $searchParams, $expectedValues) {
     //get the values from db
-    $dbValues = array();
+    $dbValues = [];
     \CRM_Core_DAO::commonRetrieve($daoName, $searchParams, $dbValues);
 
     // compare db values with expected values
diff --git a/civicrm/Civi/Test/GenericAssertionsTrait.php b/civicrm/Civi/Test/GenericAssertionsTrait.php
index 0d53d034ba372f201fc34cf8d270d84c500d5829..8d04b6359b9613474de876c5e7e50a9d368e389f 100644
--- a/civicrm/Civi/Test/GenericAssertionsTrait.php
+++ b/civicrm/Civi/Test/GenericAssertionsTrait.php
@@ -35,8 +35,8 @@ trait GenericAssertionsTrait {
    * @param array $actual
    */
   public function assertTreeEquals($expected, $actual) {
-    $e = array();
-    $a = array();
+    $e = [];
+    $a = [];
     \CRM_Utils_Array::flatten($expected, $e, '', ':::');
     \CRM_Utils_Array::flatten($actual, $a, '', ':::');
     ksort($e);
diff --git a/civicrm/Civi/Test/GuzzleTestTrait.php b/civicrm/Civi/Test/GuzzleTestTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..c372d3eee9e6ac4794b95e276bd6c2736c90310e
--- /dev/null
+++ b/civicrm/Civi/Test/GuzzleTestTrait.php
@@ -0,0 +1,191 @@
+<?php
+
+namespace Civi\Test;
+
+use GuzzleHttp\Handler\MockHandler;
+use GuzzleHttp\HandlerStack;
+use GuzzleHttp\Psr7\Response;
+use GuzzleHttp\Middleware;
+use GuzzleHttp\Client;
+
+/**
+ * Class GuzzleTestTrait
+ *
+ * This trait defines a number of helper functions for testing guzzle.
+ */
+trait GuzzleTestTrait {
+  /**
+   * @var \GuzzleHttp\Client
+   */
+  protected $guzzleClient;
+
+  /**
+   * Array containing guzzle history of requests and responses.
+   *
+   * @var array
+   */
+  protected $container;
+
+  /**
+   * Mockhandler to simulate guzzle requests.
+   *
+   * @var \GuzzleHttp\Handler\MockHandler
+   */
+  protected $mockHandler;
+
+  /**
+   * The url to mock-interact with.
+   *
+   * @var string
+   */
+  protected $baseUri;
+
+  /**
+   * @return \GuzzleHttp\Client
+   */
+  public function getGuzzleClient() {
+    return $this->guzzleClient;
+  }
+
+  /**
+   * @param \GuzzleHttp\Client $guzzleClient
+   */
+  public function setGuzzleClient($guzzleClient) {
+    $this->guzzleClient = $guzzleClient;
+  }
+
+  /**
+   * @return array
+   */
+  public function getContainer() {
+    return $this->container;
+  }
+
+  /**
+   * @param array $container
+   */
+  public function setContainer($container) {
+    $this->container = $container;
+  }
+
+  /**
+   * @return mixed
+   */
+  public function getBaseUri() {
+    return $this->baseUri;
+  }
+
+  /**
+   * @param mixed $baseUri
+   */
+  public function setBaseUri($baseUri) {
+    $this->baseUri = $baseUri;
+  }
+
+  /**
+   * @return \GuzzleHttp\Handler\MockHandler
+   */
+  public function getMockHandler() {
+    return $this->mockHandler;
+  }
+
+  /**
+   * @param \GuzzleHttp\Handler\MockHandler $mockHandler
+   */
+  public function setMockHandler($mockHandler) {
+    $this->mockHandler = $mockHandler;
+  }
+
+  /**
+   * @param $responses
+   */
+  protected function createMockHandler($responses) {
+    $mocks = [];
+    foreach ($responses as $response) {
+      $mocks[] = new Response(200, [], $response);
+    }
+    $this->setMockHandler(new MockHandler($mocks));
+  }
+
+  /**
+   * @param $files
+   */
+  protected function createMockHandlerForFiles($files) {
+    $body = [];
+    foreach ($files as $file) {
+      $body[] = trim(file_get_contents(__DIR__ . $file));
+    }
+    $this->createMockHandler($body);
+  }
+
+  /**
+   * Set up a guzzle client with a history container.
+   *
+   * After you have run the requests you can inspect $this->container
+   * for the outgoing requests and incoming responses.
+   *
+   * If $this->mock is defined then no outgoing http calls will be made
+   * and the responses configured on the handler will be returned instead
+   * of replies from a remote provider.
+   */
+  protected function setUpClientWithHistoryContainer() {
+    $this->container = [];
+    $history = Middleware::history($this->container);
+    $handler = HandlerStack::create($this->getMockHandler());
+    $handler->push($history);
+    $this->guzzleClient = new Client(['base_uri' => $this->baseUri, 'handler' => $handler]);
+  }
+
+  /**
+   * Get the bodies of the requests sent via Guzzle.
+   *
+   * @return array
+   */
+  protected function getRequestBodies() {
+    $requests = [];
+    foreach ($this->getContainer() as $guzzle) {
+      $requests[] = (string) $guzzle['request']->getBody();
+    }
+    return $requests;
+  }
+
+  /**
+   * Get the bodies of the requests sent via Guzzle.
+   *
+   * @return array
+   */
+  protected function getRequestHeaders() {
+    $requests = [];
+    foreach ($this->getContainer() as $guzzle) {
+      $requests[] = $guzzle['request']->getHeaders();
+    }
+    return $requests;
+  }
+
+  /**
+   * Get the bodies of the requests sent via Guzzle.
+   *
+   * @return array
+   */
+  protected function getRequestUrls() {
+    $requests = [];
+    foreach ($this->getContainer() as $guzzle) {
+      $requests[] = (string) $guzzle['request']->getUri();
+    }
+    return $requests;
+  }
+
+  /**
+   * Get the bodies of the responses returned via Guzzle.
+   *
+   * @return array
+   */
+  protected function getResponseBodies() {
+    $responses = [];
+    foreach ($this->getContainer() as $guzzle) {
+      $responses[] = (string) $guzzle['response']->getBody();
+    }
+    return $responses;
+  }
+
+}
diff --git a/civicrm/Civi/Test/HookInterface.php b/civicrm/Civi/Test/HookInterface.php
index 53d5c8cd11226fbeb572ac879dd5a1e69bd872a3..4885cd06772525e0610ac326716ea60dd2c169c4 100644
--- a/civicrm/Civi/Test/HookInterface.php
+++ b/civicrm/Civi/Test/HookInterface.php
@@ -9,13 +9,13 @@ namespace Civi\Test;
  * This interface allows you to subscribe to hooks as part of the test.
  * Simply create an eponymous hook function (e.g. `hook_civicrm_post()`).
  *
- * @code
+ * ```
  * class MyTest extends \PHPUnit_Framework_TestCase implements \Civi\Test\HookInterface {
  *   public function hook_civicrm_post($op, $objectName, $objectId, &$objectRef) {
  *     echo "Running hook_civicrm_post\n";
  *   }
  * }
- * @endCode
+ * ```
  *
  * At time of writing, there are a few limitations in how HookInterface is handled
  * by CiviTestListener:
diff --git a/civicrm/Civi/Test/MailingTestTrait.php b/civicrm/Civi/Test/MailingTestTrait.php
index 6609750fbebcc369169164ce356dd91db03bd999..b6ee14d872f5c802eebd306a28a33ade4671c9d2 100644
--- a/civicrm/Civi/Test/MailingTestTrait.php
+++ b/civicrm/Civi/Test/MailingTestTrait.php
@@ -18,7 +18,7 @@ trait MailingTestTrait {
    *
    * @return int
    */
-  public function createMailing($params = array()) {
+  public function createMailing($params = []) {
     $params = array_merge(array(
       'subject' => 'maild' . rand(),
       'body_text' => 'bdkfhdskfhduew{domain.address}{action.optOutUrl}',
diff --git a/civicrm/Civi/Token/Event/TokenRegisterEvent.php b/civicrm/Civi/Token/Event/TokenRegisterEvent.php
index ee867b949e895594206032a8223b045ddb7a992b..730ddb616c87c662f83d0be2d0b10eb15ec18264 100644
--- a/civicrm/Civi/Token/Event/TokenRegisterEvent.php
+++ b/civicrm/Civi/Token/Event/TokenRegisterEvent.php
@@ -8,7 +8,7 @@ namespace Civi\Token\Event;
  * The TokenRegisterEvent is fired when constructing a list of available
  * tokens. Listeners may register by specifying the entity/field/label for the token.
  *
- * @code
+ * ```
  * $ev->entity('profile')
  *    ->register('viewUrl', ts('Default Profile URL (View Mode)')
  *    ->register('editUrl', ts('Default Profile URL (Edit Mode)');
@@ -17,7 +17,7 @@ namespace Civi\Token\Event;
  *   'field' => 'viewUrl',
  *   'label' => ts('Default Profile URL (View Mode)'),
  * ));
- * @endcode
+ * ```
  *
  * Event name: 'civi.token.list'
  */
diff --git a/civicrm/Civi/Token/Event/TokenValueEvent.php b/civicrm/Civi/Token/Event/TokenValueEvent.php
index c9a251ccc752ae05c24770372f7c5a1e4ce6470e..ed7a3e2504bb55e0ea096b1f517b4d90ed8db481 100644
--- a/civicrm/Civi/Token/Event/TokenValueEvent.php
+++ b/civicrm/Civi/Token/Event/TokenValueEvent.php
@@ -8,7 +8,7 @@ namespace Civi\Token\Event;
  * A TokenValueEvent is fired to convert raw query data into mergeable
  * tokens. For example:
  *
- * @code
+ * ```
  * $event = new TokenValueEvent($myContext, 'text/html', array(
  *   array('contact_id' => 123),
  *   array('contact_id' => 456),
diff --git a/civicrm/Civi/Token/TokenRow.php b/civicrm/Civi/Token/TokenRow.php
index cb38ead05b985a388b8458285c5deb2de0979d87..536b6ab237245674db3f83319a4b2ebd482e7e1d 100644
--- a/civicrm/Civi/Token/TokenRow.php
+++ b/civicrm/Civi/Token/TokenRow.php
@@ -11,25 +11,25 @@ namespace Civi\Token;
  * (1) When setting up a job, you may specify general/baseline info.
  * This is called the "context" data. Here, we create two rows:
  *
- * @code
+ * ```
  * $proc->addRow()->context('contact_id', 123);
  * $proc->addRow()->context('contact_id', 456);
- * @endCode
+ * ```
  *
  * (2) When defining a token (eg `{profile.viewUrl}`), you might read the
  * context-data (`contact_id`) and set the token-data (`profile => viewUrl`):
  *
- * @code
+ * ```
  * foreach ($proc->getRows() as $row) {
  *   $row->tokens('profile', [
  *     'viewUrl' => 'http://example.com/profile?cid=' . urlencode($row->context['contact_id'];
  *   ]);
  * }
- * @endCode
+ * ```
  *
  * The context and tokens can be accessed using either methods or attributes.
  *
- * @code
+ * ```
  * # Setting context data
  * $row->context('contact_id', 123);
  * $row->context(['contact_id' => 123]);
@@ -43,7 +43,7 @@ namespace Civi\Token;
  *
  * # Reading token data
  * echo $row->tokens['profile']['viewUrl'];
- * @endCode
+ * ```
  *
  * Note: The methods encourage a "fluent" style. They were written for PHP 5.3
  * (eg before short-array syntax was supported) and are fairly flexible about
diff --git a/civicrm/ang/api4Explorer/Clause.html b/civicrm/ang/api4Explorer/Clause.html
index 6ccdff2bcd5ffad7ed7591674fb095c8677a8d87..9f9412e9f723ec9b611f7241d43dd6b981888d69 100644
--- a/civicrm/ang/api4Explorer/Clause.html
+++ b/civicrm/ang/api4Explorer/Clause.html
@@ -1,7 +1,7 @@
 <legend>{{ data.label || data.op + ' group' }}<span class="crm-marker" ng-if="data.required"> *</span></legend>
 <div class="btn-group btn-group-xs" ng-if="data.groupParent">
   <button class="btn btn-danger-outline" ng-click="removeGroup()" title="{{:: ts('Remove group') }}">
-    <i class="crm-i fa-trash"></i>
+    <i class="crm-i fa-trash" aria-hidden="true"></i>
   </button>
 </div>
 <div class="api4-clause-group-sortable" ng-model="data.clauses" ui-sortable="{axis: 'y', connectWith: '.api4-clause-group-sortable', containment: '.api4-clause-fieldset', over: onSortOver}" ui-sortable-start="onSort" ui-sortable-stop="onSort">
@@ -10,7 +10,7 @@
       <span class="badge badge-info">
         <span ng-if="!index && !data.groupParent">{{ data.type }}</span>
         <span ng-if="index || data.groupParent">{{ data.op }}</span>
-        <i class="crm-i fa-arrows"></i>
+        <i class="crm-i fa-arrows" aria-hidden="true"></i>
       </span>
     </div>
     <div ng-if="clause[0] !== 'AND' && clause[0] !== 'OR' && clause[0] !== 'NOT'" class="api4-input-group">
diff --git a/civicrm/ang/api4Explorer/Explorer.html b/civicrm/ang/api4Explorer/Explorer.html
index abff9b0162f619fd98f292b6de49d6b85da113ed..d1379c892949d92713735a79550bbcd8c981d24c 100644
--- a/civicrm/ang/api4Explorer/Explorer.html
+++ b/civicrm/ang/api4Explorer/Explorer.html
@@ -8,7 +8,7 @@
   <!--This warning will show if bootstrap is unavailable. Normally it will be hidden by the bootstrap .collapse class.-->
   <div class="messages warning no-popup collapse">
     <p>
-      <i class="crm-i fa-exclamation-triangle"></i>
+      <i class="crm-i fa-exclamation-triangle" aria-hidden="true"></i>
       <strong>{{:: ts('Bootstrap theme not found.') }}</strong>
     </p>
     <p>{{:: ts('This screen may not work correctly without a bootstrap-based theme such as Shoreditch installed.') }}</p>
@@ -48,21 +48,36 @@
             <label class="radio-inline">
               <input type="radio" ng-model="params[name]" ng-value="false" />false
             </label>
-            <a href class="crm-hover-button" title="Clear" ng-click="clearParam(name)" ng-show="params[name] !== null"><i class="crm-i fa-times"></i></a>
+            <a href class="crm-hover-button" title="Clear" ng-click="clearParam(name)" ng-show="params[name] !== null"><i class="crm-i fa-times" aria-hidden="true"></i></a>
           </div>
           <fieldset class="api4-input form-inline" ng-mouseenter="help('select', availableParams.select)" ng-mouseleave="help()" ng-if="availableParams.select && !isSelectRowCount()">
             <legend>select<span class="crm-marker" ng-if="::availableParams.select.required"> *</span></legend>
             <div ng-model="params.select" ui-sortable="{axis: 'y'}">
               <div class="api4-input form-inline" ng-repeat="item in params.select track by $index">
-                <i class="crm-i fa-arrows"></i>
+                <i class="crm-i fa-arrows" aria-hidden="true"></i>
                 <input class="form-control huge" type="text" ng-model="params.select[$index]" />
-                <a href class="crm-hover-button" title="Clear" ng-click="clearParam('select', $index)"><i class="crm-i fa-times"></i></a>
+                <a href class="crm-hover-button" title="Clear" ng-click="clearParam('select', $index)"><i class="crm-i fa-times" aria-hidden="true"></i></a>
               </div>
             </div>
             <div class="api4-input form-inline">
               <input class="collapsible-optgroups form-control huge" ng-model="controls.select" crm-ui-select="{data: fieldsAndJoinsAndFunctionsAndWildcards}" placeholder="Add select" />
             </div>
           </fieldset>
+          <fieldset class="api4-input form-inline" ng-mouseenter="help('join', availableParams.join)" ng-mouseleave="help()" ng-if="::availableParams.join">
+            <legend>join<span class="crm-marker" ng-if="::availableParams.join.required"> *</span></legend>
+            <div ng-model="params.join" ui-sortable="{axis: 'y'}">
+              <div class="api4-input form-inline" ng-repeat="item in params.join track by $index">
+                <i class="crm-i fa-arrows"></i>
+                <input class="form-control twenty" type="text" ng-model="params.join[$index][0]" />
+                <select class="form-control" ng-model="params.join[$index][1]" ng-options="o.k as o.v for o in ::joinTypes" ></select>
+                <input class="form-control twenty" type="text" ng-model="params.join[$index][2]" />
+                <a href class="crm-hover-button" title="Clear" ng-click="clearParam('join', $index)"><i class="crm-i fa-times"></i></a>
+              </div>
+            </div>
+            <div class="api4-input form-inline">
+              <input class="collapsible-optgroups form-control huge" ng-model="controls.join" crm-ui-select="{data: entities}" placeholder="Add join" />
+            </div>
+          </fieldset>
           <div class="api4-input form-inline" ng-mouseenter="help('fields', availableParams.fields)" ng-mouseleave="help()" ng-if="::availableParams.fields">
             <label for="api4-param-fields">fields<span class="crm-marker" ng-if="::availableParams.fields.required"> *</span></label>
             <input class="form-control" ng-list crm-ui-select="::{data: fields, multiple: true}" id="api4-param-fields" ng-model="params.fields" style="width: 85%;"/>
@@ -75,7 +90,7 @@
             <label for="api4-param-{{:: name }}">{{:: name }}<span class="crm-marker" ng-if="::param.required"> *</span></label>
             <input class="form-control" ng-if="::!param.options" type="{{:: param.type[0] === 'int' && param.type.length === 1 ? 'number' : 'text' }}" id="api4-param-{{:: name }}" ng-model="params[name]"/>
             <select class="form-control" ng-if="::param.options" ng-options="o for o in ::param.options" id="api4-param-{{:: name }}" ng-model="params[name]"></select>
-            <a href class="crm-hover-button" title="Clear" ng-click="clearParam(name)" ng-show="!!params[name]"><i class="crm-i fa-times"></i></a>
+            <a href class="crm-hover-button" title="Clear" ng-click="clearParam(name)" ng-show="!!params[name]"><i class="crm-i fa-times" aria-hidden="true"></i></a>
           </div>
           <div class="api4-input" ng-mouseenter="help(name, param)" ng-mouseleave="help()" ng-repeat="(name, param) in ::getGenericParams(['array', 'mixed'])">
             <label for="api4-param-{{:: name }}">{{:: name }}<span class="crm-marker" ng-if="::param.required"> *</span></label>
@@ -98,9 +113,9 @@
             <legend>groupBy<span class="crm-marker" ng-if="::availableParams.groupBy.required"> *</span></legend>
             <div ng-model="params.groupBy" ui-sortable="{axis: 'y'}">
               <div class="api4-input form-inline" ng-repeat="item in params.groupBy track by $index">
-                <i class="crm-i fa-arrows"></i>
+                <i class="crm-i fa-arrows" aria-hidden="true"></i>
                 <input class="form-control huge" type="text" ng-model="params.groupBy[$index]" />
-                <a href class="crm-hover-button" title="Clear" ng-click="clearParam('groupBy', $index)"><i class="crm-i fa-times"></i></a>
+                <a href class="crm-hover-button" title="Clear" ng-click="clearParam('groupBy', $index)"><i class="crm-i fa-times" aria-hidden="true"></i></a>
               </div>
             </div>
             <div class="api4-input form-inline">
@@ -113,17 +128,17 @@
             <legend>orderBy<span class="crm-marker" ng-if="::availableParams.orderBy.required"> *</span></legend>
             <div ng-model="params.orderBy" ui-sortable="{axis: 'y'}">
               <div class="api4-input form-inline" ng-repeat="clause in params.orderBy">
-                <i class="crm-i fa-arrows"></i>
+                <i class="crm-i fa-arrows" aria-hidden="true"></i>
                 <input class="form-control huge" type="text" ng-model="clause[0]" />
                 <select class="form-control" ng-model="clause[1]">
                   <option value="ASC">ASC</option>
                   <option value="DESC">DESC</option>
                 </select>
-                <a href class="crm-hover-button" title="Clear" ng-click="clearParam('orderBy', $index)"><i class="crm-i fa-times"></i></a>
+                <a href class="crm-hover-button" title="Clear" ng-click="clearParam('orderBy', $index)"><i class="crm-i fa-times" aria-hidden="true"></i></a>
               </div>
             </div>
             <div class="api4-input form-inline">
-              <input class="collapsible-optgroups form-control huge" ng-model="controls.orderBy" crm-ui-select="{data: fieldsAndJoinsAndFunctions}" placeholder="Add orderBy" />
+              <input class="collapsible-optgroups form-control huge" ng-model="controls.orderBy" crm-ui-select="{data: fieldsAndJoinsAndFunctionsWithSuffixes}" placeholder="Add orderBy" />
             </div>
           </fieldset>
           <fieldset ng-if="::availableParams.limit && availableParams.offset">
@@ -136,7 +151,7 @@
                 <label for="api4-param-offset">offset<span class="crm-marker" ng-if="::availableParams.offset.required"> *</span></label>
                 <input class="form-control" type="number" min="0" id="api4-param-offset" ng-model="params.offset"/>
               </span>
-              <a href class="crm-hover-button" title="Clear" ng-click="clearParam('limit');clearParam('offset');" ng-show="!!params.limit || !!params.offset"><i class="crm-i fa-times"></i></a>
+              <a href class="crm-hover-button" title="Clear" ng-click="clearParam('limit');clearParam('offset');" ng-show="!!params.limit || !!params.offset"><i class="crm-i fa-times" aria-hidden="true"></i></a>
             </div>
           </fieldset>
           <fieldset ng-if="::availableParams.chain" ng-mouseenter="help('chain', availableParams.chain)" ng-mouseleave="help()">
diff --git a/civicrm/ang/api4Explorer/Explorer.js b/civicrm/ang/api4Explorer/Explorer.js
index 97c6152e2355e3b0e5035d8e7fbe677853b35966..17de0a2958e63a98b4b0e6521012e2017acc6453 100644
--- a/civicrm/ang/api4Explorer/Explorer.js
+++ b/civicrm/ang/api4Explorer/Explorer.js
@@ -28,6 +28,7 @@
     $scope.havingOptions = [];
     $scope.fieldsAndJoins = [];
     $scope.fieldsAndJoinsAndFunctions = [];
+    $scope.fieldsAndJoinsAndFunctionsWithSuffixes = [];
     $scope.fieldsAndJoinsAndFunctionsAndWildcards = [];
     $scope.availableParams = {};
     $scope.params = {};
@@ -52,6 +53,7 @@
     $scope.loading = false;
     $scope.controls = {};
     $scope.langs = ['php', 'js', 'ang', 'cli'];
+    $scope.joinTypes = [{k: false, v: ts('Optional')}, {k: true, v: ts('Required')}];
     $scope.code = {
       php: [
         {name: 'oop', label: ts('OOP Style'), code: ''},
@@ -136,7 +138,7 @@
           }
           fields.push({
             text: link.alias,
-            description: 'Join to ' + link.entity,
+            description: 'Implicit join to ' + link.entity,
             children: wildCard.concat(formatForSelect2(linkFields, [], 'name', ['description'], link.alias + '.'))
           });
         }
@@ -253,7 +255,7 @@
       if (_.isEmpty($scope.availableParams)) {
         return;
       }
-      var specialParams = ['select', 'fields', 'action', 'where', 'values', 'defaults', 'orderBy', 'chain', 'groupBy', 'having'];
+      var specialParams = ['select', 'fields', 'action', 'where', 'values', 'defaults', 'orderBy', 'chain', 'groupBy', 'having', 'join'];
       if ($scope.availableParams.limit && $scope.availableParams.offset) {
         specialParams.push('limit', 'offset');
       }
@@ -356,6 +358,7 @@
       $scope.action = $routeParams.api4action;
       $scope.fieldsAndJoins.length = 0;
       $scope.fieldsAndJoinsAndFunctions.length = 0;
+      $scope.fieldsAndJoinsAndFunctionsWithSuffixes.length = 0;
       $scope.fieldsAndJoinsAndFunctionsAndWildcards.length = 0;
       if (!actions.length) {
         formatForSelect2(getEntity().actions, actions, 'name', ['description', 'params']);
@@ -381,10 +384,12 @@
             });
           }
           $scope.fieldsAndJoinsAndFunctions = addJoins($scope.fields.concat(functions), true);
+          $scope.fieldsAndJoinsAndFunctionsWithSuffixes = addJoins(getFieldList($scope.action, ['name', 'label']).concat(functions), false, ['name', 'label']);
           $scope.fieldsAndJoinsAndFunctionsAndWildcards = addJoins(getFieldList($scope.action, ['name', 'label']).concat(functions), true, ['name', 'label']);
         } else {
           $scope.fieldsAndJoins = getFieldList($scope.action, ['name']);
           $scope.fieldsAndJoinsAndFunctions = $scope.fields;
+          $scope.fieldsAndJoinsAndFunctionsWithSuffixes = getFieldList($scope.action, ['name', 'label']);
           $scope.fieldsAndJoinsAndFunctionsAndWildcards = getFieldList($scope.action, ['name', 'label']);
         }
         $scope.fieldsAndJoinsAndFunctionsAndWildcards.unshift({id: '*', text: '*', 'description': 'All core ' + $scope.entity + ' fields'});
@@ -454,12 +459,15 @@
               });
             });
           }
-          if (typeof objectParams[name] !== 'undefined' || name === 'groupBy' || name === 'select') {
+          if (typeof objectParams[name] !== 'undefined' || name === 'groupBy' || name === 'select' || name === 'join') {
             $scope.$watch('controls.' + name, function(value) {
               var field = value;
               $timeout(function() {
                 if (field) {
-                  if (typeof objectParams[name] === 'undefined') {
+                  if (name === 'join') {
+                    $scope.params[name].push([field + ' AS ' + _.snakeCase(field), false, '[]']);
+                  }
+                  else if (typeof objectParams[name] === 'undefined') {
                     $scope.params[name].push(field);
                   } else {
                     var defaultOp = _.cloneDeep(objectParams[name]);
diff --git a/civicrm/ang/crmCaseType.js b/civicrm/ang/crmCaseType.js
index 0b9743223772e423ac70027ff1ef881cba011174..c8b836b385d464b823c76723144669920dc8dce8 100644
--- a/civicrm/ang/crmCaseType.js
+++ b/civicrm/ang/crmCaseType.js
@@ -138,9 +138,9 @@
       link: function(scope, element, attrs) {
         element.addClass('crm-editable crm-editable-enabled');
         var titleLabel = $(element).find('span');
-        var penIcon = $('<i class="crm-i fa-pencil crm-editable-placeholder"></i>').prependTo(element);
-        var saveButton = $('<button type="button"><i class="crm-i fa-check"></i></button>').appendTo(element);
-        var cancelButton = $('<button type="cancel"><i class="crm-i fa-times"></i></button>').appendTo(element);
+        var penIcon = $('<i class="crm-i fa-pencil crm-editable-placeholder" aria-hidden="true"></i>').prependTo(element);
+        var saveButton = $('<button type="button"><i class="crm-i fa-check" aria-hidden="true"></i></button>').appendTo(element);
+        var cancelButton = $('<button type="cancel"><i class="crm-i fa-times" aria-hidden="true"></i></button>').appendTo(element);
         $('button', element).wrapAll('<div class="crm-editable-form" style="display:none" />');
         var buttons = $('.crm-editable-form', element);
         titleLabel.on('click', startEditMode);
diff --git a/civicrm/ang/crmCaseType/activityTypesTable.html b/civicrm/ang/crmCaseType/activityTypesTable.html
index 3563f0318f24ea742e1176d3ff33ade58a7e0189..46645da174bb9b10b0a5a767611c825d2fe88c6f 100644
--- a/civicrm/ang/crmCaseType/activityTypesTable.html
+++ b/civicrm/ang/crmCaseType/activityTypesTable.html
@@ -15,10 +15,10 @@ Required vars: caseType
   <tbody ui-sortable ng-model="caseType.definition.activityTypes">
   <tr ng-repeat="activityType in caseType.definition.activityTypes">
     <td>
-      <i class="crm-i fa-arrows grip-n-drag"></i>
+      <i class="crm-i fa-arrows grip-n-drag" aria-hidden="true"></i>
     </td>
     <td>
-      <i class="crm-i {{ activityTypes[activityType.name].icon }}"></i>
+      <i class="crm-i {{ activityTypes[activityType.name].icon }}" aria-hidden="true"></i>
       {{ activityTypes[activityType.name].label }}
     </td>
     <td>
diff --git a/civicrm/ang/crmCaseType/list.html b/civicrm/ang/crmCaseType/list.html
index ed3f7bd359c7af1dc4d5e67a68294a3f8db2e868..c9ebc7e60d0ce66d9b01b582435028ddabdfac19 100644
--- a/civicrm/ang/crmCaseType/list.html
+++ b/civicrm/ang/crmCaseType/list.html
@@ -74,6 +74,6 @@ Required vars: caseTypes
   </table>
 
   <div class="crm-submit-buttons">
-    <a ng-href="#/caseType/new" class="button"><span><i class="crm-i fa-plus-circle"></i> {{:: ts('New Case Type') }}</span></a>
+    <a ng-href="#/caseType/new" class="button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {{:: ts('New Case Type') }}</span></a>
   </div>
 </div>
diff --git a/civicrm/ang/crmCaseType/sequenceTable.html b/civicrm/ang/crmCaseType/sequenceTable.html
index ef62fb06b5f6553922702fd645860173310145de..4ee780edd29372018567dd7a3b5b28262a5c6833 100644
--- a/civicrm/ang/crmCaseType/sequenceTable.html
+++ b/civicrm/ang/crmCaseType/sequenceTable.html
@@ -14,10 +14,10 @@ Required vars: activitySet
   <tbody ui-sortable ng-model="activitySet.activityTypes">
   <tr ng-repeat="activity in activitySet.activityTypes">
     <td>
-      <i class="crm-i fa-arrows grip-n-drag"></i>
+      <i class="crm-i fa-arrows grip-n-drag" aria-hidden="true"></i>
     </td>
     <td>
-      <i class="crm-i {{ activityTypes[activity.name].icon }}"></i>
+      <i class="crm-i {{ activityTypes[activity.name].icon }}" aria-hidden="true"></i>
       {{ activity.name }}
     </td>
     <td>
diff --git a/civicrm/ang/crmCaseType/statusTable.html b/civicrm/ang/crmCaseType/statusTable.html
index e7dfd9eb21444cb8ba1daef7f8fbd2ca8997c2b6..085241e22f625b5137ae5473aac1ce8b5a23a0be 100644
--- a/civicrm/ang/crmCaseType/statusTable.html
+++ b/civicrm/ang/crmCaseType/statusTable.html
@@ -28,7 +28,7 @@ Required vars: selectedStatuses
   <tfoot>
   <tr>
     <td></td>
-    <td><a class="crm-hover-button action-item" ng-click="newStatus()" href><i class="crm-i fa-plus"></i> {{:: ts('New Status') }}</a></td>
+    <td><a class="crm-hover-button action-item" ng-click="newStatus()" href><i class="crm-i fa-plus" aria-hidden="true"></i> {{:: ts('New Status') }}</a></td>
     <td></td>
   </tr>
   </tfoot>
diff --git a/civicrm/ang/crmCaseType/timelineTable.html b/civicrm/ang/crmCaseType/timelineTable.html
index 76113a12081474aa55fb131e9e43864f13db5646..e25351d782812116ca64813b214c1e42894909e7 100644
--- a/civicrm/ang/crmCaseType/timelineTable.html
+++ b/civicrm/ang/crmCaseType/timelineTable.html
@@ -19,10 +19,10 @@ Required vars: activitySet
   <tbody ui-sortable ng-model="activitySet.activityTypes">
   <tr ng-repeat="activity in activitySet.activityTypes">
     <td>
-      <i class="crm-i fa-arrows grip-n-drag"></i>
+      <i class="crm-i fa-arrows grip-n-drag" aria-hidden="true"></i>
     </td>
     <td>
-      <i class="crm-i {{activityTypes[activity.name].icon}}"></i>
+      <i class="crm-i {{activityTypes[activity.name].icon}}" aria-hidden="true"></i>
       {{activity.label}}
     </td>
     <td>
diff --git a/civicrm/ang/crmCxn/ManageCtrl.html b/civicrm/ang/crmCxn/ManageCtrl.html
index 78509a06018fc93e480b9388e113de43c7e62f6d..2e84994ec64f038d99f179fadd224890dccd7cda 100644
--- a/civicrm/ang/crmCxn/ManageCtrl.html
+++ b/civicrm/ang/crmCxn/ManageCtrl.html
@@ -115,6 +115,6 @@
 </div>
 
 <div ng-show="appMetas.length === 0" class="messages status no-popup">
-  <i class="crm-i fa-info-circle"></i>
+  <i class="crm-i fa-info-circle" aria-hidden="true"></i>
   {{:: ts('No available applications') }}
 </div>
diff --git a/civicrm/ang/crmMailing.css b/civicrm/ang/crmMailing.css
index b853cb4466e34587e7992997d21a58d9e4e847e4..0d854055ffbe5a777d0ca979dfb6408e3b7a4b82 100644
--- a/civicrm/ang/crmMailing.css
+++ b/civicrm/ang/crmMailing.css
@@ -86,10 +86,6 @@ input[name=preview_test_email]:-ms-input-placeholder {
   font-size: 1.2em;
   float: none;
 }
-.crm-container a.crmMailing-submit-button div {
-  background: url(../i/check.gif) no-repeat left center;
-  padding-left: 20px;
-}
 .crm-container a.crmMailing-submit-button.disabled,
 .crm-container a.crmMailing-submit-button.blocking {
   opacity: .6;
diff --git a/civicrm/ang/crmMailing/BlockPreview.js b/civicrm/ang/crmMailing/BlockPreview.js
index 5e582dc0566a0d5cb06576db3bf397c66cbf1a4f..334e15d4408d2b5e2170bbce95f3b6361dec3303 100644
--- a/civicrm/ang/crmMailing/BlockPreview.js
+++ b/civicrm/ang/crmMailing/BlockPreview.js
@@ -48,7 +48,7 @@
             markup += '</ol>';
             markup = '<h4>' + ts('A test message will be sent to %1 people:', {1: count}) + '</h4>' + markup;
             if (!count) {
-              markup = '<div class="messages status"><i class="crm-i fa-exclamation-triangle"></i> ' +
+              markup = '<div class="messages status"><i class="crm-i fa-exclamation-triangle" aria-hidden="true"></i> ' +
               (data.contact.count ? ts('None of the contacts in this group have an email address.') : ts('Group is empty.')) +
               '</div>';
             }
diff --git a/civicrm/ang/crmMailing/EditMailingCtrl/2step.html b/civicrm/ang/crmMailing/EditMailingCtrl/2step.html
index 5901a0dd799f96559ab53ac5f032b694975f31e5..6e0ee05ef5bd20f46c2c25b12802041c31d417fe 100644
--- a/civicrm/ang/crmMailing/EditMailingCtrl/2step.html
+++ b/civicrm/ang/crmMailing/EditMailingCtrl/2step.html
@@ -42,9 +42,7 @@
           <div crm-mailing-block-schedule crm-mailing="mailing"></div>
         </div>
         <center>
-          <a class="button crmMailing-submit-button crmMailing-btn-primary" ng-click="submit()" ng-class="{blocking: block.check(), disabled: crmMailingSubform.$invalid}">
-            <div>{{:: ts('Submit Mailing') }}</div>
-          </a>
+          <a class="button crmMailing-submit-button crmMailing-btn-primary" crm-icon="fa-paper-plane" ng-click="submit()" ng-class="{blocking: block.check(), disabled: crmMailingSubform.$invalid}">{{:: ts('Submit Mailing') }}</a>
         </center>
       </div>
 
diff --git a/civicrm/ang/crmMailing/EditMailingCtrl/wizard.html b/civicrm/ang/crmMailing/EditMailingCtrl/wizard.html
index 569880f0c087ef3cbcba536d88aaa2aeac36f985..8c1818409987a3c3d83be6fdf91be4e25c78d701 100644
--- a/civicrm/ang/crmMailing/EditMailingCtrl/wizard.html
+++ b/civicrm/ang/crmMailing/EditMailingCtrl/wizard.html
@@ -45,9 +45,7 @@
           <div crm-mailing-block-review crm-mailing="mailing" crm-mailing-attachments="attachments"></div>
         </div>
         <center>
-          <a class="button crmMailing-submit-button crmMailing-btn-primary" ng-click="submit()" ng-class="{blocking: block.check(), disabled: crmMailingSubform.$invalid}">
-            <div>{{:: ts('Submit Mailing') }}</div>
-          </a>
+          <a class="button crmMailing-submit-button crmMailing-btn-primary" crm-icon="fa-paper-plane" ng-click="submit()" ng-class="{blocking: block.check(), disabled: crmMailingSubform.$invalid}">{{:: ts('Submit Mailing') }}</a>
         </center>
       </div>
 
diff --git a/civicrm/ang/crmMailing/EditMailingCtrl/workflow.html b/civicrm/ang/crmMailing/EditMailingCtrl/workflow.html
index e5a3d4eb4529b835634e7b04955eda7ebc864764..bdd3245dde8958b834aef40ae9e4a1cc4686f8d5 100644
--- a/civicrm/ang/crmMailing/EditMailingCtrl/workflow.html
+++ b/civicrm/ang/crmMailing/EditMailingCtrl/workflow.html
@@ -47,14 +47,10 @@
           <div crm-mailing-block-approve crm-mailing="mailing"></div>
         </div>
         <center ng-if="!checkPerm('approve mailings') && !checkPerm('access CiviMail')">
-          <a class="button crmMailing-submit-button crmMailing-btn-primary" ng-click="submit()" ng-class="{blocking: block.check(), disabled: crmMailingSubform.$invalid}">
-            <div>{{:: ts('Submit Mailing') }}</div>
-          </a>
+          <a class="button crmMailing-submit-button crmMailing-btn-primary" crm-icon="fa-check" ng-click="submit()" ng-class="{blocking: block.check(), disabled: crmMailingSubform.$invalid}">{{:: ts('Submit Mailing') }}</a>
         </center>
         <center ng-if="checkPerm('approve mailings') || checkPerm('access CiviMail')">
-          <a class="button crmMailing-submit-button crmMailing-btn-primary" ng-click="approve('Approved')" ng-class="{blocking: block.check(), disabled: crmMailingSubform.$invalid}">
-            <div>{{:: ts('Submit and Approve Mailing') }}</div>
-          </a>
+          <a class="button crmMailing-submit-button crmMailing-btn-primary" crm-icon="fa-paper-plane" ng-click="approve('Approved')" ng-class="{blocking: block.check(), disabled: crmMailingSubform.$invalid}">{{:: ts('Submit and Approve Mailing') }}</a>
         </center>
       </div>
 
diff --git a/civicrm/ang/crmMailing/EmailBodyCtrl/tokenAlert.html b/civicrm/ang/crmMailing/EmailBodyCtrl/tokenAlert.html
index 81e52e173711c9746961ca45b70682ec1d6aac23..46e03827ee4e8213ccae68cf023ac2d5c74c92b1 100644
--- a/civicrm/ang/crmMailing/EmailBodyCtrl/tokenAlert.html
+++ b/civicrm/ang/crmMailing/EmailBodyCtrl/tokenAlert.html
@@ -4,7 +4,7 @@
 </p>
 
 <div ng-show="missing['domain.address'] && insertable">
-  <a ng-click="insertToken('domain.address')" class="button"><span><i class="crm-i fa-plus-circle"></i> {{:: ts('Address') }}</span></a>
+  <a ng-click="insertToken('domain.address')" class="button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {{:: ts('Address') }}</span></a>
 
   <div class="clear"></div>
 </div>
@@ -24,18 +24,18 @@
     <tbody>
     <tr>
       <td>
-        <a ng-click="insertToken('action.unsubscribeUrl')" class="button"><span><i class="crm-i fa-plus-circle"></i> {{:: ts('Unsubscribe') }}</span></a>
+        <a ng-click="insertToken('action.unsubscribeUrl')" class="button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {{:: ts('Unsubscribe') }}</span></a>
       </td>
       <td>
-        <a ng-click="insertToken('action.unsubscribe')" class="button"><span><i class="crm-i fa-plus-circle"></i> {{:: ts('Unsubscribe') }}</span></a>
+        <a ng-click="insertToken('action.unsubscribe')" class="button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {{:: ts('Unsubscribe') }}</span></a>
       </td>
     </tr>
     <tr>
       <td>
-        <a ng-click="insertToken('action.optOutUrl')" class="button"><span><i class="crm-i fa-plus-circle"></i> {{:: ts('Opt-out') }}</span></a>
+        <a ng-click="insertToken('action.optOutUrl')" class="button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {{:: ts('Opt-out') }}</span></a>
       </td>
       <td>
-        <a ng-click="insertToken('action.optOut')" class="button"><span><i class="crm-i fa-plus-circle"></i> {{:: ts('Opt-out') }}</span></a>
+        <a ng-click="insertToken('action.optOut')" class="button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {{:: ts('Opt-out') }}</span></a>
       </td>
     </tr>
     </tbody>
diff --git a/civicrm/ang/crmMailing/Recipients.js b/civicrm/ang/crmMailing/Recipients.js
index 4203d4a3c8f7b553c9e68cfb5c8af13eebadd9b6..0d39cde49e587454c28cb279d1339e163c247830 100644
--- a/civicrm/ang/crmMailing/Recipients.js
+++ b/civicrm/ang/crmMailing/Recipients.js
@@ -99,7 +99,7 @@
           if (option.entity_type != 'civicrm_mailing' && isMandatory(option.entity_id)) {
             spanClass = 'crmMailing-mandatory';
           }
-          return '<i class="crm-i '+icon+'"></i> <span class="' + spanClass + '">' + item.text + '</span>';
+          return '<i class="crm-i '+icon+'" aria-hidden="true"></i> <span class="' + spanClass + '">' + item.text + '</span>';
         }
 
         function validate() {
diff --git a/civicrm/ang/crmMailingAB/EditCtrl/edit.html b/civicrm/ang/crmMailingAB/EditCtrl/edit.html
index de95f0351b219edea69269a667acbaea36cf476e..94e6b2574b0769c377cf5f80da6388e1dace888e 100644
--- a/civicrm/ang/crmMailingAB/EditCtrl/edit.html
+++ b/civicrm/ang/crmMailingAB/EditCtrl/edit.html
@@ -158,9 +158,7 @@
           }"
           crm-abtest="abtest"></div>
         <center>
-          <a class="button crmMailing-submit-button" ng-click="submit()" ng-class="{blocking: block.check(), disabled: crmMailingAB.$invalid}">
-            <div>{{:: ts('Submit Mailing') }}</div>
-          </a>
+          <a class="button crmMailing-submit-button" crm-icon="fa-paper-plane" ng-click="submit()" ng-class="{blocking: block.check(), disabled: crmMailingAB.$invalid}">{{:: ts('Submit Mailing') }}</a>
         </center>
       </div>
       <span crm-ui-wizard-buttons style="float:right;">
diff --git a/civicrm/ang/crmMailingAB/ListCtrl.html b/civicrm/ang/crmMailingAB/ListCtrl.html
index 29fe00ed63085f3011be4569289f926b700c104e..0d5c1499e8b0c5a87a2f14795a0f7bbbd6cb3956 100644
--- a/civicrm/ang/crmMailingAB/ListCtrl.html
+++ b/civicrm/ang/crmMailingAB/ListCtrl.html
@@ -52,12 +52,12 @@ Required vars: mailingABList
 </div>
 
 <div ng-show="mailingABList.length === 0" class="messages status no-popup">
-  <i class="crm-i fa-info-circle"></i>
+  <i class="crm-i fa-info-circle" aria-hidden="true"></i>
   {{:: ts('You have no A/B mailings') }}
 </div>
 
 
 <div class="crm-submit-buttons">
   <br>
-  <a ng-href="#/abtest/new" class="button"><span><i class="crm-i fa-bar-chart"></i> {{:: ts('New A/B Test') }}</span></a>
+  <a ng-href="#/abtest/new" class="button"><span><i class="crm-i fa-flask" aria-hidden="true"></i> {{:: ts('New A/B Test') }}</span></a>
 </div>
diff --git a/civicrm/ang/crmStatusPage/StatusPage.html b/civicrm/ang/crmStatusPage/StatusPage.html
index bf252cbeb1bf9e0cfca82352c580b75104080c81..dfdfbdf24b3d66780f18c2ffbd0aaee8df3e3010 100644
--- a/civicrm/ang/crmStatusPage/StatusPage.html
+++ b/civicrm/ang/crmStatusPage/StatusPage.html
@@ -14,7 +14,7 @@
     >
     <div class="crm-status-item" ng-repeat="status in statuses | filter:{is_visible: tab.is_visible}" >
       <h3 class="crm-severity-{{status.severity}}">
-        <i ng-if="status.icon" class="crm-i {{status.icon}}"></i>
+        <i ng-if="status.icon" class="crm-i {{status.icon}}" aria-hidden="true"></i>
         {{status.title}}
         <div statuspage-popup-menu class="hush-menu css_right"></div>
         <div ng-if="!status.is_visible" class="hidden-until css_right">
diff --git a/civicrm/ang/crmUi.js b/civicrm/ang/crmUi.js
index 0b09c60de711e54d60d6e04d823878b09b16f6ae..7482999091b9081a4681a8403c107f59ee9f3056 100644
--- a/civicrm/ang/crmUi.js
+++ b/civicrm/ang/crmUi.js
@@ -824,9 +824,9 @@
             return steps[selectedIndex] && steps[selectedIndex].isStepValid();
           };
           this.iconFor = function(index) {
-            if (index < this.$index()) return '√';
-            if (index === this.$index()) return '»';
-            return ' ';
+            if (index < this.$index()) return 'crm-i fa-check';
+            if (index === this.$index()) return 'crm-i fa-angle-double-right';
+            return '';
           };
           this.isSelectable = function(step) {
             if (step.selected) return false;
@@ -919,7 +919,7 @@
             return;
           }
           if (attrs.crmIcon.substring(0,3) == 'fa-') {
-            $(element).prepend('<i class="crm-i ' + attrs.crmIcon + '"></i> ');
+            $(element).prepend('<i class="crm-i ' + attrs.crmIcon + '" aria-hidden="true"></i> ');
           }
           else {
             $(element).prepend('<span class="icon ui-icon-' + attrs.crmIcon + '"></span> ');
@@ -1051,7 +1051,7 @@
     // set a custom title (i.e., it has an initial title of "CiviCRM"). See the
     // global variables pageTitle and documentTitle.
     // Example (same title for both): <h1 crm-page-title>{{ts('Hello')}}</h1>
-    // Example (separate document title): <h1 crm-document-title="ts('Hello')" crm-page-title><i class="crm-i fa-flag"></i>{{ts('Hello')}}</h1>
+    // Example (separate document title): <h1 crm-document-title="ts('Hello')" crm-page-title><i class="crm-i fa-flag" aria-hidden="true"></i>{{ts('Hello')}}</h1>
     .directive('crmPageTitle', function($timeout) {
       return {
         scope: {
diff --git a/civicrm/ang/crmUi/tabset.html b/civicrm/ang/crmUi/tabset.html
index 6bb45711f057d0c607907bf685b7546de3b2a61e..9a17d97e1bb7085d5f692d611d81ac4d36dc9057 100644
--- a/civicrm/ang/crmUi/tabset.html
+++ b/civicrm/ang/crmUi/tabset.html
@@ -2,7 +2,7 @@
   <ul>
     <li ng-repeat="tab in tabs" class="ui-corner-all crm-tab-button crm-count-{{tab.count}}">
       <a href="#{{tab.id}}">
-        <i ng-if="tab.crmIcon" class="crm-i {{tab.crmIcon}}"></i>
+        <i ng-if="tab.crmIcon" class="crm-i {{tab.crmIcon}}" aria-hidden="true"></i>
         {{tab.$parent.$eval(tab.crmTitle)}}
         <em ng-if="tab.count">{{tab.count}}</em>
       </a>
diff --git a/civicrm/ang/crmUi/wizard.html b/civicrm/ang/crmUi/wizard.html
index bb55c89b1dcb465aae37e160c415a0b6e541bf63..2539f38336f82e0d765321c37acc2c1914b8b587 100644
--- a/civicrm/ang/crmUi/wizard.html
+++ b/civicrm/ang/crmUi/wizard.html
@@ -1,7 +1,7 @@
 <div class="crm-wizard">
   <ul class="crm-wizard-nav wizard-bar">
     <li ng-repeat="step in steps" ng-class="{'current-step':step.selected}" class="{{crmUiWizardNavClass}}">
-      <span>{{crmUiWizardCtrl.iconFor($index)}}</span>
+      <i class="{{crmUiWizardCtrl.iconFor($index)}}"></i>
       <a href="" ng-click="crmUiWizardCtrl.select(step)" ng-show="crmUiWizardCtrl.isSelectable(step)">{{1 + $index}}. {{step.$parent.$eval(step.crmTitle)}}</a>
       <span ng-show="!crmUiWizardCtrl.isSelectable(step)">{{1 + $index}}. {{step.$parent.$eval(step.crmTitle)}}</span>
       <!-- Don't know a good way to localize text like "1. Title" -->
diff --git a/civicrm/ang/exportui/exportField.html b/civicrm/ang/exportui/exportField.html
index 7ae919749c51be3d559cb25eae4817917bf16a30..25d1bfb84ea1e9206de5a802917b2791adf9115b 100644
--- a/civicrm/ang/exportui/exportField.html
+++ b/civicrm/ang/exportui/exportField.html
@@ -1,5 +1,5 @@
 <div>
-  <span class="crm-export-row-handle crm-i fa-arrows"></span>
+  <span class="crm-export-row-handle crm-i fa-arrows" aria-hidden="true"></span>
   <input class="big" crm-ui-select="{data: getFields, allowClear: true, placeholder: 'clear'}" ng-model="field.select" />
 </div>
 <div ng-if="fields[field.select].relationship_type_id">
diff --git a/civicrm/api/api.php b/civicrm/api/api.php
index 8208af08db30d36f42b8a9e653f983f18bd69e62..be1946db3735796f65dd091d9d3aece9908d98e6 100644
--- a/civicrm/api/api.php
+++ b/civicrm/api/api.php
@@ -281,33 +281,25 @@ function _civicrm_api_replace_variable($value, $parentResult, $separator) {
  *
  * @return string
  *   Entity name in underscore separated format.
+ *
+ * @deprecated
  */
 function _civicrm_api_get_entity_name_from_camel($entity) {
-  if (!$entity || $entity === strtolower($entity)) {
-    return $entity;
-  }
-  elseif ($entity == 'PCP') {
-    return 'pcp';
-  }
-  else {
-    $entity = ltrim(strtolower(str_replace('U_F',
-          'uf',
-          // That's CamelCase, beside an odd UFCamel that is expected as uf_camel
-          preg_replace('/(?=[A-Z])/', '_$0', $entity)
-        )), '_');
+  if (!$entity) {
+    // @todo - this should not be called when empty.
+    return '';
   }
-  return $entity;
+  return CRM_Core_DAO_AllCoreTables::convertEntityNameToLower($entity);
 }
 
 /**
  * Having a DAO object find the entity name.
  *
- * @param object $bao
+ * @param CRM_Core_DAO $bao
  *   DAO being passed in.
  *
  * @return string
  */
 function _civicrm_api_get_entity_name_from_dao($bao) {
-  $daoName = str_replace("BAO", "DAO", get_class($bao));
-  return CRM_Core_DAO_AllCoreTables::getBriefName($daoName);
+  return CRM_Core_DAO_AllCoreTables::getBriefName(get_class($bao));
 }
diff --git a/civicrm/api/class.api.php b/civicrm/api/class.api.php
index 3e93b97a586b61c37b8e4254b5c9270c70fea061..4ba61dd30bde6c5d50e8e25d839a895f07d596fb 100644
--- a/civicrm/api/class.api.php
+++ b/civicrm/api/class.api.php
@@ -4,36 +4,36 @@
  *
  * This class allows to consume the API, either from within a module that knows civicrm already:
  *
- * @code
+ * ```
  *   require_once('api/class.api.php');
  *   $api = new civicrm_api3();
- * @endcode
+ * ```
  *
  * or from any code on the same server as civicrm
  *
- * @code
+ * ```
  *   require_once('/your/civi/folder/api/class.api.php');
  *   // the path to civicrm.settings.php
  *   $api = new civicrm_api3 (array('conf_path'=> '/your/path/to/your/civicrm/or/joomla/site));
- * @endcode
+ * ```
  *
  * or to query a remote server via the rest api
  *
- * @code
+ * ```
  *   $api = new civicrm_api3 (array ('server' => 'http://example.org',
  *                                   'api_key'=>'theusersecretkey',
  *                                   'key'=>'thesitesecretkey'));
- * @endcode
+ * ```
  *
  * No matter how initialised and if civicrm is local or remote, you use the class the same way.
  *
- * @code
+ * ```
  *   $api->{entity}->{action}($params);
- * @endcode
+ * ```
  *
  * So, to get the individual contacts:
  *
- * @code
+ * ```
  *   if ($api->Contact->Get(array('contact_type'=>'Individual','return'=>'sort_name,current_employer')) {
  *     // each key of the result array is an attribute of the api
  *     echo "\n contacts found " . $api->count;
@@ -44,37 +44,37 @@
  *   } else {
  *     echo $api->errorMsg();
  *   }
- * @endcode
+ * ```
  *
  * Or, to create an event:
  *
- * @code
+ * ```
  *   if ($api->Event->Create(array('title'=>'Test','event_type_id' => 1,'is_public' => 1,'start_date' => 19430429))) {
  *     echo "created event id:". $api->id;
  *   } else {
  *     echo $api->errorMsg();
  *   }
- * @endcode
+ * ```
  *
  * To make it easier, the Actions can either take for input an
  * associative array $params, or simply an id. The following two lines
  * are equivalent.
  *
- * @code
+ * ```
  *   $api->Activity->Get (42);
  *   $api->Activity->Get (array('id'=>42));
- * @endcode
+ * ```
  *
  *
  * You can also get the result like civicrm_api does, but as an object
  * instead of an array (eg $entity->attribute instead of
  * $entity['attribute']).
  *
- * @code
+ * ```
  *   $result = $api->result;
  *   // is the json encoded result
  *   echo $api;
- * @endcode
+ * ```
  */
 class civicrm_api3 {
 
diff --git a/civicrm/api/v3/Activity.php b/civicrm/api/v3/Activity.php
index d02aaa2d02ff22d897e8d876989f68c0e2d40ad0..8898fb22155d6391452d7d0407fbcddfe4078809 100644
--- a/civicrm/api/v3/Activity.php
+++ b/civicrm/api/v3/Activity.php
@@ -202,6 +202,8 @@ function _civicrm_api3_activity_create_spec(&$params) {
     'FKApiName' => 'Case',
   ];
 
+  $params['activity_date_time']['api.default'] = 'now';
+
 }
 
 /**
@@ -739,13 +741,6 @@ function _civicrm_api3_activity_check_params(&$params) {
     throw new API_Exception('Invalid Activity Duration (in minutes)');
   }
 
-  //if adding a new activity & date_time not set make it now
-  // this should be managed by the wrapper layer & setting ['api.default'] in speces
-  // needs testing
-  if (empty($params['id']) && empty($params['activity_date_time'])) {
-    $params['activity_date_time'] = CRM_Utils_Date::processDate(date('Y-m-d H:i:s'));
-  }
-
   return NULL;
 }
 
diff --git a/civicrm/api/v3/Attachment.php b/civicrm/api/v3/Attachment.php
index 1e78d6796bbae0faf4e7d805d300ea2cc5d30454..8583f0b8c5acab4aa7679a2d886b12eabf743e8c 100644
--- a/civicrm/api/v3/Attachment.php
+++ b/civicrm/api/v3/Attachment.php
@@ -15,7 +15,7 @@
  * file content.
  * For core fields use "entity_table", for custom fields use "field_name"
  *
- * @code
+ * ```
  * // Create an attachment for a core field
  * $result = civicrm_api3('Attachment', 'create', array(
  *   'entity_table' => 'civicrm_activity',
@@ -26,9 +26,9 @@
  * ));
  * $attachment = $result['values'][$result['id']];
  * echo sprintf("<a href='%s'>View %s</a>", $attachment['url'], $attachment['name']);
- * @endcode
+ * ```
  *
- * @code
+ * ```
  * // Create an attachment for a custom file field
  * $result = civicrm_api3('Attachment', 'create', array(
  *   'field_name' => 'custom_6',
@@ -39,9 +39,9 @@
  * ));
  * $attachment = $result['values'][$result['id']];
  * echo sprintf("<a href='%s'>View %s</a>", $attachment['url'], $attachment['name']);
- * @endcode
+ * ```
  *
- * @code
+ * ```
  * // Move an existing file and save as an attachment
  * $result = civicrm_api3('Attachment', 'create', array(
  *   'entity_table' => 'civicrm_activity',
@@ -54,7 +54,7 @@
  * ));
  * $attachment = $result['values'][$result['id']];
  * echo sprintf("<a href='%s'>View %s</a>", $attachment['url'], $attachment['name']);
- * @endcode
+ * ```
  *
  * Notes:
  *  - File content is not returned by default. One must specify 'return => content'.
diff --git a/civicrm/api/v3/Case.php b/civicrm/api/v3/Case.php
index 1f5b9b6ae46a9bf2cfe290692499795b25332b6c..5249010c45b372c98e6b31117e6af0cadf9cff09 100644
--- a/civicrm/api/v3/Case.php
+++ b/civicrm/api/v3/Case.php
@@ -21,7 +21,7 @@
  *
  * @param array $params
  *
- * @code
+ * ```
  * // REQUIRED for create:
  * 'case_type_id' => int OR
  * 'case_type' => str (provide one or the other)
@@ -38,7 +38,7 @@
  * 'start_date' => str datestamp // defaults to: date('YmdHis')
  * 'duration' => int // in minutes
  * 'details' => str // html format
- * @endcode
+ * ```
  *
  * @throws API_Exception
  * @return array
@@ -556,13 +556,13 @@ function civicrm_api3_case_update($params) {
  *
  * @param array $params
  *
- * @code
+ * ```
  *   //REQUIRED:
  *   'id' => int
  *
  *   //OPTIONAL
  *   'move_to_trash' => bool (defaults to false)
- * @endcode
+ * ```
  *
  * @throws API_Exception
  * @return mixed
diff --git a/civicrm/api/v3/Contribution.php b/civicrm/api/v3/Contribution.php
index 66f6cc0c93f2185124060467f4bc8f9c3432468e..bbe016918144e5c0ad12852764d0970b6c4ff544 100644
--- a/civicrm/api/v3/Contribution.php
+++ b/civicrm/api/v3/Contribution.php
@@ -594,6 +594,14 @@ function civicrm_api3_contribution_repeattransaction($params) {
     throw new API_Exception(
       'A valid original contribution ID is required', 'invalid_data');
   }
+  // We don't support repeattransaction without a related recurring contribution.
+  if (empty($contribution->contribution_recur_id)) {
+    throw new API_Exception(
+      'Repeattransaction API can only be used in the context of contributions that have a contribution_recur_id.',
+      'invalid_data'
+    );
+  }
+
   $original_contribution = clone $contribution;
   $input['payment_processor_id'] = civicrm_api3('contributionRecur', 'getvalue', [
     'return' => 'payment_processor_id',
@@ -709,6 +717,7 @@ function _civicrm_api3_contribution_repeattransaction_spec(&$params) {
       'optionGroupName' => 'contribution_status',
     ],
     'api.required' => TRUE,
+    'api.default' => 'Pending',
   ];
   $params['receive_date'] = [
     'title' => 'Contribution Receive Date',
diff --git a/civicrm/api/v3/CustomField.php b/civicrm/api/v3/CustomField.php
index 020ebe2bbfdae5b7437962b19e90b49645c12bf8..1667721744b744ba6b86faa51e83ff707733553c 100644
--- a/civicrm/api/v3/CustomField.php
+++ b/civicrm/api/v3/CustomField.php
@@ -30,6 +30,12 @@
  */
 function civicrm_api3_custom_field_create($params) {
 
+  // Legacy handling for old way of naming serialized fields
+  if (!empty($params['html_type']) && ($params['html_type'] == 'CheckBox' || strpos($params['html_type'], 'Multi-') === 0)) {
+    $params['serialize'] = 1;
+    $params['html_type'] = str_replace('Multi-', '', $params['html_type']);
+  }
+
   // Array created for passing options in params.
   if (isset($params['option_values']) && is_array($params['option_values'])) {
     $weight = 0;
@@ -116,7 +122,26 @@ function civicrm_api3_custom_field_delete($params) {
  * @return array
  */
 function civicrm_api3_custom_field_get($params) {
-  return _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), $params);
+  if (CRM_Core_BAO_Domain::isDBVersionAtLeast('5.27.alpha1') && ($params['legacy_html_type'] ?? TRUE) && !empty($params['return'])) {
+    if (is_array($params['return'])) {
+      $params['return'][] = 'serialize';
+    }
+    elseif (is_string($params['return'])) {
+      $params['return'] .= ',serialize';
+    }
+  }
+
+  $results = _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), $params);
+
+  if (($params['legacy_html_type'] ?? TRUE) && !empty($results['values']) && is_array($results['values'])) {
+    foreach ($results['values'] as $id => $result) {
+      if (!empty($result['serialize']) && !empty($result['html_type'])) {
+        $results['values'][$id]['html_type'] = str_replace('Select', 'Multi-Select', $result['html_type']);
+      }
+    }
+  }
+
+  return $results;
 }
 
 /**
diff --git a/civicrm/api/v3/CustomValue.php b/civicrm/api/v3/CustomValue.php
index 0711224f0021e52fa9fa0d979a068caf86a01472..34f96a9f2daa350e2c3115255a74d9a0b3ccaf27 100644
--- a/civicrm/api/v3/CustomValue.php
+++ b/civicrm/api/v3/CustomValue.php
@@ -22,7 +22,7 @@
  *   Expected keys are in format custom_fieldID:recordID or custom_groupName:fieldName:recordID.
  *
  * @example:
- * @code
+ * ```
  *   // entity ID. You do not need to specify entity type, we figure it out based on the fields you're using
  *   'entity_id' => 123,
  *   // (omitting :id) inserts or updates a field in a single-valued group
@@ -39,7 +39,7 @@
  *   'custom_some_group:my_field' => 'myinfo',
  *   // updates record ID 8 in my_other_field in multi-valued some_big_group
  *   'custom_some_big_group:my_other_field:8' => 'myinfo',
- * @endcode
+ * ```
  *
  * @throws Exception
  * @return array
diff --git a/civicrm/api/v3/Generic.php b/civicrm/api/v3/Generic.php
index d8f2e92db4adcdedbe0aef19a39de6e211b8d95f..0eef89a7a2f6114876af58579cf4df0607e48c75 100644
--- a/civicrm/api/v3/Generic.php
+++ b/civicrm/api/v3/Generic.php
@@ -406,6 +406,7 @@ function civicrm_api3_generic_replace($apiRequest) {
  *
  * @return array
  *   Array of results
+ * @throws \CiviCRM_API3_Exception
  */
 function civicrm_api3_generic_getoptions($apiRequest) {
   // Resolve aliases.
diff --git a/civicrm/api/v3/Generic/Getlist.php b/civicrm/api/v3/Generic/Getlist.php
index 2fecc84c4fcf6dc820d2135510136bf840acabc7..f63948e27c2ce66b5e673aeb281d549dabbb2185 100644
--- a/civicrm/api/v3/Generic/Getlist.php
+++ b/civicrm/api/v3/Generic/Getlist.php
@@ -22,7 +22,7 @@
  * @throws \CiviCRM_API3_Exception
  */
 function civicrm_api3_generic_getList($apiRequest) {
-  $entity = _civicrm_api_get_entity_name_from_camel($apiRequest['entity']);
+  $entity = CRM_Core_DAO_AllCoreTables::convertEntityNameToLower($apiRequest['entity']);
   $request = $apiRequest['params'];
   $meta = civicrm_api3_generic_getfields(['action' => 'get'] + $apiRequest, FALSE);
 
diff --git a/civicrm/api/v3/GroupContact.php b/civicrm/api/v3/GroupContact.php
index 250438378f774e038337164192bc3a8e3c9cc9ae..ef1121ccb9f96766a9d49bd2e13c00a62767e079 100644
--- a/civicrm/api/v3/GroupContact.php
+++ b/civicrm/api/v3/GroupContact.php
@@ -72,7 +72,7 @@ function _civicrm_api3_group_contact_create_spec(&$params) {
  *
  * This api has a legacy/nonstandard signature.
  * On success, the return array will be structured as follows:
- * @code
+ * ```
  * array(
  *   "is_error" => 0,
  *   "version"  => 3,
@@ -83,16 +83,16 @@ function _civicrm_api3_group_contact_create_spec(&$params) {
  *     "total_count" => integer
  *   )
  * )
- * @endcode
+ * ```
  *
  * On failure, the return array will be structured as follows:
- * @code
+ * ```
  * array(
  *   'is_error' => 1,
  *   'error_message' = string,
  *   'error_data' = mixed or undefined
  * )
- * @endcode
+ * ```
  *
  * @param array $params
  *   Input parameters:
diff --git a/civicrm/api/v3/Mailing.php b/civicrm/api/v3/Mailing.php
index 185b3cd374ad063926d3d56efa66f56d2e1ad448..011a19f149ef88d2570850dbc7f3166d4ec59ce7 100644
--- a/civicrm/api/v3/Mailing.php
+++ b/civicrm/api/v3/Mailing.php
@@ -134,8 +134,8 @@ function _civicrm_api3_mailing_create_spec(&$params) {
 
   $params['forward_replies']['api.default'] = FALSE;
   $params['auto_responder']['api.default'] = FALSE;
-  $params['open_tracking']['api.default'] = TRUE;
-  $params['url_tracking']['api.default'] = TRUE;
+  $params['open_tracking']['api.default'] = Civi::settings()->get('open_tracking_default');
+  $params['url_tracking']['api.default'] = Civi::settings()->get('url_tracking_default');
 
   $params['header_id']['api.default'] = CRM_Mailing_PseudoConstant::defaultComponent('Header', '');
   $params['footer_id']['api.default'] = CRM_Mailing_PseudoConstant::defaultComponent('Footer', '');
diff --git a/civicrm/api/v3/MailingAB.php b/civicrm/api/v3/MailingAB.php
index a8c96ab80a9abb7b09c4aa5f4a8dc3f05de63721..8ae6d7f89b5ab115a8c37ecd0df50a75d6d18203 100644
--- a/civicrm/api/v3/MailingAB.php
+++ b/civicrm/api/v3/MailingAB.php
@@ -23,6 +23,7 @@ function _civicrm_api3_mailing_a_b_create_spec(&$spec) {
   $spec['created_date']['api.default'] = 'now';
   $spec['created_id']['api.required'] = 1;
   $spec['created_id']['api.default'] = 'user_contact_id';
+  $spec['domain_id']['api.default'] = CRM_Core_Config::domainID();
 }
 
 /**
diff --git a/civicrm/api/v3/Payment.php b/civicrm/api/v3/Payment.php
index b601cd684f2acf4f98fa409f197aabdea291d656..bf10d26f1546f9ea5684aede88cae3ca94b23fb1 100644
--- a/civicrm/api/v3/Payment.php
+++ b/civicrm/api/v3/Payment.php
@@ -273,6 +273,22 @@ function _civicrm_api3_payment_create_spec(&$params) {
         'type' => 'Text',
       ],
     ],
+    'order_reference' => [
+      'name' => 'order_reference',
+      'type' => CRM_Utils_Type::T_STRING,
+      'title' => 'Order Reference',
+      'description' => 'Payment Processor external order reference',
+      'maxlength' => 255,
+      'size' => 25,
+      'where' => 'civicrm_financial_trxn.order_reference',
+      'table_name' => 'civicrm_financial_trxn',
+      'entity' => 'FinancialTrxn',
+      'bao' => 'CRM_Financial_DAO_FinancialTrxn',
+      'localizable' => 0,
+      'html' => [
+        'type' => 'Text',
+      ],
+    ],
     'check_number' => [
       'name' => 'check_number',
       'type' => CRM_Utils_Type::T_STRING,
diff --git a/civicrm/api/v3/Setting.php b/civicrm/api/v3/Setting.php
index 2f6e7ee6d834fd504d36a30d1ba1ea5dc2ee52a0..7e9ea8ffa36ea1efb48412c8457396a726ed0c54 100644
--- a/civicrm/api/v3/Setting.php
+++ b/civicrm/api/v3/Setting.php
@@ -248,6 +248,9 @@ function _civicrm_api3_setting_fill_spec(&$params) {
  *
  * @return array
  *   api result array
+ *
+ * @throws \API_Exception
+ * @throws \CiviCRM_API3_Exception
  */
 function civicrm_api3_setting_create($params) {
   $domains = _civicrm_api3_setting_getDomainArray($params);
@@ -379,24 +382,24 @@ function _civicrm_api3_setting_getvalue_spec(&$params) {
  * @param array $params
  *
  * @return array
- * @throws \Exception
+ * @throws API_Exception
  */
 function _civicrm_api3_setting_getDomainArray(&$params) {
   if (empty($params['domain_id']) && isset($params['id'])) {
     $params['domain_id'] = $params['id'];
   }
 
-  if ($params['domain_id'] == 'current_domain') {
+  if ($params['domain_id'] === 'current_domain') {
     $params['domain_id'] = CRM_Core_Config::domainID();
   }
 
-  if ($params['domain_id'] == 'all') {
+  if ($params['domain_id'] === 'all') {
     $domainAPIResult = civicrm_api('domain', 'get', ['version' => 3, 'return' => 'id']);
     if (isset($domainAPIResult['values'])) {
       $params['domain_id'] = array_keys($domainAPIResult['values']);
     }
     else {
-      throw new Exception('All domains not retrieved - problem with Domain Get api call ' . $domainAPIResult['error_message']);
+      throw new API_Exception('All domains not retrieved - problem with Domain Get api call ' . $domainAPIResult['error_message']);
     }
   }
   if (is_array($params['domain_id'])) {
diff --git a/civicrm/api/v3/examples/Setting/GetFields.ex.php b/civicrm/api/v3/examples/Setting/GetFields.ex.php
index 2f479ace759adc232159c283853b09d21324d10d..936e6f6f77c047792792c53feaf0c21a92761e39 100644
--- a/civicrm/api/v3/examples/Setting/GetFields.ex.php
+++ b/civicrm/api/v3/examples/Setting/GetFields.ex.php
@@ -2775,7 +2775,7 @@ function setting_getfields_expectedresult() {
         'html_type' => 'text',
         'default' => 'simple',
         'add' => '4.5',
-        'title' => 'How to handle full-tet queries',
+        'title' => 'How to handle full-text queries',
         'is_domain' => 1,
         'is_contact' => 0,
         'description' => '',
diff --git a/civicrm/api/v3/utils.php b/civicrm/api/v3/utils.php
index a8e8e65d028fc69dd31bee5086366aa83fe5a0b7..ae9eaa0a18c0fe73e87f66ebcc80a7ee13080849 100644
--- a/civicrm/api/v3/utils.php
+++ b/civicrm/api/v3/utils.php
@@ -1237,7 +1237,7 @@ function formatCheckBoxField(&$checkboxFieldValue, $customFieldLabel, $entity) {
  * @return array
  */
 function _civicrm_api3_basic_get($bao_name, $params, $returnAsSuccess = TRUE, $entity = "", $sql = NULL, $uniqueFields = FALSE) {
-  $entity = $entity ?: CRM_Core_DAO_AllCoreTables::getBriefName(str_replace('_BAO_', '_DAO_', $bao_name));
+  $entity = $entity ?: CRM_Core_DAO_AllCoreTables::getBriefName($bao_name);
   $options = _civicrm_api3_get_options_from_params($params);
 
   $query = new \Civi\API\Api3SelectQuery($entity, CRM_Utils_Array::value('check_permissions', $params, FALSE));
@@ -2377,7 +2377,7 @@ function _civicrm_api3_api_resolve_alias($entity, $fieldName, $action = 'create'
   if (strpos($fieldName, 'custom_') === 0 && is_numeric($fieldName[7])) {
     return $fieldName;
   }
-  if ($fieldName == _civicrm_api_get_entity_name_from_camel($entity) . '_id') {
+  if ($fieldName === (CRM_Core_DAO_AllCoreTables::convertEntityNameToLower($entity) . '_id')) {
     return 'id';
   }
   $result = civicrm_api($entity, 'getfields', [
diff --git a/civicrm/civicrm-version.php b/civicrm/civicrm-version.php
index 58fe534bcfa13c24a75153f173efc07ca6c3a367..4e293db9c9165dcc4ff726cb300dbd3875a11a07 100644
--- a/civicrm/civicrm-version.php
+++ b/civicrm/civicrm-version.php
@@ -1,7 +1,7 @@
 <?php
 /** @deprecated */
 function civicrmVersion( ) {
-  return array( 'version'  => '5.26.2',
+  return array( 'version'  => '5.27.0',
                 'cms'      => 'Wordpress',
                 'revision' => '' );
 }
diff --git a/civicrm/composer.json b/civicrm/composer.json
index 6e414e4c6650460f441f4b5f84a5354cd0dcae2f..f7226e719a3e087bc27ea0e2872b7ae281e78c98 100644
--- a/civicrm/composer.json
+++ b/civicrm/composer.json
@@ -46,14 +46,14 @@
     "cache/integration-tests": "~0.16.0",
     "dompdf/dompdf" : "0.8.*",
     "electrolinux/phpquery": "^0.9.6",
-    "symfony/config": "^2.8.50 || ~3.0",
+    "symfony/config": "~3.0 || ~4.4",
     "symfony/polyfill-iconv": "~1.0",
-    "symfony/dependency-injection": "^2.8.50 || ~3.0",
-    "symfony/event-dispatcher": "^2.8.50 || ~3.0",
-    "symfony/filesystem": "^2.8.50 || ~3.0",
-    "symfony/process": "^2.8.50 || ~3.0",
+    "symfony/dependency-injection": "~3.0 || ~4.4",
+    "symfony/event-dispatcher": "~3.0 || ~4.4",
+    "symfony/filesystem": "~3.0 || ~4.4",
+    "symfony/process": "~3.0 || ~4.4",
     "psr/log": "~1.0",
-    "symfony/finder": "^2.8.50 || ~3.0",
+    "symfony/finder": "~3.0 || ~4.4",
     "tecnickcom/tcpdf" : "6.2.*",
     "totten/ca-config": "~17.05",
     "zetacomponents/base": "1.9.*",
@@ -69,7 +69,7 @@
     "guzzlehttp/guzzle": "^6.3",
     "psr/simple-cache": "~1.0.1",
     "cweagans/composer-patches": "~1.0",
-    "pear/log": "1.13.1",
+    "pear/log": "1.13.2",
     "adrienrn/php-mimetyper": "0.2.2",
     "civicrm/composer-downloads-plugin": "^2.0",
     "league/csv": "^9.2",
diff --git a/civicrm/composer.lock b/civicrm/composer.lock
index 42f872a30d126683c19344218302aae70a10bc3a..d2b5f94261dd7e5ffae6954fa036f55461233769 100644
--- a/civicrm/composer.lock
+++ b/civicrm/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "00d8b8be8e838f8ff098162f88af562c",
+    "content-hash": "f547e4d1ac65fa8044d302f46a7e7267",
     "packages": [
         {
             "name": "adrienrn/php-mimetyper",
@@ -890,20 +890,20 @@
         },
         {
             "name": "pear/log",
-            "version": "1.13.1",
+            "version": "1.13.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/pear/Log.git",
-                "reference": "c4be9ded2353c7c231d4c35cc3da75b209453803"
+                "reference": "d8cde3dba893a36ec561bf6188fdc39f4221c4d3"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/pear/Log/zipball/c4be9ded2353c7c231d4c35cc3da75b209453803",
-                "reference": "c4be9ded2353c7c231d4c35cc3da75b209453803",
+                "url": "https://api.github.com/repos/pear/Log/zipball/d8cde3dba893a36ec561bf6188fdc39f4221c4d3",
+                "reference": "d8cde3dba893a36ec561bf6188fdc39f4221c4d3",
                 "shasum": ""
             },
             "require": {
-                "pear/pear_exception": "1.0.0",
+                "pear/pear_exception": "1.0.1",
                 "php": ">5.2"
             },
             "require-dev": {
@@ -939,7 +939,7 @@
                 "log",
                 "logging"
             ],
-            "time": "2016-04-16T00:49:33+00:00"
+            "time": "2020-06-02T00:04:03+00:00"
         },
         {
             "name": "pear/mail",
@@ -1168,16 +1168,16 @@
         },
         {
             "name": "pear/pear_exception",
-            "version": "v1.0.0",
+            "version": "v1.0.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/pear/PEAR_Exception.git",
-                "reference": "8c18719fdae000b690e3912be401c76e406dd13b"
+                "reference": "dbb42a5a0e45f3adcf99babfb2a1ba77b8ac36a7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/pear/PEAR_Exception/zipball/8c18719fdae000b690e3912be401c76e406dd13b",
-                "reference": "8c18719fdae000b690e3912be401c76e406dd13b",
+                "url": "https://api.github.com/repos/pear/PEAR_Exception/zipball/dbb42a5a0e45f3adcf99babfb2a1ba77b8ac36a7",
+                "reference": "dbb42a5a0e45f3adcf99babfb2a1ba77b8ac36a7",
                 "shasum": ""
             },
             "require": {
@@ -1193,9 +1193,9 @@
                 }
             },
             "autoload": {
-                "psr-0": {
-                    "PEAR": ""
-                }
+                "classmap": [
+                    "PEAR/"
+                ]
             },
             "notification-url": "https://packagist.org/downloads/",
             "include-path": [
@@ -1219,7 +1219,7 @@
             "keywords": [
                 "exception"
             ],
-            "time": "2015-02-10T20:07:52+00:00"
+            "time": "2019-12-10T10:24:42+00:00"
         },
         {
             "name": "pear/validate_finance_creditcard",
@@ -1659,6 +1659,55 @@
             ],
             "time": "2016-08-06T20:24:11+00:00"
         },
+        {
+            "name": "psr/container",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/container.git",
+                "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+                "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Container\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common Container Interface (PHP FIG PSR-11)",
+            "homepage": "https://github.com/php-fig/container",
+            "keywords": [
+                "PSR-11",
+                "container",
+                "container-interface",
+                "container-interop",
+                "psr"
+            ],
+            "time": "2017-02-14T16:28:37+00:00"
+        },
         {
             "name": "psr/http-message",
             "version": "1.0.1",
@@ -1851,25 +1900,32 @@
         },
         {
             "name": "symfony/config",
-            "version": "v2.8.50",
+            "version": "v3.4.40",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/config.git",
-                "reference": "7dd5f5040dc04c118d057fb5886563963eb70011"
+                "reference": "3634991bea549e73c45a964c38f30ceeae6ed877"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/config/zipball/7dd5f5040dc04c118d057fb5886563963eb70011",
-                "reference": "7dd5f5040dc04c118d057fb5886563963eb70011",
+                "url": "https://api.github.com/repos/symfony/config/zipball/3634991bea549e73c45a964c38f30ceeae6ed877",
+                "reference": "3634991bea549e73c45a964c38f30ceeae6ed877",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.9",
-                "symfony/filesystem": "~2.3|~3.0.0",
+                "php": "^5.5.9|>=7.0.8",
+                "symfony/filesystem": "~2.8|~3.0|~4.0",
                 "symfony/polyfill-ctype": "~1.8"
             },
+            "conflict": {
+                "symfony/dependency-injection": "<3.3",
+                "symfony/finder": "<3.3"
+            },
             "require-dev": {
-                "symfony/yaml": "~2.7|~3.0.0"
+                "symfony/dependency-injection": "~3.3|~4.0",
+                "symfony/event-dispatcher": "~3.3|~4.0",
+                "symfony/finder": "~3.3|~4.0",
+                "symfony/yaml": "~3.0|~4.0"
             },
             "suggest": {
                 "symfony/yaml": "To use the yaml reference dumper"
@@ -1877,7 +1933,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.8-dev"
+                    "dev-master": "3.4-dev"
                 }
             },
             "autoload": {
@@ -1904,43 +1960,51 @@
             ],
             "description": "Symfony Config Component",
             "homepage": "https://symfony.com",
-            "time": "2018-11-26T09:38:12+00:00"
+            "time": "2020-04-12T14:33:46+00:00"
         },
         {
             "name": "symfony/dependency-injection",
-            "version": "v2.8.50",
+            "version": "v3.4.40",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/dependency-injection.git",
-                "reference": "c306198fee8f872a8f5f031e6e4f6f83086992d8"
+                "reference": "d10ff5503b0b27711087eef4ac7835a752fe42fd"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/c306198fee8f872a8f5f031e6e4f6f83086992d8",
-                "reference": "c306198fee8f872a8f5f031e6e4f6f83086992d8",
+                "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/d10ff5503b0b27711087eef4ac7835a752fe42fd",
+                "reference": "d10ff5503b0b27711087eef4ac7835a752fe42fd",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.9"
+                "php": "^5.5.9|>=7.0.8",
+                "psr/container": "^1.0"
             },
             "conflict": {
-                "symfony/expression-language": "<2.6"
+                "symfony/config": "<3.3.7",
+                "symfony/finder": "<3.3",
+                "symfony/proxy-manager-bridge": "<3.4",
+                "symfony/yaml": "<3.4"
+            },
+            "provide": {
+                "psr/container-implementation": "1.0"
             },
             "require-dev": {
-                "symfony/config": "~2.2|~3.0.0",
-                "symfony/expression-language": "~2.6|~3.0.0",
-                "symfony/yaml": "~2.3.42|~2.7.14|~2.8.7|~3.0.7"
+                "symfony/config": "~3.3|~4.0",
+                "symfony/expression-language": "~2.8|~3.0|~4.0",
+                "symfony/yaml": "~3.4|~4.0"
             },
             "suggest": {
                 "symfony/config": "",
                 "symfony/expression-language": "For using expressions in service container configuration",
+                "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required",
                 "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them",
                 "symfony/yaml": ""
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.8-dev"
+                    "dev-master": "3.4-dev"
                 }
             },
             "autoload": {
@@ -1967,31 +2031,34 @@
             ],
             "description": "Symfony DependencyInjection Component",
             "homepage": "https://symfony.com",
-            "time": "2019-04-16T11:33:46+00:00"
+            "time": "2020-04-13T09:33:40+00:00"
         },
         {
             "name": "symfony/event-dispatcher",
-            "version": "v2.8.50",
+            "version": "v3.4.40",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/event-dispatcher.git",
-                "reference": "a77e974a5fecb4398833b0709210e3d5e334ffb0"
+                "reference": "9d4e22943b73acc1ba50595b7de1a01fe9dbad48"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a77e974a5fecb4398833b0709210e3d5e334ffb0",
-                "reference": "a77e974a5fecb4398833b0709210e3d5e334ffb0",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9d4e22943b73acc1ba50595b7de1a01fe9dbad48",
+                "reference": "9d4e22943b73acc1ba50595b7de1a01fe9dbad48",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.9"
+                "php": "^5.5.9|>=7.0.8"
+            },
+            "conflict": {
+                "symfony/dependency-injection": "<3.3"
             },
             "require-dev": {
                 "psr/log": "~1.0",
-                "symfony/config": "^2.0.5|~3.0.0",
-                "symfony/dependency-injection": "~2.6|~3.0.0",
-                "symfony/expression-language": "~2.6|~3.0.0",
-                "symfony/stopwatch": "~2.3|~3.0.0"
+                "symfony/config": "~2.8|~3.0|~4.0",
+                "symfony/dependency-injection": "~3.3|~4.0",
+                "symfony/expression-language": "~2.8|~3.0|~4.0",
+                "symfony/stopwatch": "~2.8|~3.0|~4.0"
             },
             "suggest": {
                 "symfony/dependency-injection": "",
@@ -2000,7 +2067,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.8-dev"
+                    "dev-master": "3.4-dev"
                 }
             },
             "autoload": {
@@ -2027,30 +2094,30 @@
             ],
             "description": "Symfony EventDispatcher Component",
             "homepage": "https://symfony.com",
-            "time": "2018-11-21T14:20:20+00:00"
+            "time": "2020-03-15T09:38:08+00:00"
         },
         {
             "name": "symfony/filesystem",
-            "version": "v2.8.50",
+            "version": "v3.4.40",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/filesystem.git",
-                "reference": "7ae46872dad09dffb7fe1e93a0937097339d0080"
+                "reference": "78a93e5606a19d0fb490afc3c4a9b7ecd86e1515"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/filesystem/zipball/7ae46872dad09dffb7fe1e93a0937097339d0080",
-                "reference": "7ae46872dad09dffb7fe1e93a0937097339d0080",
+                "url": "https://api.github.com/repos/symfony/filesystem/zipball/78a93e5606a19d0fb490afc3c4a9b7ecd86e1515",
+                "reference": "78a93e5606a19d0fb490afc3c4a9b7ecd86e1515",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.9",
+                "php": "^5.5.9|>=7.0.8",
                 "symfony/polyfill-ctype": "~1.8"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.8-dev"
+                    "dev-master": "3.4-dev"
                 }
             },
             "autoload": {
@@ -2077,29 +2144,29 @@
             ],
             "description": "Symfony Filesystem Component",
             "homepage": "https://symfony.com",
-            "time": "2018-11-11T11:18:13+00:00"
+            "time": "2020-04-12T16:54:01+00:00"
         },
         {
             "name": "symfony/finder",
-            "version": "v2.8.50",
+            "version": "v3.4.40",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/finder.git",
-                "reference": "1444eac52273e345d9b95129bf914639305a9ba4"
+                "reference": "5ec813ccafa8164ef21757e8c725d3a57da59200"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/finder/zipball/1444eac52273e345d9b95129bf914639305a9ba4",
-                "reference": "1444eac52273e345d9b95129bf914639305a9ba4",
+                "url": "https://api.github.com/repos/symfony/finder/zipball/5ec813ccafa8164ef21757e8c725d3a57da59200",
+                "reference": "5ec813ccafa8164ef21757e8c725d3a57da59200",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.9"
+                "php": "^5.5.9|>=7.0.8"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.8-dev"
+                    "dev-master": "3.4-dev"
                 }
             },
             "autoload": {
@@ -2126,20 +2193,20 @@
             ],
             "description": "Symfony Finder Component",
             "homepage": "https://symfony.com",
-            "time": "2018-11-11T11:18:13+00:00"
+            "time": "2020-02-14T07:34:21+00:00"
         },
         {
             "name": "symfony/polyfill-ctype",
-            "version": "v1.12.0",
+            "version": "v1.17.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-ctype.git",
-                "reference": "550ebaac289296ce228a706d0867afc34687e3f4"
+                "reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4",
-                "reference": "550ebaac289296ce228a706d0867afc34687e3f4",
+                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e94c8b1bbe2bc77507a1056cdb06451c75b427f9",
+                "reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9",
                 "shasum": ""
             },
             "require": {
@@ -2151,7 +2218,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.12-dev"
+                    "dev-master": "1.17-dev"
                 }
             },
             "autoload": {
@@ -2184,20 +2251,20 @@
                 "polyfill",
                 "portable"
             ],
-            "time": "2019-08-06T08:03:45+00:00"
+            "time": "2020-05-12T16:14:59+00:00"
         },
         {
             "name": "symfony/polyfill-iconv",
-            "version": "v1.12.0",
+            "version": "v1.17.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-iconv.git",
-                "reference": "685968b11e61a347c18bf25db32effa478be610f"
+                "reference": "c4de7601eefbf25f9d47190abe07f79fe0a27424"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/685968b11e61a347c18bf25db32effa478be610f",
-                "reference": "685968b11e61a347c18bf25db32effa478be610f",
+                "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/c4de7601eefbf25f9d47190abe07f79fe0a27424",
+                "reference": "c4de7601eefbf25f9d47190abe07f79fe0a27424",
                 "shasum": ""
             },
             "require": {
@@ -2209,7 +2276,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.12-dev"
+                    "dev-master": "1.17-dev"
                 }
             },
             "autoload": {
@@ -2243,29 +2310,29 @@
                 "portable",
                 "shim"
             ],
-            "time": "2019-08-06T08:03:45+00:00"
+            "time": "2020-05-12T16:47:27+00:00"
         },
         {
             "name": "symfony/process",
-            "version": "v2.8.50",
+            "version": "v3.4.40",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/process.git",
-                "reference": "c3591a09c78639822b0b290d44edb69bf9f05dc8"
+                "reference": "f5104c9dcbc2cfad45d01d5150c1da9836967271"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/process/zipball/c3591a09c78639822b0b290d44edb69bf9f05dc8",
-                "reference": "c3591a09c78639822b0b290d44edb69bf9f05dc8",
+                "url": "https://api.github.com/repos/symfony/process/zipball/f5104c9dcbc2cfad45d01d5150c1da9836967271",
+                "reference": "f5104c9dcbc2cfad45d01d5150c1da9836967271",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.9"
+                "php": "^5.5.9|>=7.0.8"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.8-dev"
+                    "dev-master": "3.4-dev"
                 }
             },
             "autoload": {
@@ -2292,7 +2359,7 @@
             ],
             "description": "Symfony Process Component",
             "homepage": "https://symfony.com",
-            "time": "2018-11-11T11:18:13+00:00"
+            "time": "2020-04-12T14:33:46+00:00"
         },
         {
             "name": "tecnickcom/tcpdf",
@@ -2431,9 +2498,7 @@
             "version": "3.0.0+php53",
             "dist": {
                 "type": "zip",
-                "url": "https://github.com/tplaner/When/archive/c1ec099f421bff354cc5c929f83b94031423fc80.zip",
-                "reference": null,
-                "shasum": null
+                "url": "https://github.com/tplaner/When/archive/c1ec099f421bff354cc5c929f83b94031423fc80.zip"
             },
             "require": {
                 "php": ">=5.3.0"
@@ -2771,5 +2836,6 @@
     "platform-dev": [],
     "platform-overrides": {
         "php": "7.1"
-    }
+    },
+    "plugin-api-version": "1.1.0"
 }
diff --git a/civicrm/css/admin.css b/civicrm/css/admin.css
index faa614cd4f2c5d39f0d67ea8f1a8f9b0ed12bd74..41d332fd41ec8c266f2be4fa08b06495c432324a 100644
--- a/civicrm/css/admin.css
+++ b/civicrm/css/admin.css
@@ -43,3 +43,19 @@
   border: 1px solid #000;
   text-align: center;
 }
+
+@media screen and (min-width: 480px) {
+  #crm-container .admin-section-items {
+    column-count: 2;
+    column-gap: 1em;
+  }
+  #crm-container .admin-section-items dl {
+    -webkit-column-break-inside: avoid;
+    page-break-inside: avoid;
+    break-inside: avoid;
+    margin: 0 1em;
+  }
+  #crm-container .admin-section-items dt {
+    font-weight: bold;
+  }
+}
diff --git a/civicrm/css/civicrm.css b/civicrm/css/civicrm.css
index 06aa01c01d7f80986ca582e82a5484f2ae16e0a9..d260ee66abff2dc6b3800ea776856d237d6089c8 100644
--- a/civicrm/css/civicrm.css
+++ b/civicrm/css/civicrm.css
@@ -5,6 +5,20 @@
  * Other civi blocks outside the main container also have the class crm-container (but not the id)
  * All styles should start with .crm-container unless they are specific to the main div only
  */
+
+/* Use this class to hide text that should only be there for screen readers */
+.sr-only {
+   border: 0;
+   clip: rect(1px, 1px, 1px, 1px);
+   clip-path: inset(50%);
+   height: 1px;
+   width: 1px;
+   margin: -1px;
+   overflow: hidden;
+   padding: 0;
+   position: absolute;
+}
+
 .crm-container input {
   box-sizing: content-box;
 }
@@ -1535,18 +1549,12 @@ input.crm-form-entityref {
   cursor: pointer;
   position: relative;
   white-space: nowrap;
-  padding-right: 15px !important;
   display: inline;
 }
 .crm-container .btn-slide:after {
-  content: "";
-  display: block;
-  height: 15px;
-  position: absolute;
-  right: 2px;
-  top: 3px;
-  width: 15px;
-  background: url("../i/TreePlus.gif") no-repeat right 1px;
+  font-family: "FontAwesome";
+  content: "\f0da";
+  padding-left: .5ex;
 }
 
 .crm-container .btn-slide-active .panel {
@@ -1687,6 +1695,10 @@ input.crm-form-entityref {
 .crm-container a.crm-event-feed-link {
   margin: 0 1ex;
   color: #52534D;
+  display: inline-block;
+  font-size: 10px;
+  padding: 2px 2px 0;
+  vertical-align: bottom;
 }
 
 .crm-container a.crm-event-feed-link:hover {
@@ -2218,73 +2230,110 @@ div.crm-master-accordion-header a.helpicon {
 
 /* accordion styles */
 
-.crm-container .crm-accordion-header {
-  background-image: url("../i/TreeMinusWhite.gif");
-  background-repeat: no-repeat;
-  background-position: 2px center;
+.crm-container .crm-accordion-header,
+.crm-container .crm-collapsible .collapsible-title,
+.crm-container span.collapsed,
+.crm-container a.collapsed,
+.crm-container .crm-expand-row {
   cursor: pointer;
+}
+
+.crm-container .crm-accordion-wrapper {
+  margin-bottom: 4px;
+}
+
+/* Specific types of headers */
+
+#crm-container .widget-content .crm-accordion-header {
+  background-color: #EFEFE5;
+  color: #080808;
+}
+
+.crm-container a.crm-expand-row:before,
+.crm-container a.crm-expand-row:link::before,
+.crm-container a.crm-expand-row:visited::before {
+  color: #3E3E3E;
+}
+
+.crm-container .crm-accordion-header {
   color: #F5F6F1;
   font-weight: normal;
-  padding: 4px 8px 4px 20px;
+  padding: 4px 8px;
   background-color: #5D677B;
+  border-radius: 4px 4px 0 0;
 }
 
-.crm-container .crm-accordion-header:hover {
-  background-color: #32414f;
+.crm-container .collapsed .crm-accordion-header {
+  border-radius: 4px;
 }
 
-.crm-container .collapsed .crm-accordion-header {
-  background-image: url("../i/TreePlusWhite.gif");
+.crm-container .crm-accordion-header.active {
+  font-weight: bold;
+  background-color: #3E3E3E;
 }
 
-.crm-container .collapsed .crm-accordion-body,
-.crm-container .crm-collapsible.collapsed .collapsible-title + * {
-  display: none;
+.crm-container .crm-accordion-header:hover {
+  background-color: #2F2F2E;
 }
 
-.crm-container .crm-expand-row {
-  min-width: 16px;
-  min-height: 16px;
-  display: inline-block;
+#crm-container .widget-content .crm-accordion-header:hover {
+  background-color: #e8e8de;
 }
 
-.crm-container .crm-accordion-inner .crm-accordion-header,
-.crm-container .crm-accordion-wrapper .crm-master-accordion-header,
-.crm-container .crm-collapsible .collapsible-title {
-  background-image: url("../i/TreeMinus.gif");
+.crm-container .crm-accordion-wrapper .crm-master-accordion-header {
   background-color: transparent;
   color: #3E3E3E;
 }
 
-.crm-container .crm-accordion-inner.collapsed .crm-accordion-header,
-.crm-container .crm-accordion-wrapper.collapsed .crm-master-accordion-header,
-.crm-container .crm-collapsible.collapsed .collapsible-title {
-  background-image: url("../i/TreePlus.gif");
-}
-
 .crm-container .crm-accordion-wrapper .crm-master-accordion-header {
-  background-color: transparent;
   font-size: 16px;
-  color: #3e3e3e;
-  margin-bottom: 0;
 }
 
-.crm-container .crm-accordion-inner .crm-accordion-header {
-  font-size: 13px;
+.crm-container .crm-master-accordion-header.crm-accordion-header:hover,
+.crm-container .crm-collapsible .collapsible-title:hover {
+  color: #121A2D;
 }
 
-.crm-container .crm-accordion-wrapper {
-  margin-bottom: 4px;
+.crm-container .collapsed .crm-accordion-body,
+.crm-container .crm-collapsible.collapsed .collapsible-title + * {
+  display: none;
 }
 
-.crm-container .crm-accordion-header {
-  border-radius: 4px 4px 0 0;
+/* Collapse icon */
+
+/* General icon settings for all collapsible things */
+.crm-container .crm-accordion-header:before,
+.crm-container .crm-collapsible .collapsible-title:before,
+.crm-container span.collapsed:before,
+.crm-container a.collapsed:before,
+.crm-container .crm-expand-row:before {
+  font-family: "FontAwesome";
+  display: inline-block;
+  width: 1em;
+  content: "\f0da";
+  font-size: 13px;
 }
 
-.crm-container .collapsed .crm-accordion-header {
-  border-radius: 4px;
+/* Collapsed icon */
+.crm-container .collapsed .crm-accordion-header:before,
+.crm-container .crm-collapsible.collapsed .collapsible-title:before,
+.crm-container span.collapsed:before,
+.crm-container a.collapsed:before,
+.crm-container .crm-expand-row:before {
+  content: "\f0da";
+}
+
+/* Expanded icon */
+.crm-container .crm-accordion-header:before,
+.crm-container .crm-collapsible .collapsible-title:before,
+.crm-container span.expanded:before,
+.crm-container a.expanded:before,
+.crm-container .crm-expand-row.expanded:before {
+  content: "\f0d7";
 }
 
+/* Accordion bodies */
+
 .crm-container .crm-accordion-body {
   border-radius: 0 0 4px 4px;
   border: 1px solid #70716B;
@@ -2292,12 +2341,8 @@ div.crm-master-accordion-header a.helpicon {
   padding: 4px 0;
 }
 
-.crm-container .crm-collapsible .collapsible-title {
-  padding-left: 19px;
-  text-decoration: none;
-  background-repeat: no-repeat;
-  background-position: 0 center;
-  cursor: pointer;
+#crm-container .widget-content .crm-accordion-body {
+  border-color: #e8e8de;
 }
 
 .crm-container .crm-master-accordion-header+.crm-accordion-body {
@@ -2305,19 +2350,10 @@ div.crm-master-accordion-header a.helpicon {
   padding: 0;
 }
 
-.crm-container .crm-accordion-header.active {
-  font-weight: bold;
-  background-color: #41477E;
-}
-
-.crm-container .crm-accordion-header.active:hover {
-  background-color: #2E3471;
-}
-
-.crm-container .crm-master-accordion-header.crm-accordion-header:hover,
-.crm-container .crm-collapsible .collapsible-title:hover {
-  background-color: transparent;
-  color: #0200A0;
+#crm-container .widget-content .crm-accordion-body,
+.crm-container .crm-accordion-body.padded {
+  padding-left: .5em;
+  padding-right: .5em;
 }
 
 .crm-container .crm-child-row > td {
@@ -2456,9 +2492,8 @@ div.crm-master-accordion-header a.helpicon {
 
 /* privacy icons */
 #crm-container div span.privacy-flag {
-  background-repeat: no-repeat;
-  background-image: url("../i/stop-icon.png");
   float: right;
+  font-size: 80%;
 }
 
 /* specific, targeted fixes */
@@ -3025,6 +3060,7 @@ tbody.scrollContent tr.alternateRow {
 }
 .crm-container .select2-container-multi .select2-choices .select2-search-field input {
   padding: 4px;
+  min-height: unset;  /* Overide style imposed by WordPress 5.3 - see https://lab.civicrm.org/dev/wordpress/issues/46 */
 }
 .crm-container .select2-search-choice-close {
   top: 2px;
@@ -3484,21 +3520,6 @@ span.crm-select-item-color {
   padding-left: 20px;
 }
 
-.crm-container span.collapsed,
-.crm-container a.collapsed,
-.crm-container .crm-expand-row {
-  background: url("../i/TreePlus.gif") no-repeat 0 0;
-  padding-left: 19px;
-  cursor: pointer;
-}
-
-.crm-container span.expanded,
-.crm-container a.expanded {
-  background: url("../i/TreeMinus.gif") no-repeat 0 0;
-  padding-left: 19px;
-  cursor: pointer;
-}
-
 /* Notifications */
 #crm-notification-container {
   width: 350px;
diff --git a/civicrm/css/contactSummary.css b/civicrm/css/contactSummary.css
index 813f0c491619ccb3643b305467e681c91f8fc9c0..1fc283ef5691bfd262228800bccd7f324f8b0d0a 100644
--- a/civicrm/css/contactSummary.css
+++ b/civicrm/css/contactSummary.css
@@ -226,12 +226,6 @@ div#crm-contact-thumbnail {
   text-align: right;
 }
 
-#crm-container .geotag {
-  padding: 2px 0 2px 20px !important;
-  background: url('../i/geotag_16.png') left center no-repeat;
-  font-size: .9em;
-}
-
 /* contact actions */
 
 #crm-contact-actions-wrapper {
diff --git a/civicrm/css/dashboard.css b/civicrm/css/dashboard.css
index cf2f34e815617599fc20824c30841b054f68e858..7f783555fd5344f244c1f67dd602bd1cd116b641 100644
--- a/civicrm/css/dashboard.css
+++ b/civicrm/css/dashboard.css
@@ -71,25 +71,6 @@
   min-height: 10em;
 }
 
-#crm-container .widget-content .crm-accordion-header {
-  background-color: #EFEFE5;
-  background-image: url("../i/TreeMinus.gif");
-  color: #080808;
-}
-
-#crm-container .widget-content .crm-accordion-wrapper.collapsed .crm-accordion-header {
-  background-image: url("../i/TreePlus.gif");
-}
-
-#crm-container .widget-content .crm-accordion-header:hover {
-  background-color: #e8e8de;
-}
-
-#crm-container .widget-content .crm-accordion-body {
-  border-color: #e8e8de;
-  padding: 4px .5em;
-}
-
 #crm-container .widget-controls {
   background-color: #5D677B;
   /* Standards-browsers do this anyway because all inner block elements are floated.  IE doesn't because it's crap. */
diff --git a/civicrm/i/TreeMinus.gif b/civicrm/i/TreeMinus.gif
deleted file mode 100644
index 964739a1ec8487402e005be65528fc72fb48e27d..0000000000000000000000000000000000000000
Binary files a/civicrm/i/TreeMinus.gif and /dev/null differ
diff --git a/civicrm/i/TreeMinusWhite.gif b/civicrm/i/TreeMinusWhite.gif
deleted file mode 100644
index f5ae57ea85aed0b3251c03e23be9e9b635eb112a..0000000000000000000000000000000000000000
Binary files a/civicrm/i/TreeMinusWhite.gif and /dev/null differ
diff --git a/civicrm/i/TreePlus.gif b/civicrm/i/TreePlus.gif
deleted file mode 100644
index a07d4c540ffdfcaa0551d02654905280ed62c9c0..0000000000000000000000000000000000000000
Binary files a/civicrm/i/TreePlus.gif and /dev/null differ
diff --git a/civicrm/i/TreePlusWhite.gif b/civicrm/i/TreePlusWhite.gif
deleted file mode 100644
index 69c21a32f90183ba564885ac0bafc2adde1861e8..0000000000000000000000000000000000000000
Binary files a/civicrm/i/TreePlusWhite.gif and /dev/null differ
diff --git a/civicrm/i/admin/01.png b/civicrm/i/admin/01.png
deleted file mode 100644
index 159903a0cd7326e403fe94d74ff262cfac38b7ac..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/01.png and /dev/null differ
diff --git a/civicrm/i/admin/02.png b/civicrm/i/admin/02.png
deleted file mode 100644
index fe203d73d4d8186cc8a73424cd3ec5d6d71937c5..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/02.png and /dev/null differ
diff --git a/civicrm/i/admin/03.png b/civicrm/i/admin/03.png
deleted file mode 100644
index eea651b8fd5d4289ba52418928f443210c47286d..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/03.png and /dev/null differ
diff --git a/civicrm/i/admin/04.png b/civicrm/i/admin/04.png
deleted file mode 100644
index 6b9e2c295921f05077d11e9f67dca3257d54b4f1..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/04.png and /dev/null differ
diff --git a/civicrm/i/admin/05.png b/civicrm/i/admin/05.png
deleted file mode 100644
index be900bbb9138b35d6294c8b4254484d8e7d930ed..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/05.png and /dev/null differ
diff --git a/civicrm/i/admin/06.png b/civicrm/i/admin/06.png
deleted file mode 100644
index 8b363579e70f0ff96114733b33f5b3c215319ad6..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/06.png and /dev/null differ
diff --git a/civicrm/i/admin/07.png b/civicrm/i/admin/07.png
deleted file mode 100644
index 594e42a4159814dd7db36ff8bffcf7bf68046e96..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/07.png and /dev/null differ
diff --git a/civicrm/i/admin/08.png b/civicrm/i/admin/08.png
deleted file mode 100644
index e56dbd826e38f2b9819c96ec694e2690841f40e5..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/08.png and /dev/null differ
diff --git a/civicrm/i/admin/09.png b/civicrm/i/admin/09.png
deleted file mode 100644
index 32db30b9f00de74ff3bec7217db3ef5d6633c83a..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/09.png and /dev/null differ
diff --git a/civicrm/i/admin/10.png b/civicrm/i/admin/10.png
deleted file mode 100644
index 6b2bde2c514e36904857ff166d35b30f99df8d55..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/10.png and /dev/null differ
diff --git a/civicrm/i/admin/11.png b/civicrm/i/admin/11.png
deleted file mode 100644
index cb8a01be7325e00b68ed59d2b715dffbf0c778af..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/11.png and /dev/null differ
diff --git a/civicrm/i/admin/12.png b/civicrm/i/admin/12.png
deleted file mode 100644
index cdb432d166072e56d1dc54364e76aa3f416a56ff..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/12.png and /dev/null differ
diff --git a/civicrm/i/admin/13.png b/civicrm/i/admin/13.png
deleted file mode 100644
index ae10d7dda631f9429d3ed816dba9eb189521476b..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/13.png and /dev/null differ
diff --git a/civicrm/i/admin/14.png b/civicrm/i/admin/14.png
deleted file mode 100644
index 4078a96e35c9f178214d88d910d1b0c6ca668f06..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/14.png and /dev/null differ
diff --git a/civicrm/i/admin/36.png b/civicrm/i/admin/36.png
deleted file mode 100644
index 0af91e7ae2af8ea1062cc52bbf4ece95cce6ebaa..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/36.png and /dev/null differ
diff --git a/civicrm/i/admin/DataStore.gif b/civicrm/i/admin/DataStore.gif
deleted file mode 100644
index f277d48a2c7dbbd6082ea6ca7b8edddac8ca8504..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/DataStore.gif and /dev/null differ
diff --git a/civicrm/i/admin/PayPal_mark_37x23.gif b/civicrm/i/admin/PayPal_mark_37x23.gif
deleted file mode 100644
index d0a918a26e9b242274eee6d31973be32201823fd..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/PayPal_mark_37x23.gif and /dev/null differ
diff --git a/civicrm/i/admin/Premiums.png b/civicrm/i/admin/Premiums.png
deleted file mode 100644
index 89b2f501a9d9186c30aa24e038f807cc888929cf..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/Premiums.png and /dev/null differ
diff --git a/civicrm/i/admin/Profile.png b/civicrm/i/admin/Profile.png
deleted file mode 100644
index c2b746b8ba25f092e47b27f6e19f999fec849635..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/Profile.png and /dev/null differ
diff --git a/civicrm/i/admin/Synch_user.png b/civicrm/i/admin/Synch_user.png
deleted file mode 100644
index f92a154a6f181bd0d4cd0a936a25a1b35eb95cfc..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/Synch_user.png and /dev/null differ
diff --git a/civicrm/i/admin/accepted_creditcards.png b/civicrm/i/admin/accepted_creditcards.png
deleted file mode 100644
index ec34af9729fcdd83086e1e516d3802840e1318da..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/accepted_creditcards.png and /dev/null differ
diff --git a/civicrm/i/admin/communication.png b/civicrm/i/admin/communication.png
deleted file mode 100644
index 10ea5d60553e3bb46a8e77eaacd3fa79175cab2f..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/communication.png and /dev/null differ
diff --git a/civicrm/i/admin/contribution_types.png b/civicrm/i/admin/contribution_types.png
deleted file mode 100644
index dd372944b41458e199d114ae87410fc0edae3c1e..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/contribution_types.png and /dev/null differ
diff --git a/civicrm/i/admin/custm_data.png b/civicrm/i/admin/custm_data.png
deleted file mode 100644
index 2ff99b29225972048bdb10762d302fada006adf9..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/custm_data.png and /dev/null differ
diff --git a/civicrm/i/admin/domain.png b/civicrm/i/admin/domain.png
deleted file mode 100644
index b4a8edf63c76841ea40a1b7dc53cf5c3964b8098..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/domain.png and /dev/null differ
diff --git a/civicrm/i/admin/duplicate_matching.png b/civicrm/i/admin/duplicate_matching.png
deleted file mode 100644
index 6ae758ade5e6e45c11bac1c49746c78d844986f6..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/duplicate_matching.png and /dev/null differ
diff --git a/civicrm/i/admin/event_manage.png b/civicrm/i/admin/event_manage.png
deleted file mode 100644
index e1a7637b16ddd15e2a232e3b91c45905e61e3cee..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/event_manage.png and /dev/null differ
diff --git a/civicrm/i/admin/event_type.png b/civicrm/i/admin/event_type.png
deleted file mode 100644
index 2c2fa8837576f839f50f27c36f9213fb93b808ab..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/event_type.png and /dev/null differ
diff --git a/civicrm/i/admin/import_export_map.png b/civicrm/i/admin/import_export_map.png
deleted file mode 100644
index e469807bcc198826d44b919ccfac3365526b4c58..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/import_export_map.png and /dev/null differ
diff --git a/civicrm/i/admin/membership_status.png b/civicrm/i/admin/membership_status.png
deleted file mode 100644
index ad278bac3e6477a001bdd178819cd3d845e7d213..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/membership_status.png and /dev/null differ
diff --git a/civicrm/i/admin/membership_type.png b/civicrm/i/admin/membership_type.png
deleted file mode 100644
index c645a9b99e5d1417d250e02827cbc06f71e307c9..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/membership_type.png and /dev/null differ
diff --git a/civicrm/i/admin/online_contribution_pages.png b/civicrm/i/admin/online_contribution_pages.png
deleted file mode 100644
index c72ea3dce1800ec44e9efd73fea083b7868aeb80..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/online_contribution_pages.png and /dev/null differ
diff --git a/civicrm/i/admin/option.png b/civicrm/i/admin/option.png
deleted file mode 100644
index 7727d88883e6815f4bb9e24d927d43e4979ec0cf..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/option.png and /dev/null differ
diff --git a/civicrm/i/admin/parti_role.png b/civicrm/i/admin/parti_role.png
deleted file mode 100644
index 63ad8f9d8527fc1ecc6ab5cd43425b17f0e70fdf..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/parti_role.png and /dev/null differ
diff --git a/civicrm/i/admin/parti_status.png b/civicrm/i/admin/parti_status.png
deleted file mode 100644
index 07e4f5da61ccafacff9454fe0fb6bf2683f2027b..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/parti_status.png and /dev/null differ
diff --git a/civicrm/i/admin/payment_instruments.png b/civicrm/i/admin/payment_instruments.png
deleted file mode 100644
index 457e8b007c4cfcf179d3c1f781300e1d2a6ec8b1..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/payment_instruments.png and /dev/null differ
diff --git a/civicrm/i/admin/price_sets.png b/civicrm/i/admin/price_sets.png
deleted file mode 100644
index fc7a3feadcf1a7969815e4e43751234e1a677bac..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/price_sets.png and /dev/null differ
diff --git a/civicrm/i/admin/rela_type.png b/civicrm/i/admin/rela_type.png
deleted file mode 100644
index 494054e4e650dfd11539337d931be533c14e2194..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/rela_type.png and /dev/null differ
diff --git a/civicrm/i/admin/small/01.png b/civicrm/i/admin/small/01.png
deleted file mode 100644
index 04717e2c5d9dfa5e2822b46db0927fc305ea304f..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/01.png and /dev/null differ
diff --git a/civicrm/i/admin/small/02.png b/civicrm/i/admin/small/02.png
deleted file mode 100644
index 56433c8b478243c098e1bf04a1d156b841b2d2e6..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/02.png and /dev/null differ
diff --git a/civicrm/i/admin/small/03.png b/civicrm/i/admin/small/03.png
deleted file mode 100644
index 59ae2277e2d2ee5fc4afb1d244c546b73a01d9bf..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/03.png and /dev/null differ
diff --git a/civicrm/i/admin/small/04.png b/civicrm/i/admin/small/04.png
deleted file mode 100644
index 15cacc2589c4f542be48b90f2d61fe2d2a776442..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/04.png and /dev/null differ
diff --git a/civicrm/i/admin/small/05.png b/civicrm/i/admin/small/05.png
deleted file mode 100644
index 2f370c2cda3af202198707a4b927a6b900cf369e..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/05.png and /dev/null differ
diff --git a/civicrm/i/admin/small/06.png b/civicrm/i/admin/small/06.png
deleted file mode 100644
index fe213c6f9c0d0bee797a0bf766b68c0e17a20db2..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/06.png and /dev/null differ
diff --git a/civicrm/i/admin/small/07.png b/civicrm/i/admin/small/07.png
deleted file mode 100644
index 1730ce7e629975c1a760023ae7bdae3244fc1034..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/07.png and /dev/null differ
diff --git a/civicrm/i/admin/small/08.png b/civicrm/i/admin/small/08.png
deleted file mode 100644
index 4f64c210b5366f923fd115158819d5d6e0cc4a32..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/08.png and /dev/null differ
diff --git a/civicrm/i/admin/small/09.png b/civicrm/i/admin/small/09.png
deleted file mode 100644
index dce5bd766bd34b7ae9009d10ce4ed39ce1b630d1..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/09.png and /dev/null differ
diff --git a/civicrm/i/admin/small/10.png b/civicrm/i/admin/small/10.png
deleted file mode 100644
index a36d4cfef172fb05202c250269f5af42d3a398fd..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/10.png and /dev/null differ
diff --git a/civicrm/i/admin/small/11.png b/civicrm/i/admin/small/11.png
deleted file mode 100644
index b99fa780f4733082858778c1084dd0281d13649e..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/11.png and /dev/null differ
diff --git a/civicrm/i/admin/small/12.png b/civicrm/i/admin/small/12.png
deleted file mode 100644
index 758609e4eb680767a2caa69805ecf1c566991c8c..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/12.png and /dev/null differ
diff --git a/civicrm/i/admin/small/13.png b/civicrm/i/admin/small/13.png
deleted file mode 100644
index 1eed2c87c87e3da0c5ad996b74534a83a1a2776d..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/13.png and /dev/null differ
diff --git a/civicrm/i/admin/small/36.png b/civicrm/i/admin/small/36.png
deleted file mode 100644
index 2f1a6d9b3f0b8c68899221d87a722349e3159a5f..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/36.png and /dev/null differ
diff --git a/civicrm/i/admin/small/Premiums.png b/civicrm/i/admin/small/Premiums.png
deleted file mode 100644
index eb1c688b0c01d35dc101868de8e84ae8bdf922f9..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/Premiums.png and /dev/null differ
diff --git a/civicrm/i/admin/small/Profile.png b/civicrm/i/admin/small/Profile.png
deleted file mode 100644
index 09c0736e2c64e39b098ad9b5767784719d02d9ae..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/Profile.png and /dev/null differ
diff --git a/civicrm/i/admin/small/Synch_user.png b/civicrm/i/admin/small/Synch_user.png
deleted file mode 100644
index 5be37f91a070816aa02c534e4696263638212b9c..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/Synch_user.png and /dev/null differ
diff --git a/civicrm/i/admin/small/accepted_creditcards.png b/civicrm/i/admin/small/accepted_creditcards.png
deleted file mode 100644
index b421a4a951cd8071a668d72d6177869114e5a939..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/accepted_creditcards.png and /dev/null differ
diff --git a/civicrm/i/admin/small/case_type.gif b/civicrm/i/admin/small/case_type.gif
deleted file mode 100644
index 9a1d1ad73f82b63b3a9814a0b50f27d11f0d407c..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/case_type.gif and /dev/null differ
diff --git a/civicrm/i/admin/small/case_type.png b/civicrm/i/admin/small/case_type.png
deleted file mode 100644
index 21b5f745105d5fb43750969e84e35db7d88e052b..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/case_type.png and /dev/null differ
diff --git a/civicrm/i/admin/small/communication.png b/civicrm/i/admin/small/communication.png
deleted file mode 100644
index a6a913c36010b7103529af89ede41f2e7163eadf..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/communication.png and /dev/null differ
diff --git a/civicrm/i/admin/small/contribution_types.png b/civicrm/i/admin/small/contribution_types.png
deleted file mode 100644
index 00e28d4491eaa112c1a5767d69d49a1af9e147c5..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/contribution_types.png and /dev/null differ
diff --git a/civicrm/i/admin/small/custm_data.png b/civicrm/i/admin/small/custm_data.png
deleted file mode 100644
index acb13b72e25205784677e37383a7dc0fdda18a18..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/custm_data.png and /dev/null differ
diff --git a/civicrm/i/admin/small/domain.png b/civicrm/i/admin/small/domain.png
deleted file mode 100644
index 57a2b1ab4b4e0b34a3b471cd297c86cd04601fba..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/domain.png and /dev/null differ
diff --git a/civicrm/i/admin/small/duplicate_matching.png b/civicrm/i/admin/small/duplicate_matching.png
deleted file mode 100644
index 20eb405360e83231f10e3d4edac9bca20e3b5397..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/duplicate_matching.png and /dev/null differ
diff --git a/civicrm/i/admin/small/event_manage.png b/civicrm/i/admin/small/event_manage.png
deleted file mode 100644
index 2f7342e58f1f0cb4dac39a8d79b3e89b248f3693..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/event_manage.png and /dev/null differ
diff --git a/civicrm/i/admin/small/event_type.png b/civicrm/i/admin/small/event_type.png
deleted file mode 100644
index 67d20f60528b84efe036ecfe2b27fd6046d4e673..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/event_type.png and /dev/null differ
diff --git a/civicrm/i/admin/small/grant_type.png b/civicrm/i/admin/small/grant_type.png
deleted file mode 100644
index 95fc4608dd4803590f3f846c094a47514fd6f3de..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/grant_type.png and /dev/null differ
diff --git a/civicrm/i/admin/small/import_export_map.png b/civicrm/i/admin/small/import_export_map.png
deleted file mode 100644
index 920734118976e8b716ed339f82723c759279f4a7..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/import_export_map.png and /dev/null differ
diff --git a/civicrm/i/admin/small/membership_status.png b/civicrm/i/admin/small/membership_status.png
deleted file mode 100644
index bf7e712a776669016c7d75713d8ddf8f41a82610..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/membership_status.png and /dev/null differ
diff --git a/civicrm/i/admin/small/membership_type.png b/civicrm/i/admin/small/membership_type.png
deleted file mode 100644
index 2efe7026c5ef977d848853f9e55443d4702d6676..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/membership_type.png and /dev/null differ
diff --git a/civicrm/i/admin/small/online_contribution_pages.png b/civicrm/i/admin/small/online_contribution_pages.png
deleted file mode 100644
index a854f4115f45e8424d33535cc09496e59c386333..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/online_contribution_pages.png and /dev/null differ
diff --git a/civicrm/i/admin/small/option.png b/civicrm/i/admin/small/option.png
deleted file mode 100644
index 887a66a7117abf538e8ce1543df6f5dc380c7405..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/option.png and /dev/null differ
diff --git a/civicrm/i/admin/small/parti_role.png b/civicrm/i/admin/small/parti_role.png
deleted file mode 100644
index f16850d0ffbc05bf51b07f4267849357e52dd6f2..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/parti_role.png and /dev/null differ
diff --git a/civicrm/i/admin/small/parti_status.png b/civicrm/i/admin/small/parti_status.png
deleted file mode 100644
index 6cdbc5e4b84fb263269bcba5c690a872fbb3a289..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/parti_status.png and /dev/null differ
diff --git a/civicrm/i/admin/small/payment_instruments.png b/civicrm/i/admin/small/payment_instruments.png
deleted file mode 100644
index 57c61a33efee75c9e96d583e8ce4529703d56968..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/payment_instruments.png and /dev/null differ
diff --git a/civicrm/i/admin/small/price_sets.png b/civicrm/i/admin/small/price_sets.png
deleted file mode 100644
index b6c3b98719bdcd089f5c6fe7ef161582af6732b0..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/price_sets.png and /dev/null differ
diff --git a/civicrm/i/admin/small/redaction_type.png b/civicrm/i/admin/small/redaction_type.png
deleted file mode 100644
index 8d9239552c5bd291304a362d48c1cdffacf5c30c..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/redaction_type.png and /dev/null differ
diff --git a/civicrm/i/admin/small/rela_type.png b/civicrm/i/admin/small/rela_type.png
deleted file mode 100644
index 7a02c626e97316da9a208f9bb416b5b886982fca..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/rela_type.png and /dev/null differ
diff --git a/civicrm/i/admin/small/report_list.gif b/civicrm/i/admin/small/report_list.gif
deleted file mode 100644
index 328a91d78d547a6046e074a973895a779ce9e2c9..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/report_list.gif and /dev/null differ
diff --git a/civicrm/i/admin/small/report_template.gif b/civicrm/i/admin/small/report_template.gif
deleted file mode 100644
index 3505bbda9ea1fe6910054902de829b5d38a0abc6..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/report_template.gif and /dev/null differ
diff --git a/civicrm/i/admin/small/soft_credit_type.png b/civicrm/i/admin/small/soft_credit_type.png
deleted file mode 100644
index a1b1e10274b4cd1d15942d1277c62cfe4a9b3c6a..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/soft_credit_type.png and /dev/null differ
diff --git a/civicrm/i/admin/small/template.png b/civicrm/i/admin/small/template.png
deleted file mode 100644
index 18f3c4fa21b9fd1ce187ecb8b3175ea453135280..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/template.png and /dev/null differ
diff --git a/civicrm/i/admin/small/title.png b/civicrm/i/admin/small/title.png
deleted file mode 100644
index 3c2aa2403f065e15d03c49d82f0dc10326393b60..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/title.png and /dev/null differ
diff --git a/civicrm/i/admin/small/updatepath.png b/civicrm/i/admin/small/updatepath.png
deleted file mode 100644
index 293abb304b02676ba49db323e2a15cb832a7ec30..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/small/updatepath.png and /dev/null differ
diff --git a/civicrm/i/admin/template.png b/civicrm/i/admin/template.png
deleted file mode 100644
index e3f348a9377589098a4cfab3a0861436a5e75a56..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/template.png and /dev/null differ
diff --git a/civicrm/i/admin/title.png b/civicrm/i/admin/title.png
deleted file mode 100644
index 71b94c4ecc41cb4de28b9e482555555f6e3e97dd..0000000000000000000000000000000000000000
Binary files a/civicrm/i/admin/title.png and /dev/null differ
diff --git a/civicrm/i/check.gif b/civicrm/i/check.gif
deleted file mode 100644
index 7e21249cb8bc055f172ea2eaace1d29e9fcf2650..0000000000000000000000000000000000000000
Binary files a/civicrm/i/check.gif and /dev/null differ
diff --git a/civicrm/i/copy.png b/civicrm/i/copy.png
deleted file mode 100644
index cd918ab1970cb5999e83eb462f9a8a62da196527..0000000000000000000000000000000000000000
Binary files a/civicrm/i/copy.png and /dev/null differ
diff --git a/civicrm/i/feed-icon.png b/civicrm/i/feed-icon.png
deleted file mode 100644
index fadd43ee1ddb28124bcd593e2051de8297a0acb6..0000000000000000000000000000000000000000
Binary files a/civicrm/i/feed-icon.png and /dev/null differ
diff --git a/civicrm/i/geotag_16.png b/civicrm/i/geotag_16.png
deleted file mode 100644
index 8df245699d4d0240e58e67287e0a043f0fcbe75b..0000000000000000000000000000000000000000
Binary files a/civicrm/i/geotag_16.png and /dev/null differ
diff --git a/civicrm/i/grippie.png b/civicrm/i/grippie.png
deleted file mode 100644
index 6524d4167d2d6e3939cfcf3111ce520acd66bc21..0000000000000000000000000000000000000000
Binary files a/civicrm/i/grippie.png and /dev/null differ
diff --git a/civicrm/i/group.png b/civicrm/i/group.png
deleted file mode 100644
index 6f7f75cbd11f2f50025daa3a07e5c505c8144a65..0000000000000000000000000000000000000000
Binary files a/civicrm/i/group.png and /dev/null differ
diff --git a/civicrm/i/ical_feed.gif b/civicrm/i/ical_feed.gif
deleted file mode 100644
index e31788a7713c1e3921f0b8cdf99664695b38c9f9..0000000000000000000000000000000000000000
Binary files a/civicrm/i/ical_feed.gif and /dev/null differ
diff --git a/civicrm/i/office-calendar.png b/civicrm/i/office-calendar.png
deleted file mode 100644
index 60ad82b236e3414dfbadaff7853deacc9d918ed5..0000000000000000000000000000000000000000
Binary files a/civicrm/i/office-calendar.png and /dev/null differ
diff --git a/civicrm/i/stop-icon.png b/civicrm/i/stop-icon.png
deleted file mode 100644
index e00eea441f7bf8d0e9b1b51a617e36d47405c6f1..0000000000000000000000000000000000000000
Binary files a/civicrm/i/stop-icon.png and /dev/null differ
diff --git a/civicrm/i/tel.gif b/civicrm/i/tel.gif
deleted file mode 100644
index c8b0e4d9ad4355fdc5bc794fe74c77ddd3aeadfa..0000000000000000000000000000000000000000
Binary files a/civicrm/i/tel.gif and /dev/null differ
diff --git a/civicrm/install/civicrm.php b/civicrm/install/civicrm.php
index 0932b1902b58e85622a2632f580a9278ac00adea..ed2df94303effa1093b915be4a40d3f8f24c79a2 100644
--- a/civicrm/install/civicrm.php
+++ b/civicrm/install/civicrm.php
@@ -200,7 +200,7 @@ function civicrm_config(&$config) {
   global $tplPath, $installType;
 
   // Ex: $extraSettings[] = '$civicrm_settings["domain"]["foo"] = "bar";';
-  $extraSettings = array();
+  $extraSettings = [];
 
   $params = array(
     'crmRoot' => $crmPath,
diff --git a/civicrm/js/Common.js b/civicrm/js/Common.js
index f4c2a60a6f09eeb0ed0b4eae89e90867f5001d19..0ed3e38c67b131ba9db029c452cbda00595042cc 100644
--- a/civicrm/js/Common.js
+++ b/civicrm/js/Common.js
@@ -387,7 +387,7 @@ if (!CRM.vars) CRM.vars = {};
       description = row.description || $(row.element).data('description'),
       ret = '';
     if (icon) {
-      ret += '<i class="crm-i ' + icon + '"></i> ';
+      ret += '<i class="crm-i ' + icon + '" aria-hidden="true"></i> ';
     }
     if (color) {
       ret += '<span class="crm-select-item-color" style="background-color: ' + color + '"></span> ';
@@ -395,6 +395,35 @@ if (!CRM.vars) CRM.vars = {};
     return ret + _.escape(row.text) + (description ? '<div class="crm-select2-row-description"><p>' + _.escape(description) + '</p></div>' : '');
   }
 
+  /**
+   * Helper to generate an icon with alt text.
+   *
+   * See also smarty `{icon}` and CRM_Core_Page::crmIcon() functions
+   *
+   * @param string icon
+   *   The Font Awesome icon class to use.
+   * @param string text
+   *   Alt text to display.
+   * @param mixed condition
+   *   This will only display if this is truthy.
+   *
+   * @return string
+   *   The formatted icon markup.
+   */
+  CRM.utils.formatIcon = function (icon, text, condition) {
+    if (typeof condition !== 'undefined' && !condition) {
+      return '';
+    }
+    var title = '';
+    var sr = '';
+    if (text) {
+      text = _.escape(text);
+      title = ' title="' + text + '"';
+      sr = '<span class="sr-only">' + text + '</span>';
+    }
+    return '<i class="crm-i ' + icon + '"' + title + ' aria-hidden="true"></i>' + sr;
+  };
+
   /**
    * Wrapper for select2 initialization function; supplies defaults
    * @param options object
@@ -434,7 +463,7 @@ if (!CRM.vars) CRM.vars = {};
             placeholder = settings.placeholder || $el.data('placeholder') || $el.attr('placeholder') || $('option[value=""]', $el).text();
           if (m.length && placeholder === m) {
             iconClass = $el.attr('class').match(/(fa-\S*)/)[1];
-            out = '<i class="crm-i ' + iconClass + '"></i> ' + out;
+            out = '<i class="crm-i ' + iconClass + '" aria-hidden="true"></i> ' + out;
           }
           return out;
         };
@@ -704,7 +733,7 @@ if (!CRM.vars) CRM.vars = {};
     }
     _.each(createLinks, function(link) {
       markup += ' <a class="crm-add-entity crm-hover-button" href="' + link.url + '">' +
-        '<i class="crm-i ' + (link.icon || 'fa-plus-circle') + '"></i> ' +
+        '<i class="crm-i ' + (link.icon || 'fa-plus-circle') + '" aria-hidden="true"></i> ' +
         _.escape(link.label) + '</a>';
     });
     markup += '</div>';
diff --git a/civicrm/js/crm.datepicker.js b/civicrm/js/crm.datepicker.js
index e1a15f85d0afb6b57cc1c4109f2d730bc325275e..95663c09492afe6fa782171d69713994da66afa2 100644
--- a/civicrm/js/crm.datepicker.js
+++ b/civicrm/js/crm.datepicker.js
@@ -29,7 +29,7 @@
         type = hasDatepicker ? 'text' : 'number';
 
       if (settings.allowClear !== undefined ? settings.allowClear : !$dataField.is('.required, [required]')) {
-        $clearLink = $('<a class="crm-hover-button crm-clear-link" title="'+ _.escape(ts('Clear')) +'"><i class="crm-i fa-times"></i></a>')
+        $clearLink = $('<a class="crm-hover-button crm-clear-link" title="'+ _.escape(ts('Clear')) +'"><i class="crm-i fa-times" aria-hidden="true"></i></a>')
           .insertAfter($dataField);
       }
       if (settings.time !== false) {
diff --git a/civicrm/js/crm.drupal7.js b/civicrm/js/crm.drupal7.js
new file mode 100644
index 0000000000000000000000000000000000000000..8f172f23b9798ff9f57cd9a3d86ea8da227d361e
--- /dev/null
+++ b/civicrm/js/crm.drupal7.js
@@ -0,0 +1,18 @@
+// https://civicrm.org/licensing
+(function($) {
+  "use strict";
+
+  $(document).on('crmLoad', '#civicrm-menu', hideMenuToggleButtonForNonAdminUsers);
+
+  /**
+   * Hides the Menu Toggle Button when the Admin Menu is not available for the user.
+   */
+  function hideMenuToggleButtonForNonAdminUsers() {
+    $(document).ready(function() {
+      if (!$('#toolbar').length) {
+        CRM.menubar.removeToggleButton();
+      }
+    });
+  }
+
+})(CRM.$);
diff --git a/civicrm/js/crm.menubar.js b/civicrm/js/crm.menubar.js
index eba37ed3c46d6c90b6281b3be088900ce874018a..d28f0983498f6a908f41c5b3839575871977f3ba 100644
--- a/civicrm/js/crm.menubar.js
+++ b/civicrm/js/crm.menubar.js
@@ -8,6 +8,7 @@
     data: null,
     settings: {collapsibleBehavior: 'accordion'},
     position: 'over-cms-menu',
+    toggleButton: true,
     attachTo: (CRM.menubar && CRM.menubar.position === 'above-crm-container') ? '#crm-container' : 'body',
     initialize: function() {
       var cache = CRM.cache.get('menubar');
@@ -231,17 +232,24 @@
       }
     },
     initializePosition: function() {
-      if (CRM.menubar.position === 'over-cms-menu' || CRM.menubar.position === 'below-cms-menu') {
+      if (CRM.menubar.toggleButton && (CRM.menubar.position === 'over-cms-menu' || CRM.menubar.position === 'below-cms-menu')) {
         $('#civicrm-menu')
           .on('click', 'a[href="#toggle-position"]', function(e) {
             e.preventDefault();
             CRM.menubar.togglePosition();
           })
-          .append('<li id="crm-menubar-toggle-position"><a href="#toggle-position" title="' + ts('Adjust menu position') + '"><i class="crm-i fa-arrow-up"></i></a>');
+          .append('<li id="crm-menubar-toggle-position"><a href="#toggle-position" title="' + ts('Adjust menu position') + '"><i class="crm-i fa-arrow-up" aria-hidden="true"></i></a>');
         CRM.menubar.position = CRM.cache.get('menubarPosition', CRM.menubar.position);
       }
       $('body').addClass('crm-menubar-visible crm-menubar-' + CRM.menubar.position);
     },
+    removeToggleButton: function() {
+      $('#crm-menubar-toggle-position').remove();
+      CRM.menubar.toggleButton = false;
+      if (CRM.menubar.position === 'below-cms-menu') {
+        CRM.menubar.togglePosition();
+      }
+    },
     initializeResponsive: function() {
       var $mainMenuState = $('#crm-menubar-state');
       // hide mobile menu beforeunload
@@ -357,10 +365,6 @@
         var $selection = $('.crm-quickSearchField input:checked'),
           label = $selection.parent().text(),
           value = $selection.val();
-        // These fields are not supported by advanced search
-        if (!value || value === 'first_name' || value === 'last_name') {
-          value = 'sort_name';
-        }
         $('#crm-qsearch-input').attr({name: value, placeholder: '\uf002 ' + label});
       }
       $('.crm-quickSearchField').click(function() {
diff --git a/civicrm/js/jquery/jquery.crmEditable.js b/civicrm/js/jquery/jquery.crmEditable.js
index b68b27f0a24012a04ecf7d34a41526bfe4b31902..7d0da65ba1b8fbae9bb5c7e8bc3ab9fc06897219 100644
--- a/civicrm/js/jquery/jquery.crmEditable.js
+++ b/civicrm/js/jquery/jquery.crmEditable.js
@@ -108,10 +108,10 @@
 
       var settings = {
         tooltip: $i.data('tooltip') || ts('Click to edit'),
-        placeholder: $i.data('placeholder') || '<i class="crm-i fa-pencil crm-editable-placeholder"></i>',
+        placeholder: $i.data('placeholder') || '<i class="crm-i fa-pencil crm-editable-placeholder" aria-hidden="true"></i>',
         onblur: 'cancel',
-        cancel: '<button type="cancel"><i class="crm-i fa-times"></i></button>',
-        submit: '<button type="submit"><i class="crm-i fa-check"></i></button>',
+        cancel: '<button type="cancel"><i class="crm-i fa-times" aria-hidden="true"></i></button>',
+        submit: '<button type="submit"><i class="crm-i fa-check" aria-hidden="true"></i></button>',
         cssclass: 'crm-editable-form',
         data: getData,
         onreset: restoreContainer
diff --git a/civicrm/js/jquery/jquery.crmIconPicker.js b/civicrm/js/jquery/jquery.crmIconPicker.js
index 19f50f6b226505d3e9105cdf8a922820511a02e6..2cc4bd536cbd597dc504c9b5f5dbbc44a03fde7b 100644
--- a/civicrm/js/jquery/jquery.crmIconPicker.js
+++ b/civicrm/js/jquery/jquery.crmIconPicker.js
@@ -90,7 +90,7 @@
             '<div class="icon-ctrls crm-clearfix">' +
             '<input class="crm-form-text" name="search" placeholder="&#xf002"/>' +
             '<select class="crm-form-select"></select>' +
-            '<button type="button" class="cancel" title=""><i class="crm-i fa-ban"></i> ' + ts('No icon') + '</button>' +
+            '<button type="button" class="cancel" title=""><i class="crm-i fa-ban" aria-hidden="true"></i> ' + ts('No icon') + '</button>' +
             '</div>' +
             '<div class="icons"></div>'
           );
diff --git a/civicrm/js/jquery/jquery.dashboard.js b/civicrm/js/jquery/jquery.dashboard.js
index b757e27f6c472c4f3c4afd33501cf34093658e5a..4191285449bac84b76339fa5c142b87c4a65ae9d 100644
--- a/civicrm/js/jquery/jquery.dashboard.js
+++ b/civicrm/js/jquery/jquery.dashboard.js
@@ -364,7 +364,7 @@
 
       // Adds controls to a widget.  id is for internal use and image file name in images/dashboard/ (a .gif).
       widget.addControl = function(id, control) {
-          var markup = '<a class="crm-i ' + control.icon + '" alt="' + control.description + '" title="' + control.description + '"></a>';
+          var markup = '<a class="crm-i ' + control.icon + '" alt="' + control.description + '" title="' + control.description + '" aria-hidden="true"></a>';
           control.element = $(markup).prependTo($('.widget-controls', widget.element)).click(control.callback);
       };
 
diff --git a/civicrm/js/view/crm.designer.js b/civicrm/js/view/crm.designer.js
index 9d43b92bf06322c2066a6626cd5a454842d8779a..b00a2964f15670b57c4f876311e4e93f620884ce 100644
--- a/civicrm/js/view/crm.designer.js
+++ b/civicrm/js/view/crm.designer.js
@@ -675,7 +675,7 @@
       });
       var form1 = CRM.loadForm(url)
         .on('crmFormLoad', function() {
-          $(this).prepend('<div class="messages status"><i class="crm-i fa-info-circle"></i> ' + ts('Note: This will modify the field system-wide, not just in this profile form.') + '</div>');
+          $(this).prepend('<div class="messages status"><i class="crm-i fa-info-circle" aria-hidden="true"></i> ' + ts('Note: This will modify the field system-wide, not just in this profile form.') + '</div>');
         });
     },
     onChangeIsDuplicate: function(model, value, options) {
diff --git a/civicrm/package.json b/civicrm/package.json
index f3abb57f5282386ce243dae0e67c6ada01e53810..0dffff7c40da0bc0639d66a2164f6ef73b58e234 100644
--- a/civicrm/package.json
+++ b/civicrm/package.json
@@ -12,7 +12,7 @@
     "bower": "^1.8.8",
     "civicrm-cv": "^0.1.2",
     "jasmine-core": "~3.3.0",
-    "karma": "^4.1.0",
+    "karma": "^5.0.9",
     "karma-jasmine": "~2.0.1",
     "karma-ng-html2js-preprocessor": "^1.0.0",
     "karma-phantomjs-launcher": "^1.0.4"
diff --git a/civicrm/packages/HTTP/Request.php b/civicrm/packages/HTTP/Request.php
deleted file mode 100644
index 230a6a6ea72bc4e5cee6e62859da091e47cc98ff..0000000000000000000000000000000000000000
--- a/civicrm/packages/HTTP/Request.php
+++ /dev/null
@@ -1,1521 +0,0 @@
-<?php
-/**
- * Class for performing HTTP requests
- *
- * PHP versions 4 and 5
- *
- * LICENSE:
- *
- * Copyright (c) 2002-2007, Richard Heyes
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * o Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * o 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.
- * o The names of the authors may not 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.
- *
- * @category    HTTP
- * @package     HTTP_Request
- * @author      Richard Heyes <richard@phpguru.org>
- * @author      Alexey Borzov <avb@php.net>
- * @copyright   2002-2007 Richard Heyes
- * @license     http://opensource.org/licenses/bsd-license.php New BSD License
- * @version     CVS: $Id: Request.php,v 1.63 2008/10/11 11:07:10 avb Exp $
- * @link        http://pear.php.net/package/HTTP_Request/
- */
-
-/**
- * PEAR and PEAR_Error classes (for error handling)
- */
-require_once 'PEAR.php';
-/**
- * Socket class
- */
-require_once 'Net/Socket.php';
-/**
- * URL handling class
- */
-require_once 'Net/URL.php';
-
-/**#@+
- * Constants for HTTP request methods
- */
-define('HTTP_REQUEST_METHOD_GET',     'GET',     true);
-define('HTTP_REQUEST_METHOD_HEAD',    'HEAD',    true);
-define('HTTP_REQUEST_METHOD_POST',    'POST',    true);
-define('HTTP_REQUEST_METHOD_PUT',     'PUT',     true);
-define('HTTP_REQUEST_METHOD_DELETE',  'DELETE',  true);
-define('HTTP_REQUEST_METHOD_OPTIONS', 'OPTIONS', true);
-define('HTTP_REQUEST_METHOD_TRACE',   'TRACE',   true);
-/**#@-*/
-
-/**#@+
- * Constants for HTTP request error codes
- */
-define('HTTP_REQUEST_ERROR_FILE',             1);
-define('HTTP_REQUEST_ERROR_URL',              2);
-define('HTTP_REQUEST_ERROR_PROXY',            4);
-define('HTTP_REQUEST_ERROR_REDIRECTS',        8);
-define('HTTP_REQUEST_ERROR_RESPONSE',        16);
-define('HTTP_REQUEST_ERROR_GZIP_METHOD',     32);
-define('HTTP_REQUEST_ERROR_GZIP_READ',       64);
-define('HTTP_REQUEST_ERROR_GZIP_DATA',      128);
-define('HTTP_REQUEST_ERROR_GZIP_CRC',       256);
-/**#@-*/
-
-/**#@+
- * Constants for HTTP protocol versions
- */
-define('HTTP_REQUEST_HTTP_VER_1_0', '1.0', true);
-define('HTTP_REQUEST_HTTP_VER_1_1', '1.1', true);
-/**#@-*/
-
-if (extension_loaded('mbstring') && (2 & ini_get('mbstring.func_overload'))) {
-   /**
-    * Whether string functions are overloaded by their mbstring equivalents
-    */
-    define('HTTP_REQUEST_MBSTRING', true);
-} else {
-   /**
-    * @ignore
-    */
-    define('HTTP_REQUEST_MBSTRING', false);
-}
-
-/**
- * Class for performing HTTP requests
- *
- * Simple example (fetches yahoo.com and displays it):
- * <code>
- * $a = &new HTTP_Request('http://www.yahoo.com/');
- * $a->sendRequest();
- * echo $a->getResponseBody();
- * </code>
- *
- * @category    HTTP
- * @package     HTTP_Request
- * @author      Richard Heyes <richard@phpguru.org>
- * @author      Alexey Borzov <avb@php.net>
- * @version     Release: 1.4.4
- */
-class HTTP_Request
-{
-   /**#@+
-    * @access private
-    */
-    /**
-    * Instance of Net_URL
-    * @var Net_URL
-    */
-    var $_url;
-
-    /**
-    * Type of request
-    * @var string
-    */
-    var $_method;
-
-    /**
-    * HTTP Version
-    * @var string
-    */
-    var $_http;
-
-    /**
-    * Request headers
-    * @var array
-    */
-    var $_requestHeaders;
-
-    /**
-    * Basic Auth Username
-    * @var string
-    */
-    var $_user;
-
-    /**
-    * Basic Auth Password
-    * @var string
-    */
-    var $_pass;
-
-    /**
-    * Socket object
-    * @var Net_Socket
-    */
-    var $_sock;
-
-    /**
-    * Proxy server
-    * @var string
-    */
-    var $_proxy_host;
-
-    /**
-    * Proxy port
-    * @var integer
-    */
-    var $_proxy_port;
-
-    /**
-    * Proxy username
-    * @var string
-    */
-    var $_proxy_user;
-
-    /**
-    * Proxy password
-    * @var string
-    */
-    var $_proxy_pass;
-
-    /**
-    * Post data
-    * @var array
-    */
-    var $_postData;
-
-   /**
-    * Request body
-    * @var string
-    */
-    var $_body;
-
-   /**
-    * A list of methods that MUST NOT have a request body, per RFC 2616
-    * @var array
-    */
-    var $_bodyDisallowed = array('TRACE');
-
-   /**
-    * Methods having defined semantics for request body
-    *
-    * Content-Length header (indicating that the body follows, section 4.3 of
-    * RFC 2616) will be sent for these methods even if no body was added
-    *
-    * @var array
-    */
-    var $_bodyRequired = array('POST', 'PUT');
-
-   /**
-    * Files to post
-    * @var array
-    */
-    var $_postFiles = array();
-
-    /**
-    * Connection timeout.
-    * @var float
-    */
-    var $_timeout;
-
-    /**
-    * HTTP_Response object
-    * @var HTTP_Response
-    */
-    var $_response;
-
-    /**
-    * Whether to allow redirects
-    * @var boolean
-    */
-    var $_allowRedirects;
-
-    /**
-    * Maximum redirects allowed
-    * @var integer
-    */
-    var $_maxRedirects;
-
-    /**
-    * Current number of redirects
-    * @var integer
-    */
-    var $_redirects;
-
-   /**
-    * Whether to append brackets [] to array variables
-    * @var bool
-    */
-    var $_useBrackets = true;
-
-   /**
-    * Attached listeners
-    * @var array
-    */
-    var $_listeners = array();
-
-   /**
-    * Whether to save response body in response object property
-    * @var bool
-    */
-    var $_saveBody = true;
-
-   /**
-    * Timeout for reading from socket (array(seconds, microseconds))
-    * @var array
-    */
-    var $_readTimeout = null;
-
-   /**
-    * Options to pass to Net_Socket::connect. See stream_context_create
-    * @var array
-    */
-    var $_socketOptions = null;
-   /**#@-*/
-
-    /**
-    * Constructor
-    *
-    * Sets up the object
-    * @param    string  The url to fetch/access
-    * @param    array   Associative array of parameters which can have the following keys:
-    * <ul>
-    *   <li>method         - Method to use, GET, POST etc (string)</li>
-    *   <li>http           - HTTP Version to use, 1.0 or 1.1 (string)</li>
-    *   <li>user           - Basic Auth username (string)</li>
-    *   <li>pass           - Basic Auth password (string)</li>
-    *   <li>proxy_host     - Proxy server host (string)</li>
-    *   <li>proxy_port     - Proxy server port (integer)</li>
-    *   <li>proxy_user     - Proxy auth username (string)</li>
-    *   <li>proxy_pass     - Proxy auth password (string)</li>
-    *   <li>timeout        - Connection timeout in seconds (float)</li>
-    *   <li>allowRedirects - Whether to follow redirects or not (bool)</li>
-    *   <li>maxRedirects   - Max number of redirects to follow (integer)</li>
-    *   <li>useBrackets    - Whether to append [] to array variable names (bool)</li>
-    *   <li>saveBody       - Whether to save response body in response object property (bool)</li>
-    *   <li>readTimeout    - Timeout for reading / writing data over the socket (array (seconds, microseconds))</li>
-    *   <li>socketOptions  - Options to pass to Net_Socket object (array)</li>
-    * </ul>
-    * @access public
-    */
-    function __construct($url = '', $params = array())
-    {
-        $this->_method         =  HTTP_REQUEST_METHOD_GET;
-        $this->_http           =  HTTP_REQUEST_HTTP_VER_1_1;
-        $this->_requestHeaders = array();
-        $this->_postData       = array();
-        $this->_body           = null;
-
-        $this->_user = null;
-        $this->_pass = null;
-
-        $this->_proxy_host = null;
-        $this->_proxy_port = null;
-        $this->_proxy_user = null;
-        $this->_proxy_pass = null;
-
-        $this->_allowRedirects = false;
-        $this->_maxRedirects   = 3;
-        $this->_redirects      = 0;
-
-        $this->_timeout  = null;
-        $this->_response = null;
-
-        foreach ($params as $key => $value) {
-            $this->{'_' . $key} = $value;
-        }
-
-        if (!empty($url)) {
-            $this->setURL($url);
-        }
-
-        // Default useragent
-        $this->addHeader('User-Agent', 'PEAR HTTP_Request class ( http://pear.php.net/ )');
-
-        // We don't do keep-alives by default
-        $this->addHeader('Connection', 'close');
-
-        // Basic authentication
-        if (!empty($this->_user)) {
-            $this->addHeader('Authorization', 'Basic ' . base64_encode($this->_user . ':' . $this->_pass));
-        }
-
-        // Proxy authentication (see bug #5913)
-        if (!empty($this->_proxy_user)) {
-            $this->addHeader('Proxy-Authorization', 'Basic ' . base64_encode($this->_proxy_user . ':' . $this->_proxy_pass));
-        }
-
-        // Use gzip encoding if possible
-        if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http && extension_loaded('zlib')) {
-            $this->addHeader('Accept-Encoding', 'gzip');
-        }
-    }
-
-    /**
-    * Generates a Host header for HTTP/1.1 requests
-    *
-    * @access private
-    * @return string
-    */
-    function _generateHostHeader()
-    {
-        if ($this->_url->port != 80 AND strcasecmp($this->_url->protocol, 'http') == 0) {
-            $host = $this->_url->host . ':' . $this->_url->port;
-
-        } elseif ($this->_url->port != 443 AND strcasecmp($this->_url->protocol, 'https') == 0) {
-            $host = $this->_url->host . ':' . $this->_url->port;
-
-        } elseif ($this->_url->port == 443 AND strcasecmp($this->_url->protocol, 'https') == 0 AND strpos($this->_url->url, ':443') !== false) {
-            $host = $this->_url->host . ':' . $this->_url->port;
-
-        } else {
-            $host = $this->_url->host;
-        }
-
-        return $host;
-    }
-
-    /**
-    * Resets the object to its initial state (DEPRECATED).
-    * Takes the same parameters as the constructor.
-    *
-    * @param  string $url    The url to be requested
-    * @param  array  $params Associative array of parameters
-    *                        (see constructor for details)
-    * @access public
-    * @deprecated deprecated since 1.2, call the constructor if this is necessary
-    */
-    function reset($url, $params = array())
-    {
-        $this->__construct($url, $params);
-    }
-
-    /**
-    * Sets the URL to be requested
-    *
-    * @param  string The url to be requested
-    * @access public
-    */
-    function setURL($url)
-    {
-        $this->_url = new Net_URL($url, $this->_useBrackets);
-
-        if (!empty($this->_url->user) || !empty($this->_url->pass)) {
-            $this->setBasicAuth($this->_url->user, $this->_url->pass);
-        }
-
-        if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http) {
-            $this->addHeader('Host', $this->_generateHostHeader());
-        }
-
-        // set '/' instead of empty path rather than check later (see bug #8662)
-        if (empty($this->_url->path)) {
-            $this->_url->path = '/';
-        }
-    }
-
-   /**
-    * Returns the current request URL
-    *
-    * @return   string  Current request URL
-    * @access   public
-    */
-    function getUrl()
-    {
-        return empty($this->_url)? '': $this->_url->getUrl();
-    }
-
-    /**
-    * Sets a proxy to be used
-    *
-    * @param string     Proxy host
-    * @param int        Proxy port
-    * @param string     Proxy username
-    * @param string     Proxy password
-    * @access public
-    */
-    function setProxy($host, $port = 8080, $user = null, $pass = null)
-    {
-        $this->_proxy_host = $host;
-        $this->_proxy_port = $port;
-        $this->_proxy_user = $user;
-        $this->_proxy_pass = $pass;
-
-        if (!empty($user)) {
-            $this->addHeader('Proxy-Authorization', 'Basic ' . base64_encode($user . ':' . $pass));
-        }
-    }
-
-    /**
-    * Sets basic authentication parameters
-    *
-    * @param string     Username
-    * @param string     Password
-    */
-    function setBasicAuth($user, $pass)
-    {
-        $this->_user = $user;
-        $this->_pass = $pass;
-
-        $this->addHeader('Authorization', 'Basic ' . base64_encode($user . ':' . $pass));
-    }
-
-    /**
-    * Sets the method to be used, GET, POST etc.
-    *
-    * @param string     Method to use. Use the defined constants for this
-    * @access public
-    */
-    function setMethod($method)
-    {
-        $this->_method = $method;
-    }
-
-    /**
-    * Sets the HTTP version to use, 1.0 or 1.1
-    *
-    * @param string     Version to use. Use the defined constants for this
-    * @access public
-    */
-    function setHttpVer($http)
-    {
-        $this->_http = $http;
-    }
-
-    /**
-    * Adds a request header
-    *
-    * @param string     Header name
-    * @param string     Header value
-    * @access public
-    */
-    function addHeader($name, $value)
-    {
-        $this->_requestHeaders[strtolower($name)] = $value;
-    }
-
-    /**
-    * Removes a request header
-    *
-    * @param string     Header name to remove
-    * @access public
-    */
-    function removeHeader($name)
-    {
-        if (isset($this->_requestHeaders[strtolower($name)])) {
-            unset($this->_requestHeaders[strtolower($name)]);
-        }
-    }
-
-    /**
-    * Adds a querystring parameter
-    *
-    * @param string     Querystring parameter name
-    * @param string     Querystring parameter value
-    * @param bool       Whether the value is already urlencoded or not, default = not
-    * @access public
-    */
-    function addQueryString($name, $value, $preencoded = false)
-    {
-        $this->_url->addQueryString($name, $value, $preencoded);
-    }
-
-    /**
-    * Sets the querystring to literally what you supply
-    *
-    * @param string     The querystring data. Should be of the format foo=bar&x=y etc
-    * @param bool       Whether data is already urlencoded or not, default = already encoded
-    * @access public
-    */
-    function addRawQueryString($querystring, $preencoded = true)
-    {
-        $this->_url->addRawQueryString($querystring, $preencoded);
-    }
-
-    /**
-    * Adds postdata items
-    *
-    * @param string     Post data name
-    * @param string     Post data value
-    * @param bool       Whether data is already urlencoded or not, default = not
-    * @access public
-    */
-    function addPostData($name, $value, $preencoded = false)
-    {
-        if ($preencoded) {
-            $this->_postData[$name] = $value;
-        } else {
-            $this->_postData[$name] = $this->_arrayMapRecursive('urlencode', $value);
-        }
-    }
-
-   /**
-    * Recursively applies the callback function to the value
-    *
-    * @param    mixed   Callback function
-    * @param    mixed   Value to process
-    * @access   private
-    * @return   mixed   Processed value
-    */
-    function _arrayMapRecursive($callback, $value)
-    {
-        if (!is_array($value)) {
-            return call_user_func($callback, $value);
-        } else {
-            $map = array();
-            foreach ($value as $k => $v) {
-                $map[$k] = $this->_arrayMapRecursive($callback, $v);
-            }
-            return $map;
-        }
-    }
-
-   /**
-    * Adds a file to form-based file upload
-    *
-    * Used to emulate file upload via a HTML form. The method also sets
-    * Content-Type of HTTP request to 'multipart/form-data'.
-    *
-    * If you just want to send the contents of a file as the body of HTTP
-    * request you should use setBody() method.
-    *
-    * @access public
-    * @param  string    name of file-upload field
-    * @param  mixed     file name(s)
-    * @param  mixed     content-type(s) of file(s) being uploaded
-    * @return bool      true on success
-    * @throws PEAR_Error
-    */
-    function addFile($inputName, $fileName, $contentType = 'application/octet-stream')
-    {
-        if (!is_array($fileName) && !is_readable($fileName)) {
-            return PEAR::raiseError("File '{$fileName}' is not readable", HTTP_REQUEST_ERROR_FILE);
-        } elseif (is_array($fileName)) {
-            foreach ($fileName as $name) {
-                if (!is_readable($name)) {
-                    return PEAR::raiseError("File '{$name}' is not readable", HTTP_REQUEST_ERROR_FILE);
-                }
-            }
-        }
-        $this->addHeader('Content-Type', 'multipart/form-data');
-        $this->_postFiles[$inputName] = array(
-            'name' => $fileName,
-            'type' => $contentType
-        );
-        return true;
-    }
-
-    /**
-    * Adds raw postdata (DEPRECATED)
-    *
-    * @param string     The data
-    * @param bool       Whether data is preencoded or not, default = already encoded
-    * @access public
-    * @deprecated       deprecated since 1.3.0, method setBody() should be used instead
-    */
-    function addRawPostData($postdata, $preencoded = true)
-    {
-        $this->_body = $preencoded ? $postdata : urlencode($postdata);
-    }
-
-   /**
-    * Sets the request body (for POST, PUT and similar requests)
-    *
-    * @param    string  Request body
-    * @access   public
-    */
-    function setBody($body)
-    {
-        $this->_body = $body;
-    }
-
-    /**
-    * Clears any postdata that has been added (DEPRECATED).
-    *
-    * Useful for multiple request scenarios.
-    *
-    * @access public
-    * @deprecated deprecated since 1.2
-    */
-    function clearPostData()
-    {
-        $this->_postData = null;
-    }
-
-    /**
-    * Appends a cookie to "Cookie:" header
-    *
-    * @param string $name cookie name
-    * @param string $value cookie value
-    * @access public
-    */
-    function addCookie($name, $value)
-    {
-        $cookies = isset($this->_requestHeaders['cookie']) ? $this->_requestHeaders['cookie']. '; ' : '';
-        $this->addHeader('Cookie', $cookies . $name . '=' . $value);
-    }
-
-    /**
-    * Clears any cookies that have been added (DEPRECATED).
-    *
-    * Useful for multiple request scenarios
-    *
-    * @access public
-    * @deprecated deprecated since 1.2
-    */
-    function clearCookies()
-    {
-        $this->removeHeader('Cookie');
-    }
-
-    /**
-    * Sends the request
-    *
-    * @access public
-    * @param  bool   Whether to store response body in Response object property,
-    *                set this to false if downloading a LARGE file and using a Listener
-    * @return mixed  PEAR error on error, true otherwise
-    */
-    function sendRequest($saveBody = true)
-    {
-        if (!is_a($this->_url, 'Net_URL')) {
-            return PEAR::raiseError('No URL given', HTTP_REQUEST_ERROR_URL);
-        }
-
-        $host = isset($this->_proxy_host) ? $this->_proxy_host : $this->_url->host;
-        $port = isset($this->_proxy_port) ? $this->_proxy_port : $this->_url->port;
-
-        if (strcasecmp($this->_url->protocol, 'https') == 0) {
-            // Bug #14127, don't try connecting to HTTPS sites without OpenSSL
-            if (version_compare(PHP_VERSION, '4.3.0', '<') || !extension_loaded('openssl')) {
-                return PEAR::raiseError('Need PHP 4.3.0 or later with OpenSSL support for https:// requests',
-                                        HTTP_REQUEST_ERROR_URL);
-            } elseif (isset($this->_proxy_host)) {
-                return PEAR::raiseError('HTTPS proxies are not supported', HTTP_REQUEST_ERROR_PROXY);
-            }
-            $host = 'ssl://' . $host;
-        }
-
-        // magic quotes may fuck up file uploads and chunked response processing
-        $magicQuotes = ini_get('magic_quotes_runtime');
-        ini_set('magic_quotes_runtime', false);
-
-        // RFC 2068, section 19.7.1: A client MUST NOT send the Keep-Alive
-        // connection token to a proxy server...
-        if (isset($this->_proxy_host) && !empty($this->_requestHeaders['connection']) &&
-            'Keep-Alive' == $this->_requestHeaders['connection'])
-        {
-            $this->removeHeader('connection');
-        }
-
-        $keepAlive = (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http && empty($this->_requestHeaders['connection'])) ||
-                     (!empty($this->_requestHeaders['connection']) && 'Keep-Alive' == $this->_requestHeaders['connection']);
-        $sockets   = PEAR::getStaticProperty('HTTP_Request', 'sockets');
-        $sockKey   = $host . ':' . $port;
-        unset($this->_sock);
-
-        // There is a connected socket in the "static" property?
-        if ($keepAlive && !empty($sockets[$sockKey]) &&
-            !empty($sockets[$sockKey]->fp))
-        {
-            $this->_sock =& $sockets[$sockKey];
-            $err = null;
-        } else {
-            $this->_notify('connect');
-            $this->_sock = new Net_Socket();
-            $err = $this->_sock->connect($host, $port, null, $this->_timeout, $this->_socketOptions);
-        }
-        PEAR::isError($err) or $err = $this->_sock->write($this->_buildRequest());
-
-        if (!PEAR::isError($err)) {
-            if (!empty($this->_readTimeout)) {
-                $this->_sock->setTimeout($this->_readTimeout[0], $this->_readTimeout[1]);
-            }
-
-            $this->_notify('sentRequest');
-
-            // Read the response
-            $this->_response = new HTTP_Response($this->_sock, $this->_listeners);
-            $err = $this->_response->process(
-                $this->_saveBody && $saveBody,
-                HTTP_REQUEST_METHOD_HEAD != $this->_method
-            );
-
-            if ($keepAlive) {
-                $keepAlive = (isset($this->_response->_headers['content-length'])
-                              || (isset($this->_response->_headers['transfer-encoding'])
-                                  && strtolower($this->_response->_headers['transfer-encoding']) == 'chunked'));
-                if ($keepAlive) {
-                    if (isset($this->_response->_headers['connection'])) {
-                        $keepAlive = strtolower($this->_response->_headers['connection']) == 'keep-alive';
-                    } else {
-                        $keepAlive = 'HTTP/'.HTTP_REQUEST_HTTP_VER_1_1 == $this->_response->_protocol;
-                    }
-                }
-            }
-        }
-
-        ini_set('magic_quotes_runtime', $magicQuotes);
-
-        if (PEAR::isError($err)) {
-            return $err;
-        }
-
-        if (!$keepAlive) {
-            $this->disconnect();
-        // Store the connected socket in "static" property
-        } elseif (empty($sockets[$sockKey]) || empty($sockets[$sockKey]->fp)) {
-            $sockets[$sockKey] =& $this->_sock;
-        }
-
-        // Check for redirection
-        if (    $this->_allowRedirects
-            AND $this->_redirects <= $this->_maxRedirects
-            AND $this->getResponseCode() > 300
-            AND $this->getResponseCode() < 399
-            AND !empty($this->_response->_headers['location'])) {
-
-
-            $redirect = $this->_response->_headers['location'];
-
-            // Absolute URL
-            if (preg_match('/^https?:\/\//i', $redirect)) {
-                $this->_url = new Net_URL($redirect);
-                $this->addHeader('Host', $this->_generateHostHeader());
-            // Absolute path
-            } elseif ($redirect{0} == '/') {
-                $this->_url->path = $redirect;
-
-            // Relative path
-            } elseif (substr($redirect, 0, 3) == '../' OR substr($redirect, 0, 2) == './') {
-                if (substr($this->_url->path, -1) == '/') {
-                    $redirect = $this->_url->path . $redirect;
-                } else {
-                    $redirect = dirname($this->_url->path) . '/' . $redirect;
-                }
-                $redirect = Net_URL::resolvePath($redirect);
-                $this->_url->path = $redirect;
-
-            // Filename, no path
-            } else {
-                if (substr($this->_url->path, -1) == '/') {
-                    $redirect = $this->_url->path . $redirect;
-                } else {
-                    $redirect = dirname($this->_url->path) . '/' . $redirect;
-                }
-                $this->_url->path = $redirect;
-            }
-
-            $this->_redirects++;
-            return $this->sendRequest($saveBody);
-
-        // Too many redirects
-        } elseif ($this->_allowRedirects AND $this->_redirects > $this->_maxRedirects) {
-            return PEAR::raiseError('Too many redirects', HTTP_REQUEST_ERROR_REDIRECTS);
-        }
-
-        return true;
-    }
-
-    /**
-     * Disconnect the socket, if connected. Only useful if using Keep-Alive.
-     *
-     * @access public
-     */
-    function disconnect()
-    {
-        if (!empty($this->_sock) && !empty($this->_sock->fp)) {
-            $this->_notify('disconnect');
-            $this->_sock->disconnect();
-        }
-    }
-
-    /**
-    * Returns the response code
-    *
-    * @access public
-    * @return mixed     Response code, false if not set
-    */
-    function getResponseCode()
-    {
-        return isset($this->_response->_code) ? $this->_response->_code : false;
-    }
-
-    /**
-    * Returns the response reason phrase
-    *
-    * @access public
-    * @return mixed     Response reason phrase, false if not set
-    */
-    function getResponseReason()
-    {
-        return isset($this->_response->_reason) ? $this->_response->_reason : false;
-    }
-
-    /**
-    * Returns either the named header or all if no name given
-    *
-    * @access public
-    * @param string     The header name to return, do not set to get all headers
-    * @return mixed     either the value of $headername (false if header is not present)
-    *                   or an array of all headers
-    */
-    function getResponseHeader($headername = null)
-    {
-        if (!isset($headername)) {
-            return isset($this->_response->_headers)? $this->_response->_headers: array();
-        } else {
-            $headername = strtolower($headername);
-            return isset($this->_response->_headers[$headername]) ? $this->_response->_headers[$headername] : false;
-        }
-    }
-
-    /**
-    * Returns the body of the response
-    *
-    * @access public
-    * @return mixed     response body, false if not set
-    */
-    function getResponseBody()
-    {
-        return isset($this->_response->_body) ? $this->_response->_body : false;
-    }
-
-    /**
-    * Returns cookies set in response
-    *
-    * @access public
-    * @return mixed     array of response cookies, false if none are present
-    */
-    function getResponseCookies()
-    {
-        return isset($this->_response->_cookies) ? $this->_response->_cookies : false;
-    }
-
-    /**
-    * Builds the request string
-    *
-    * @access private
-    * @return string The request string
-    */
-    function _buildRequest()
-    {
-        $separator = ini_get('arg_separator.output');
-        ini_set('arg_separator.output', '&');
-        $querystring = ($querystring = $this->_url->getQueryString()) ? '?' . $querystring : '';
-        ini_set('arg_separator.output', $separator);
-
-        $host = isset($this->_proxy_host) ? $this->_url->protocol . '://' . $this->_url->host : '';
-        $port = (isset($this->_proxy_host) AND $this->_url->port != 80) ? ':' . $this->_url->port : '';
-        $path = $this->_url->path . $querystring;
-        $url  = $host . $port . $path;
-
-        if (!strlen($url)) {
-            $url = '/';
-        }
-
-        $request = $this->_method . ' ' . $url . ' HTTP/' . $this->_http . "\r\n";
-
-        if (in_array($this->_method, $this->_bodyDisallowed) ||
-            (0 == strlen($this->_body) && (HTTP_REQUEST_METHOD_POST != $this->_method ||
-             (empty($this->_postData) && empty($this->_postFiles)))))
-        {
-            $this->removeHeader('Content-Type');
-        } else {
-            if (empty($this->_requestHeaders['content-type'])) {
-                // Add default content-type
-                $this->addHeader('Content-Type', 'application/x-www-form-urlencoded');
-            } elseif ('multipart/form-data' == $this->_requestHeaders['content-type']) {
-                $boundary = 'HTTP_Request_' . md5(uniqid('request') . microtime());
-                $this->addHeader('Content-Type', 'multipart/form-data; boundary=' . $boundary);
-            }
-        }
-
-        // Request Headers
-        if (!empty($this->_requestHeaders)) {
-            foreach ($this->_requestHeaders as $name => $value) {
-                $canonicalName = implode('-', array_map('ucfirst', explode('-', $name)));
-                $request      .= $canonicalName . ': ' . $value . "\r\n";
-            }
-        }
-
-        // Method does not allow a body, simply add a final CRLF
-        if (in_array($this->_method, $this->_bodyDisallowed)) {
-
-            $request .= "\r\n";
-
-        // Post data if it's an array
-        } elseif (HTTP_REQUEST_METHOD_POST == $this->_method &&
-                  (!empty($this->_postData) || !empty($this->_postFiles))) {
-
-            // "normal" POST request
-            if (!isset($boundary)) {
-                $postdata = implode('&', array_map(
-                    create_function('$a', 'return $a[0] . \'=\' . $a[1];'),
-                    $this->_flattenArray('', $this->_postData)
-                ));
-
-            // multipart request, probably with file uploads
-            } else {
-                $postdata = '';
-                if (!empty($this->_postData)) {
-                    $flatData = $this->_flattenArray('', $this->_postData);
-                    foreach ($flatData as $item) {
-                        $postdata .= '--' . $boundary . "\r\n";
-                        $postdata .= 'Content-Disposition: form-data; name="' . $item[0] . '"';
-                        $postdata .= "\r\n\r\n" . urldecode($item[1]) . "\r\n";
-                    }
-                }
-                foreach ($this->_postFiles as $name => $value) {
-                    if (is_array($value['name'])) {
-                        $varname       = $name . ($this->_useBrackets? '[]': '');
-                    } else {
-                        $varname       = $name;
-                        $value['name'] = array($value['name']);
-                    }
-                    foreach ($value['name'] as $key => $filename) {
-                        $fp       = fopen($filename, 'r');
-                        $basename = basename($filename);
-                        $type     = is_array($value['type'])? @$value['type'][$key]: $value['type'];
-
-                        $postdata .= '--' . $boundary . "\r\n";
-                        $postdata .= 'Content-Disposition: form-data; name="' . $varname . '"; filename="' . $basename . '"';
-                        $postdata .= "\r\nContent-Type: " . $type;
-                        $postdata .= "\r\n\r\n" . fread($fp, filesize($filename)) . "\r\n";
-                        fclose($fp);
-                    }
-                }
-                $postdata .= '--' . $boundary . "--\r\n";
-            }
-            $request .= 'Content-Length: ' .
-                        (HTTP_REQUEST_MBSTRING? mb_strlen($postdata, 'iso-8859-1'): strlen($postdata)) .
-                        "\r\n\r\n";
-            $request .= $postdata;
-
-        // Explicitly set request body
-        } elseif (0 < strlen($this->_body)) {
-
-            $request .= 'Content-Length: ' .
-                        (HTTP_REQUEST_MBSTRING? mb_strlen($this->_body, 'iso-8859-1'): strlen($this->_body)) .
-                        "\r\n\r\n";
-            $request .= $this->_body;
-
-        // No body: send a Content-Length header nonetheless (request #12900),
-        // but do that only for methods that require a body (bug #14740)
-        } else {
-
-            if (in_array($this->_method, $this->_bodyRequired)) {
-                $request .= "Content-Length: 0\r\n";
-            }
-            $request .= "\r\n";
-        }
-
-        return $request;
-    }
-
-   /**
-    * Helper function to change the (probably multidimensional) associative array
-    * into the simple one.
-    *
-    * @param    string  name for item
-    * @param    mixed   item's values
-    * @return   array   array with the following items: array('item name', 'item value');
-    * @access   private
-    */
-    function _flattenArray($name, $values)
-    {
-        if (!is_array($values)) {
-            return array(array($name, $values));
-        } else {
-            $ret = array();
-            foreach ($values as $k => $v) {
-                if (empty($name)) {
-                    $newName = $k;
-                } elseif ($this->_useBrackets) {
-                    $newName = $name . '[' . $k . ']';
-                } else {
-                    $newName = $name;
-                }
-                $ret = array_merge($ret, $this->_flattenArray($newName, $v));
-            }
-            return $ret;
-        }
-    }
-
-
-   /**
-    * Adds a Listener to the list of listeners that are notified of
-    * the object's events
-    *
-    * Events sent by HTTP_Request object
-    * - 'connect': on connection to server
-    * - 'sentRequest': after the request was sent
-    * - 'disconnect': on disconnection from server
-    *
-    * Events sent by HTTP_Response object
-    * - 'gotHeaders': after receiving response headers (headers are passed in $data)
-    * - 'tick': on receiving a part of response body (the part is passed in $data)
-    * - 'gzTick': on receiving a gzip-encoded part of response body (ditto)
-    * - 'gotBody': after receiving the response body (passes the decoded body in $data if it was gzipped)
-    *
-    * @param    HTTP_Request_Listener   listener to attach
-    * @return   boolean                 whether the listener was successfully attached
-    * @access   public
-    */
-    function attach(&$listener)
-    {
-        if (!is_a($listener, 'HTTP_Request_Listener')) {
-            return false;
-        }
-        $this->_listeners[$listener->getId()] =& $listener;
-        return true;
-    }
-
-
-   /**
-    * Removes a Listener from the list of listeners
-    *
-    * @param    HTTP_Request_Listener   listener to detach
-    * @return   boolean                 whether the listener was successfully detached
-    * @access   public
-    */
-    function detach(&$listener)
-    {
-        if (!is_a($listener, 'HTTP_Request_Listener') ||
-            !isset($this->_listeners[$listener->getId()])) {
-            return false;
-        }
-        unset($this->_listeners[$listener->getId()]);
-        return true;
-    }
-
-
-   /**
-    * Notifies all registered listeners of an event.
-    *
-    * @param    string  Event name
-    * @param    mixed   Additional data
-    * @access   private
-    * @see      HTTP_Request::attach()
-    */
-    function _notify($event, $data = null)
-    {
-        foreach (array_keys($this->_listeners) as $id) {
-            $this->_listeners[$id]->update($this, $event, $data);
-        }
-    }
-}
-
-
-/**
- * Response class to complement the Request class
- *
- * @category    HTTP
- * @package     HTTP_Request
- * @author      Richard Heyes <richard@phpguru.org>
- * @author      Alexey Borzov <avb@php.net>
- * @version     Release: 1.4.4
- */
-class HTTP_Response
-{
-    /**
-    * Socket object
-    * @var Net_Socket
-    */
-    var $_sock;
-
-    /**
-    * Protocol
-    * @var string
-    */
-    var $_protocol;
-
-    /**
-    * Return code
-    * @var string
-    */
-    var $_code;
-
-    /**
-    * Response reason phrase
-    * @var string
-    */
-    var $_reason;
-
-    /**
-    * Response headers
-    * @var array
-    */
-    var $_headers;
-
-    /**
-    * Cookies set in response
-    * @var array
-    */
-    var $_cookies;
-
-    /**
-    * Response body
-    * @var string
-    */
-    var $_body = '';
-
-   /**
-    * Used by _readChunked(): remaining length of the current chunk
-    * @var string
-    */
-    var $_chunkLength = 0;
-
-   /**
-    * Attached listeners
-    * @var array
-    */
-    var $_listeners = array();
-
-   /**
-    * Bytes left to read from message-body
-    * @var null|int
-    */
-    var $_toRead;
-
-    /**
-    * Constructor
-    *
-    * @param  Net_Socket    socket to read the response from
-    * @param  array         listeners attached to request
-    */
-    function __construct(&$sock, &$listeners)
-    {
-        $this->_sock      =& $sock;
-        $this->_listeners =& $listeners;
-    }
-
-
-   /**
-    * Processes a HTTP response
-    *
-    * This extracts response code, headers, cookies and decodes body if it
-    * was encoded in some way
-    *
-    * @access public
-    * @param  bool      Whether to store response body in object property, set
-    *                   this to false if downloading a LARGE file and using a Listener.
-    *                   This is assumed to be true if body is gzip-encoded.
-    * @param  bool      Whether the response can actually have a message-body.
-    *                   Will be set to false for HEAD requests.
-    * @throws PEAR_Error
-    * @return mixed     true on success, PEAR_Error in case of malformed response
-    */
-    function process($saveBody = true, $canHaveBody = true)
-    {
-        do {
-            $line = $this->_sock->readLine();
-            if (!preg_match('!^(HTTP/\d\.\d) (\d{3})(?: (.+))?!', $line, $s)) {
-                return PEAR::raiseError('Malformed response', HTTP_REQUEST_ERROR_RESPONSE);
-            } else {
-                $this->_protocol = $s[1];
-                $this->_code     = intval($s[2]);
-                $this->_reason   = empty($s[3])? null: $s[3];
-            }
-            while ('' !== ($header = $this->_sock->readLine())) {
-                $this->_processHeader($header);
-            }
-        } while (100 == $this->_code);
-
-        $this->_notify('gotHeaders', $this->_headers);
-
-        // RFC 2616, section 4.4:
-        // 1. Any response message which "MUST NOT" include a message-body ...
-        // is always terminated by the first empty line after the header fields
-        // 3. ... If a message is received with both a
-        // Transfer-Encoding header field and a Content-Length header field,
-        // the latter MUST be ignored.
-        $canHaveBody = $canHaveBody && $this->_code >= 200 &&
-                       $this->_code != 204 && $this->_code != 304;
-
-        // If response body is present, read it and decode
-        $chunked = isset($this->_headers['transfer-encoding']) && ('chunked' == $this->_headers['transfer-encoding']);
-        $gzipped = isset($this->_headers['content-encoding']) && ('gzip' == $this->_headers['content-encoding']);
-        $hasBody = false;
-        if ($canHaveBody && ($chunked || !isset($this->_headers['content-length']) ||
-                0 != $this->_headers['content-length']))
-        {
-            if ($chunked || !isset($this->_headers['content-length'])) {
-                $this->_toRead = null;
-            } else {
-                $this->_toRead = $this->_headers['content-length'];
-            }
-            while (!$this->_sock->eof() && (is_null($this->_toRead) || 0 < $this->_toRead)) {
-                if ($chunked) {
-                    $data = $this->_readChunked();
-                } elseif (is_null($this->_toRead)) {
-                    $data = $this->_sock->read(4096);
-                } else {
-                    $data = $this->_sock->read(min(4096, $this->_toRead));
-                    $this->_toRead -= HTTP_REQUEST_MBSTRING? mb_strlen($data, 'iso-8859-1'): strlen($data);
-                }
-                if ('' == $data && (!$this->_chunkLength || $this->_sock->eof())) {
-                    break;
-                } else {
-                    $hasBody = true;
-                    if ($saveBody || $gzipped) {
-                        $this->_body .= $data;
-                    }
-                    $this->_notify($gzipped? 'gzTick': 'tick', $data);
-                }
-            }
-        }
-
-        if ($hasBody) {
-            // Uncompress the body if needed
-            if ($gzipped) {
-                $body = $this->_decodeGzip($this->_body);
-                if (PEAR::isError($body)) {
-                    return $body;
-                }
-                $this->_body = $body;
-                $this->_notify('gotBody', $this->_body);
-            } else {
-                $this->_notify('gotBody');
-            }
-        }
-        return true;
-    }
-
-
-   /**
-    * Processes the response header
-    *
-    * @access private
-    * @param  string    HTTP header
-    */
-    function _processHeader($header)
-    {
-        if (false === strpos($header, ':')) {
-            return;
-        }
-        list($headername, $headervalue) = explode(':', $header, 2);
-        $headername  = strtolower($headername);
-        $headervalue = ltrim($headervalue);
-
-        if ('set-cookie' != $headername) {
-            if (isset($this->_headers[$headername])) {
-                $this->_headers[$headername] .= ',' . $headervalue;
-            } else {
-                $this->_headers[$headername]  = $headervalue;
-            }
-        } else {
-            $this->_parseCookie($headervalue);
-        }
-    }
-
-
-   /**
-    * Parse a Set-Cookie header to fill $_cookies array
-    *
-    * @access private
-    * @param  string    value of Set-Cookie header
-    */
-    function _parseCookie($headervalue)
-    {
-        $cookie = array(
-            'expires' => null,
-            'domain'  => null,
-            'path'    => null,
-            'secure'  => false
-        );
-
-        // Only a name=value pair
-        if (!strpos($headervalue, ';')) {
-            $pos = strpos($headervalue, '=');
-            $cookie['name']  = trim(substr($headervalue, 0, $pos));
-            $cookie['value'] = trim(substr($headervalue, $pos + 1));
-
-        // Some optional parameters are supplied
-        } else {
-            $elements = explode(';', $headervalue);
-            $pos = strpos($elements[0], '=');
-            $cookie['name']  = trim(substr($elements[0], 0, $pos));
-            $cookie['value'] = trim(substr($elements[0], $pos + 1));
-
-            for ($i = 1; $i < count($elements); $i++) {
-                if (false === strpos($elements[$i], '=')) {
-                    $elName  = trim($elements[$i]);
-                    $elValue = null;
-                } else {
-                    list ($elName, $elValue) = array_map('trim', explode('=', $elements[$i]));
-                }
-                $elName = strtolower($elName);
-                if ('secure' == $elName) {
-                    $cookie['secure'] = true;
-                } elseif ('expires' == $elName) {
-                    $cookie['expires'] = str_replace('"', '', $elValue);
-                } elseif ('path' == $elName || 'domain' == $elName) {
-                    $cookie[$elName] = urldecode($elValue);
-                } else {
-                    $cookie[$elName] = $elValue;
-                }
-            }
-        }
-        $this->_cookies[] = $cookie;
-    }
-
-
-   /**
-    * Read a part of response body encoded with chunked Transfer-Encoding
-    *
-    * @access private
-    * @return string
-    */
-    function _readChunked()
-    {
-        // at start of the next chunk?
-        if (0 == $this->_chunkLength) {
-            $line = $this->_sock->readLine();
-            if (preg_match('/^([0-9a-f]+)/i', $line, $matches)) {
-                $this->_chunkLength = hexdec($matches[1]);
-                // Chunk with zero length indicates the end
-                if (0 == $this->_chunkLength) {
-                    $this->_sock->readLine(); // make this an eof()
-                    return '';
-                }
-            } else {
-                return '';
-            }
-        }
-        $data = $this->_sock->read($this->_chunkLength);
-        $this->_chunkLength -= HTTP_REQUEST_MBSTRING? mb_strlen($data, 'iso-8859-1'): strlen($data);
-        if (0 == $this->_chunkLength) {
-            $this->_sock->readLine(); // Trailing CRLF
-        }
-        return $data;
-    }
-
-
-   /**
-    * Notifies all registered listeners of an event.
-    *
-    * @param    string  Event name
-    * @param    mixed   Additional data
-    * @access   private
-    * @see HTTP_Request::_notify()
-    */
-    function _notify($event, $data = null)
-    {
-        foreach (array_keys($this->_listeners) as $id) {
-            $this->_listeners[$id]->update($this, $event, $data);
-        }
-    }
-
-
-   /**
-    * Decodes the message-body encoded by gzip
-    *
-    * The real decoding work is done by gzinflate() built-in function, this
-    * method only parses the header and checks data for compliance with
-    * RFC 1952
-    *
-    * @access   private
-    * @param    string  gzip-encoded data
-    * @return   string  decoded data
-    */
-    function _decodeGzip($data)
-    {
-        if (HTTP_REQUEST_MBSTRING) {
-            $oldEncoding = mb_internal_encoding();
-            mb_internal_encoding('iso-8859-1');
-        }
-        $length = strlen($data);
-        // If it doesn't look like gzip-encoded data, don't bother
-        if (18 > $length || strcmp(substr($data, 0, 2), "\x1f\x8b")) {
-            return $data;
-        }
-        $method = ord(substr($data, 2, 1));
-        if (8 != $method) {
-            return PEAR::raiseError('_decodeGzip(): unknown compression method', HTTP_REQUEST_ERROR_GZIP_METHOD);
-        }
-        $flags = ord(substr($data, 3, 1));
-        if ($flags & 224) {
-            return PEAR::raiseError('_decodeGzip(): reserved bits are set', HTTP_REQUEST_ERROR_GZIP_DATA);
-        }
-
-        // header is 10 bytes minimum. may be longer, though.
-        $headerLength = 10;
-        // extra fields, need to skip 'em
-        if ($flags & 4) {
-            if ($length - $headerLength - 2 < 8) {
-                return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
-            }
-            $extraLength = unpack('v', substr($data, 10, 2));
-            if ($length - $headerLength - 2 - $extraLength[1] < 8) {
-                return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
-            }
-            $headerLength += $extraLength[1] + 2;
-        }
-        // file name, need to skip that
-        if ($flags & 8) {
-            if ($length - $headerLength - 1 < 8) {
-                return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
-            }
-            $filenameLength = strpos(substr($data, $headerLength), chr(0));
-            if (false === $filenameLength || $length - $headerLength - $filenameLength - 1 < 8) {
-                return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
-            }
-            $headerLength += $filenameLength + 1;
-        }
-        // comment, need to skip that also
-        if ($flags & 16) {
-            if ($length - $headerLength - 1 < 8) {
-                return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
-            }
-            $commentLength = strpos(substr($data, $headerLength), chr(0));
-            if (false === $commentLength || $length - $headerLength - $commentLength - 1 < 8) {
-                return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
-            }
-            $headerLength += $commentLength + 1;
-        }
-        // have a CRC for header. let's check
-        if ($flags & 1) {
-            if ($length - $headerLength - 2 < 8) {
-                return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
-            }
-            $crcReal   = 0xffff & crc32(substr($data, 0, $headerLength));
-            $crcStored = unpack('v', substr($data, $headerLength, 2));
-            if ($crcReal != $crcStored[1]) {
-                return PEAR::raiseError('_decodeGzip(): header CRC check failed', HTTP_REQUEST_ERROR_GZIP_CRC);
-            }
-            $headerLength += 2;
-        }
-        // unpacked data CRC and size at the end of encoded data
-        $tmp = unpack('V2', substr($data, -8));
-        $dataCrc  = $tmp[1];
-        $dataSize = $tmp[2];
-
-        // finally, call the gzinflate() function
-        // don't pass $dataSize to gzinflate, see bugs #13135, #14370
-        $unpacked = gzinflate(substr($data, $headerLength, -8));
-        if (false === $unpacked) {
-            return PEAR::raiseError('_decodeGzip(): gzinflate() call failed', HTTP_REQUEST_ERROR_GZIP_READ);
-        } elseif ($dataSize != strlen($unpacked)) {
-            return PEAR::raiseError('_decodeGzip(): data size check failed', HTTP_REQUEST_ERROR_GZIP_READ);
-        } elseif ((0xffffffff & $dataCrc) != (0xffffffff & crc32($unpacked))) {
-            return PEAR::raiseError('_decodeGzip(): data CRC check failed', HTTP_REQUEST_ERROR_GZIP_CRC);
-        }
-        if (HTTP_REQUEST_MBSTRING) {
-            mb_internal_encoding($oldEncoding);
-        }
-        return $unpacked;
-    }
-} // End class HTTP_Response
-?>
diff --git a/civicrm/packages/HTTP/Request/Listener.php b/civicrm/packages/HTTP/Request/Listener.php
deleted file mode 100644
index 7bc837cb8e7354cc01c5281a162d2bf974d28865..0000000000000000000000000000000000000000
--- a/civicrm/packages/HTTP/Request/Listener.php
+++ /dev/null
@@ -1,106 +0,0 @@
-<?php
-/**
- * Listener for HTTP_Request and HTTP_Response objects
- *
- * PHP versions 4 and 5
- * 
- * LICENSE:
- *
- * Copyright (c) 2002-2007, Richard Heyes
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * o Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * o 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.
- * o The names of the authors may not 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.
- *
- * @category    HTTP
- * @package     HTTP_Request
- * @author      Alexey Borzov <avb@php.net>
- * @copyright   2002-2007 Richard Heyes
- * @license     http://opensource.org/licenses/bsd-license.php New BSD License
- * @version     CVS: $Id: Listener.php,v 1.3 2007/05/18 10:33:31 avb Exp $
- * @link        http://pear.php.net/package/HTTP_Request/ 
- */
-
-/**
- * Listener for HTTP_Request and HTTP_Response objects
- *
- * This class implements the Observer part of a Subject-Observer
- * design pattern.
- *
- * @category    HTTP
- * @package     HTTP_Request
- * @author      Alexey Borzov <avb@php.net>
- * @version     Release: 1.4.4
- */
-class HTTP_Request_Listener 
-{
-   /**
-    * A listener's identifier
-    * @var string
-    */
-    var $_id;
-
-   /**
-    * Constructor, sets the object's identifier
-    *
-    * @access public
-    */
-    function __construct()
-    {
-        $this->_id = md5(uniqid('http_request_', 1));
-    }
-
-
-   /**
-    * Returns the listener's identifier
-    *
-    * @access public
-    * @return string
-    */
-    function getId()
-    {
-        return $this->_id;
-    }
-
-
-   /**
-    * This method is called when Listener is notified of an event
-    *
-    * @access   public
-    * @param    object  an object the listener is attached to
-    * @param    string  Event name
-    * @param    mixed   Additional data
-    * @abstract
-    */
-    function update(&$subject, $event, $data = null)
-    {
-        echo "Notified of event: '$event'\n";
-        if (null !== $data) {
-            echo "Additional data: ";
-            var_dump($data);
-        }
-    }
-}
-?>
diff --git a/civicrm/packages/VERSIONS.php b/civicrm/packages/VERSIONS.php
index dfe70e54f2797bb930d4e1e3c9cf918bd48d95ed..bb33761e99a49cfb8ccf4b19eb47a1902d7e2afb 100644
--- a/civicrm/packages/VERSIONS.php
+++ b/civicrm/packages/VERSIONS.php
@@ -114,7 +114,6 @@
  * HTML_QuickForm_advmultiselect 1.5.1      BSD 3-cl.      local changes
  * HTML_QuickForm_Controller     1.0.9      PHP 3          local changes
  * HTML_Template_IT              1.2.1      BSD 3-cl.
- * HTTP_Request                  1.4.4      BSD 3-cl.
  * Log                           1.11.5     X11
  * Mail                          1.2.0      PHP 2          local changes
  * Mail_Mime                     1.8.0      BSD 3-cl.      local changes
diff --git a/civicrm/release-notes.md b/civicrm/release-notes.md
index b9c69e516ae6adb52a398ecbc3878649a9973f09..e405b785177f1d96bb1b7abcde762cdd402c61a2 100644
--- a/civicrm/release-notes.md
+++ b/civicrm/release-notes.md
@@ -15,9 +15,20 @@ Other resources for identifying changes are:
     * https://github.com/civicrm/civicrm-joomla
     * https://github.com/civicrm/civicrm-wordpress
 
+## CiviCRM 5.27.0
+
+Released July 1, 2020
+
+- **[Synopsis](release-notes/5.27.0.md#synopsis)**
+- **[Features](release-notes/5.27.0.md#features)**
+- **[Bugs resolved](release-notes/5.27.0.md#bugs)**
+- **[Miscellany](release-notes/5.27.0.md#misc)**
+- **[Credits](release-notes/5.27.0.md#credits)**
+- **[Feedback](release-notes/5.27.0.md#feedback)**
+
 ## CiviCRM 5.26.2
 
-Released June 16, 2020
+Released June 15, 2020
 
 - **[Synopsis](release-notes/5.26.2.md#synopsis)**
 - **[Bugs resolved](release-notes/5.26.2.md#bugs)**
diff --git a/civicrm/release-notes/5.26.2.md b/civicrm/release-notes/5.26.2.md
index 7e73ac5d14338872ffd0fd0c400df4825452071a..6c66442c2742241265731991d70b72fa1cf642f0 100644
--- a/civicrm/release-notes/5.26.2.md
+++ b/civicrm/release-notes/5.26.2.md
@@ -1,6 +1,6 @@
 # CiviCRM 5.26.2
 
-Released June 16, 2020
+Released June 15, 2020
 
 - **[Synopsis](#synopsis)**
 - **[Bugs resolved](#bugs)**
@@ -35,7 +35,7 @@ This release was developed by the following authors and reviewers:
 
 Wikimedia Foundation - Eileen McNaughton; Tapas Datta; Semper IT - Karin Gerritsen; 
 MJCO - Mikey O'Toole; MJW Consulting - Matt Wire; Megaphone Technology Consulting - Jon Goldberg; 
-JMA Consulting - Seamus Lee; Francesc Bassas i Bullich; CiviCRM - Coleman Watts, Tim Otten
+Francesc Bassas i Bullich; JMA Consulting - Seamus Lee; CiviCRM - Coleman Watts, Tim Otten
 
 ## <a name="feedback"></a>Feedback
 
diff --git a/civicrm/release-notes/5.27.0.md b/civicrm/release-notes/5.27.0.md
new file mode 100644
index 0000000000000000000000000000000000000000..b2f7bd71b954be8e1f35b8b56203b3b03b39ae1d
--- /dev/null
+++ b/civicrm/release-notes/5.27.0.md
@@ -0,0 +1,885 @@
+# CiviCRM 5.27.0
+
+Released July 1, 2020
+
+- **[Synopsis](#synopsis)**
+- **[Features](#features)**
+- **[Bugs resolved](#bugs)**
+- **[Miscellany](#misc)**
+- **[Credits](#credits)**
+- **[Feedback](#feedback)**
+
+## <a name="synopsis"></a>Synopsis
+
+| *Does this version...?*                                         |         |
+|:--------------------------------------------------------------- |:-------:|
+| Fix security vulnerabilities?                                   |   no    |
+| **Change the database schema?**                                 | **yes** |
+| **Alter the API?**                                              | **yes** |
+| **Require attention to configuration options?**                 | **yes** |
+| **Fix problems installing or upgrading to a previous version?** | **yes** |
+| **Introduce features?**                                         | **yes** |
+| **Fix bugs?**                                                   | **yes** |
+
+## <a name="features"></a>Features
+
+### Core CiviCRM
+
+- **Warn users who lack PHP-Intl extension (Work Towards
+  [dev/translation#48](https://lab.civicrm.org/dev/translation/-/issues/48):
+  [17668](https://github.com/civicrm/civicrm-core/pull/17668), [17728](https://github.com/civicrm/civicrm-core/pull/17728))**
+
+  A system check warning will now appear on sites that lack the PHP
+  Internationalization extension.  This is in preparation for changes that will
+  appear in CiviCRM 5.28 and require the extension.
+
+- **Advanced Search - Allow searching by first/last name; pass values from
+  quicksearch ([dev/core#1722](https://lab.civicrm.org/dev/core/-/issues/1722):
+  [17118](https://github.com/civicrm/civicrm-core/pull/17118))**
+
+  This makes separate First Name and Last Name fields available on the Advanced
+  Search form, and it ensures quick search filters are passed to the appropriate
+  field.
+
+- **Add more search options
+  ([17372](https://github.com/civicrm/civicrm-core/pull/17372))**
+
+  Improves the search form for individuals in contact reference fields by adding
+  fields for Employer Name, First Name, Last Name, and Nick Name.
+
+- **End of life planning for MySQL server 5.0 - 5.6
+  ([dev/core#1681](https://lab.civicrm.org/dev/core/-/issues/1681):
+  [17518](https://github.com/civicrm/civicrm-core/pull/17518) and
+  [17496](https://github.com/civicrm/civicrm-core/pull/17496))**
+
+  Sites running MySQL 5.5 and below will have a pre-upgrade message and a system
+  check warning to say that CiviCRM 5.28 will require MySQL 5.6 or MariaDB 10.1.
+
+- **APIv4 - Improve pseudoconstant suffix support
+  ([17368](https://github.com/civicrm/civicrm-core/pull/17368) and
+  [17482](https://github.com/civicrm/civicrm-core/pull/17482))**
+
+  APIv4 allows pseudoconstant fields to be specified with the pseudoconstant
+  name followed by a suffix, such as `activity_type_id:label`.  This now works
+  for retrieval and sort.
+
+- **APIv4 - Support custom entity joins
+  ([17394](https://github.com/civicrm/civicrm-core/pull/17394))**
+
+  Improves APIv4 so that it supports implicit and explicit joins onto custom
+  entities.
+
+- **Make reCAPTCHA settings clearer
+  ([17426](https://github.com/civicrm/civicrm-core/pull/17426))**
+
+  The help text for reCAPTCHA settings now specifies the type of reCAPTCHA
+  supported.
+
+- **Configurable logfile size and hashing
+  ([17192](https://github.com/civicrm/civicrm-core/pull/17192))**
+
+  Allows a system administrator to give logs a predictable name, allowing them
+  to be more easily ingested into other tools, and be handled by external log
+  rotation tools.
+
+- **Allow for installation on Symfony 4.4
+  ([17380](https://github.com/civicrm/civicrm-core/pull/17380))**
+
+  Makes it so CiviCRM plays nicely with Symfony 4.4.
+
+- **Add domain tokens to the Message Template admin interface
+  ([17388](https://github.com/civicrm/civicrm-core/pull/17388))**
+
+  This adds the domain tokens (the domain organization name, address, email, and
+  phone) to the available listed tokens for editing message templates.
+
+- **Hide icon elements from screen readers and add handling for
+  screen-reader-only text
+  ([17318](https://github.com/civicrm/civicrm-core/pull/17318),
+  [17293](https://github.com/civicrm/civicrm-core/pull/17293),
+  [17294](https://github.com/civicrm/civicrm-core/pull/17294), and
+  [17310](https://github.com/civicrm/civicrm-core/pull/17310))**
+
+  CiviCRM uses empty `<i>` elements to provide most icons.  Screen readers,
+  which won't render the visual icon anyway, will either skip over an empty
+  element or announce it in a way that would be confusing.  This makes it
+  explicit that screen readers and other ARIA devices should skip them.
+
+  Meanwhile, if the icon is the only way a piece of information is displayed, a
+  screen reader user won't get that information.  This adds a `sr-only` class
+  that makes the element invisible on screen without being ignored by a screen
+  reader.
+
+  Screen reader text is added for the field option wrench icons, relationship
+  permissions, and membership auto-renew status.
+
+- **Add icon helper functions and remove most uses of check.gif
+  ([17279](https://github.com/civicrm/civicrm-core/pull/17279) and
+  [17289](https://github.com/civicrm/civicrm-core/pull/17289))**
+
+  This adds helper functions in PHP and Smarty to render consistent icons with a
+  minimum of code.  It also replaces most instances of the green checkmark image
+  with a checkmark icon.
+
+- **Separate icon markup from labels and add JS icon helper
+  ([17319](https://github.com/civicrm/civicrm-core/pull/17319) and
+  [17285](https://github.com/civicrm/civicrm-core/pull/17285))**
+
+  These changes improve `CRM_Core_Action::formLink()`, `hook_civicrm_links`, and
+  certain listing pages so that HTML icon markup is added at the point of
+  display rather than during the processing of values.
+
+  It also adds a helper function in Javascript similar to those in PHP and
+  Smarty to render icons.
+
+- **crmButton: Support icon=0 for no icon
+  ([17303](https://github.com/civicrm/civicrm-core/pull/17303))**
+
+  Makes it possible to create a button without an icon using {crmButton}.
+
+- **Replace stop-icon.png with fa-ban over a relevant icon for privacy
+  ([17283](https://github.com/civicrm/civicrm-core/pull/17283) and
+  [17307](https://github.com/civicrm/civicrm-core/pull/17307))**
+
+  The communication preference icons are standardized and more specific than the
+  old stop sign.
+
+- **Replace image icons with Font Awesome icons
+  ([17296]([17280](https://github.com/civicrm/civicrm-core/pull/17280),
+  [17281](https://github.com/civicrm/civicrm-core/pull/17281),
+  [17282](https://github.com/civicrm/civicrm-core/pull/17282),
+  [17295](https://github.com/civicrm/civicrm-core/pull/17295),
+  https://github.com/civicrm/civicrm-core/pull/17296),
+  [17297](https://github.com/civicrm/civicrm-core/pull/17297))**
+
+  Nearly all image icons have been replaced with the corresponding icons from
+  the Font Awesome library.
+
+- **Simplify admin console and remove superfluous icons
+  ([17284](https://github.com/civicrm/civicrm-core/pull/17284))**
+
+  The Administration console has been redesigned to have a simpler layout,
+  display more information, dynamically include all enabled components, display
+  responsively on small screens, and remove a slew of 15-year-old image icons.
+
+- **Dedupe performance - hard-remove financial_item from list of dymnamic  refs
+  to contact table
+  ([17567](https://github.com/civicrm/civicrm-core/pull/17567))**
+
+  This improves performance when deduping by no longer querying the financial
+  item table.
+
+- **Trigger fatalErrorHandler for PEAR errors and for logs with severity ERROR
+  or higher ([17277](https://github.com/civicrm/civicrm-core/pull/17277))**
+
+  This allows extensions that implement `$config->fatalErrorHandler` to react to
+  a wider variety of errors that CiviCRM may generate.
+
+### CiviContribute
+
+- **Wrap separators in price field in spans with classes
+  ([dev/user-interface#18](https://lab.civicrm.org/dev/user-interface/-/issues/18):
+  [17078](https://github.com/civicrm/civicrm-core/pull/17078))**
+
+  Colons separating pre- and post-field help text from price fields are now
+  wrapped in `<span>` elements.
+
+- **Allow to search on contribution ID
+  ([dev/core#1580](https://lab.civicrm.org/dev/core/-/issues/1580):
+  [16484](https://github.com/civicrm/civicrm-core/pull/16484))**
+
+  This improves the "Find Contributions" search form by adding a field to search
+  by Contribution ID.
+
+- **APIv4 - Add "Price"-related entities
+  ([17363](https://github.com/civicrm/civicrm-core/pull/17363))**
+
+  Priceset, PriceField, and PriceOption entities are added to APIv4.
+
+- **APIv4 - Add ContributionRecur entity
+  ([17299](https://github.com/civicrm/civicrm-core/pull/17299))**
+
+  Adds Contribution Recur to APIv4.
+
+### CiviMail
+
+- **Allow click-through and open tracking to be enabled/disabled by default
+  ([17291](https://github.com/civicrm/civicrm-core/pull/17291))**
+
+  Adds a setting to CiviMail which allows for setting the click-through and open
+  tracking defaults.
+
+- **Extend content array of hook_civicrm_alterMailContent
+  ([dev/core#1620](https://lab.civicrm.org/dev/core/-/issues/1620):
+  [16629](https://github.com/civicrm/civicrm-core/pull/16629))**
+
+  Adds campaign_id to template for `alterMailContent` hook.
+
+- **Migrate CiviMail "extern" scripts to conventional routes
+  ([17312](https://github.com/civicrm/civicrm-core/pull/17312))**
+
+  Updates CiviMail's default behavior to replace `extern/open.php` and
+  `extern/url.php` with `civicrm/mailing/open` and `civicrm/mailing/url`.
+
+- **Make `is_archived` work as a url-search parameter
+  ([17328](https://github.com/civicrm/civicrm-core/pull/17328))**
+
+  The Find Mailings URL can now have `is_archived` as a parameter to only find
+  archived mailings.
+
+### CiviMember
+
+- **Add MembershipType to Api4
+  ([17448](https://github.com/civicrm/civicrm-core/pull/17448))**
+
+  Adds a Membership Type entity to APIv4.
+
+### Drupal Integration
+
+- **CiviCRM menu toggle "adjust menu position" customisable
+  ([dev/core#1757](https://lab.civicrm.org/dev/core/-/issues/1757):
+  [17362](https://github.com/civicrm/civicrm-core/pull/17362))**
+
+  This hides the "adjust menu position" icon in the CiviCRM Menu for non admin
+  users to avoid confusion for users who do not have access to the Drupal admin
+  menu.
+
+- **Provide "Calendar" module with an end-date when a duration is provided
+  ([601](https://github.com/civicrm/civicrm-drupal/pull/601))**
+
+  Improves display of activities in calendar views.
+
+## <a name="bugs"></a>Bugs resolved
+
+### Core CiviCRM
+
+- **CRM_Utils_Time::setTime and getTime() and their use in unit tests
+  ([dev/core#1781](https://lab.civicrm.org/dev/core/-/issues/1781):
+  [17414](https://github.com/civicrm/civicrm-core/pull/17414))**
+
+  This allows the CRM_Utils_Time functions used in unit tests to use TIME_FUNC
+  to better isolate timing bugs.
+
+- **Confusing popups when importing activities
+  ([dev/user-interface#17](https://lab.civicrm.org/dev/user-interface/-/issues/17):
+  [17067](https://github.com/civicrm/civicrm-core/pull/17067))**
+
+  This fixes a bug where the confirmation popup would appear when hitting the
+  back/cancel buttons, not just the import button, on most import forms.
+
+- **Installing CiviCRM in another language, with civicrm-setup
+  ([dev/translation#39](https://lab.civicrm.org/dev/translation/-/issues/39):
+  [17244](https://github.com/civicrm/civicrm-core/pull/17244))**
+
+  Fixes a bug when installing CiviCRM in another language with civicrm-setup.
+
+- **Replace all instances of CRM_Core_Fatal with throw new CRM_Core_Exception
+  ([dev/core#560](https://lab.civicrm.org/dev/core/-/issues/560):
+  [17421](https://github.com/civicrm/civicrm-core/pull/17421),
+  [17343](https://github.com/civicrm/civicrm-core/pull/17343),
+  [17336](https://github.com/civicrm/civicrm-core/pull/17336) and
+  [17341](https://github.com/civicrm/civicrm-core/pull/17341))**
+
+- **CiviCRM 5.13.4 - smart groups with contact subtypes: subtype is not in
+  Contact Type field on edit criteria mode
+  ([dev/core#991](https://lab.civicrm.org/dev/core/-/issues/991):
+  [16541](https://github.com/civicrm/civicrm-core/pull/16541))**
+
+  Fixes contact subtypes not being carried over when editing a smart group
+  created in Advanced Search.
+
+- **Unable to import relationship
+  ([dev/core#1731](https://lab.civicrm.org/dev/core/-/issues/1731):
+  [17163](https://github.com/civicrm/civicrm-core/pull/17163))**
+
+  Ensures that importing relationships works as expected when the same field is
+  to be imported for the main imported contact and a related contact.
+
+- **Apostrophes in richtext fields don't get exported properly in CiviReport
+  ([dev/core#1743](https://lab.civicrm.org/dev/core/-/issues/1743):
+  [17260](https://github.com/civicrm/civicrm-core/pull/17260))**
+
+- **Statically added members of a smart group are not found
+  ([dev/core#1745](https://lab.civicrm.org/dev/core/-/issues/1745):
+  [17419](https://github.com/civicrm/civicrm-core/pull/17419))**
+
+- **Deleting an activity with attachments doesn't delete the file or entries in
+  civicrm_file/civicrm_entity_file
+  ([dev/core#1753](https://lab.civicrm.org/dev/core/-/issues/1753):
+  [17298](https://github.com/civicrm/civicrm-core/pull/17298))**
+
+- **Deselected checkbox settings are not saved
+  ([dev/core#1794](https://lab.civicrm.org/dev/core/-/issues/1794):
+  [17493](https://github.com/civicrm/civicrm-core/pull/17493))**
+
+  Ensures deselected values are saved on custom field settings forms.
+
+- **Added activity types don't appear in the type dropdown on New Activity
+  ([dev/core#1822](https://lab.civicrm.org/dev/core/-/issues/1822):
+  [17625](https://github.com/civicrm/civicrm-core/pull/17625))**
+
+- **API doesn't allow passing option values by label
+  ([dev/core#1816](https://lab.civicrm.org/dev/core/-/issues/1816):
+  [17628](https://github.com/civicrm/civicrm-core/pull/17628))**
+
+  Updates APIv4 so that it matches based on label if name is not found.
+
+- **APIv4 - Improve custom field spec gathering
+  ([17471](https://github.com/civicrm/civicrm-core/pull/17471))**
+
+- **APIv4 - Remove implicit multi joins & add explicit joins
+ ([17332](https://github.com/civicrm/civicrm-core/pull/17332))**
+
+- **APIv4 - Fix entity name on custom groups in schemaMapper
+  ([17425](https://github.com/civicrm/civicrm-core/pull/17425))**
+
+- **APIv4 - fix returning custom field values with API4
+  ([17399](https://github.com/civicrm/civicrm-core/pull/17399))**
+
+- **Attempting to access Multi-Record Custom Field import results
+  in crash
+  ([dev/core#1841](https://lab.civicrm.org/dev/core/-/issues/1841):
+  [17713](https://github.com/civicrm/civicrm-core/pull/17713))**
+
+- **Fix issue with building smart groups when $contactQueries is empty
+  ([17622](https://github.com/civicrm/civicrm-core/pull/17622))**
+
+  If a clause of a contact query used to build a smart group is empty, it would
+  result in a database error.
+
+- **Replace deprecated `money_format()` fn to support PHP 7.4
+  (Work Towards [dev/core#1494](https://lab.civicrm.org/dev/core/-/issues/1494):
+  [17577](https://github.com/civicrm/civicrm-core/pull/17577), [17730](https://github.com/civicrm/civicrm-core/pull/17730))**
+
+  Adds a deprecation notice and a status check for a money value format
+  configuration other than the default.
+
+- **QueueTest - Be more forgiving about slow execution
+  ([17532](https://github.com/civicrm/civicrm-core/pull/17532))**
+
+- **CommunityMessages - Define more consistent behavior in the face of slow
+  execution ([17529](https://github.com/civicrm/civicrm-core/pull/17529))**
+
+  This helps avoid test failures on broadcast messages when the system is
+  running slowly.
+
+- **Scheduled Reminders - Set boolean fields to false if not set.
+  ([17494](https://github.com/civicrm/civicrm-core/pull/17494))**
+
+- **Allow bidirectional assignment of relationships from search results
+  ([17413](https://github.com/civicrm/civicrm-core/pull/17413))**
+
+  Previously relationships could only be created from search results where the
+  contacts would be valid in the "A" side of the relationship type.
+
+- **Navigation menu - Fix translation of new-subtype entries
+  ([17451](https://github.com/civicrm/civicrm-core/pull/17451))**
+
+  The "New" in menu items for creating a new contact of a subtype was not
+  translated.
+
+- **utf8mb4 - If strict mode enabled query will fail if KEY_BLOCK_SIZE is not 0
+  ([17253](https://github.com/civicrm/civicrm-core/pull/17253))**
+
+- **Update DAO and l10n schema file
+  ([17395](https://github.com/civicrm/civicrm-core/pull/17395))**
+
+  Price option name and value are now required in the database.
+
+- **Fix adding languages in multilingual
+  ([17228](https://github.com/civicrm/civicrm-core/pull/17228))**
+
+  This resolves a database error when adding or removing a language on a
+  multilingual site.
+
+- **Don't exclude deleted contacts from ACL cache when user has permission
+  ([17379](https://github.com/civicrm/civicrm-core/pull/17379))**
+
+- **SMTP page appears broken when language is French (JS issue)
+  ([dev/core#1760](https://lab.civicrm.org/dev/core/-/issues/1760):
+  [17317](https://github.com/civicrm/civicrm-core/pull/17317))**
+
+  Javascript strings that appear in Smarty are now escaped more thoroughly to
+  avoid markup problems.
+
+- **Add serialize column to civicrm_custom_field, remove "Multi-Select" html
+  type ([16992](https://github.com/civicrm/civicrm-core/pull/16992), [17722](https://github.com/civicrm/civicrm-core/pull/17722))**
+
+  Work towards making custom select and multi-select fields more similar to
+  core select/multi-select fields.
+
+- **Respect file fields that have been configured as view only in profiles
+  ([17079](https://github.com/civicrm/civicrm-core/pull/17079))**
+
+- **Tag create - Respect created_date and created_id params
+  ([17257](https://github.com/civicrm/civicrm-core/pull/17257))**
+
+- **Public status messages should show correct class/icon like error
+  ([17376](https://github.com/civicrm/civicrm-core/pull/17376))**
+
+- **Decorative chevrons should be icons not angle quotes
+  ([17245](https://github.com/civicrm/civicrm-core/pull/17245))**
+
+- **A/B Test - Icon should be Erlenmeyer flask, not bar chart
+  ([17316](https://github.com/civicrm/civicrm-core/pull/17316))**
+
+- **Proceeding to confirmation page should be a "next" button not a check
+  ([17246](https://github.com/civicrm/civicrm-core/pull/17246))**
+
+### CiviCase
+
+- **Case Activity: Follow-up activity assignee does not receive email
+  ([dev/core#1721](https://lab.civicrm.org/dev/core/-/issues/1721):
+  [17116](https://github.com/civicrm/civicrm-core/pull/17116))**
+
+  Ensures when a follow up case activity is created the assignee is sent an
+  email.
+
+### CiviContribute
+
+- **Custom fields disappear on page refresh
+  ([dev/core#1728](https://lab.civicrm.org/dev/core/-/issues/1728):
+  [17301](https://github.com/civicrm/civicrm-core/pull/17301))**
+
+  This ensures that financial type specific custom fields are displayed on the
+  backend contribution form when it is refreshed.
+
+- **Fix pricefield pseudoconstant.
+  ([17364](https://github.com/civicrm/civicrm-core/pull/17364))**
+
+  Fixes `PriceField` entity to build options for the `price_set_id`
+  pseudoconstant field.
+
+- **Nuance cancel options for processors
+  ([17430](https://github.com/civicrm/civicrm-core/pull/17430))**
+
+  This allows payment processors to specify the way they allow cancellation of
+  recurring contributions.
+
+- **[regression] Contribution amount choices don't appear for anonymous users
+  ([dev/core#1823](https://lab.civicrm.org/dev/core/-/issues/1823):
+  [17619](https://github.com/civicrm/civicrm-core/pull/17619))**
+
+- **Import Contribution of custom fields only accepts "Numeric Value" rather
+  than "Label" ([dev/core#1806](https://lab.civicrm.org/dev/core/-/issues/1806):
+  [17632](https://github.com/civicrm/civicrm-core/pull/17632))**
+
+- **"Financial Type" listed twice in contribution export
+  ([dev/core#1650](https://lab.civicrm.org/dev/core/-/issues/1650):
+  [17147](https://github.com/civicrm/civicrm-core/pull/17147))**
+
+- **Record Payment does not update check_number & trxn id on main contribution.
+  ([dev/core#1758](https://lab.civicrm.org/dev/core/-/issues/1758):
+  [17314](https://github.com/civicrm/civicrm-core/pull/17314))**
+
+- **civicrm_financial_trxn.fee_amount reversals mixed up
+  ([dev/core#1775](https://lab.civicrm.org/dev/core/-/issues/1775):
+  [17386](https://github.com/civicrm/civicrm-core/pull/17386))**
+
+- **jquery validation type error on the credit card field
+  ([dev/core#1797](https://lab.civicrm.org/dev/core/-/issues/1797):
+  [17497](https://github.com/civicrm/civicrm-core/pull/17497))**
+
+  Fixes a jQuery error when submitting a front end contribution form with a
+  credit card field.
+
+- **Fix payment instrument bug by using correct payment instrument
+  ([17607](https://github.com/civicrm/civicrm-core/pull/17607))**
+
+  When a backend payment form records a payment it now will use a
+  processor-specific default payment instrument rather than the site-wide
+  default if it is set.
+
+- **Payment PropertyBag - Fix setAmount
+  ([17505](https://github.com/civicrm/civicrm-core/pull/17505))**
+
+  Ensures "Amount" is set correctly on PropertyBag.
+
+- **Fixed hide show of start date
+  ([17446](https://github.com/civicrm/civicrm-core/pull/17446))**
+
+  Fix hide/show of receive date for recurring contributions.
+
+- **Don't allow Contribution.repeattransaction to be used without a recurring
+  contribution ([17447](https://github.com/civicrm/civicrm-core/pull/17447))**
+
+- **Add actual field name for processor_id to getSubscriptionDetails
+  ([17466](https://github.com/civicrm/civicrm-core/pull/17466))**
+
+- **Make 'Pending' the default status for Contribution.repeattransaction
+  ([17432](https://github.com/civicrm/civicrm-core/pull/17432))**
+
+- **Fetch line items from correct contribution when repeating a contribution
+  ([17220](https://github.com/civicrm/civicrm-core/pull/17220))**
+
+- **Support order_reference param in API3 Payment.create
+  ([17278](https://github.com/civicrm/civicrm-core/pull/17278))**
+
+- **E_WARNINGS when viewing list of pledge payments
+  ([dev/core#1791](https://lab.civicrm.org/dev/core/-/issues/1791):
+  [17434](https://github.com/civicrm/civicrm-core/pull/17434))**
+
+- **Add propertyBag handling for getEmail when incoming uses email-5
+  ([17267](https://github.com/civicrm/civicrm-core/pull/17267))**
+
+  Payment processors can now retrieve billing email using the `propertyBag`
+  class methods.
+
+### CiviMail
+
+- **Don't log subscription_history
+  ([dev/core#1762](https://lab.civicrm.org/dev/core/-/issues/1762):
+  [17323](https://github.com/civicrm/civicrm-core/pull/17323))**
+
+  The `civicrm_subscription_history` table is effectively a log of contacts
+  being added to and removed from groups.  Rows do not change value; each change
+  in group subscription results in a new row.  Consequently, there is no reason
+  to attach detailed logging to this table.
+
+- **Unsubscribe generates 500 server error responses, logs say "missing
+  parameters" ([dev/core#1773](https://lab.civicrm.org/dev/core/-/issues/1773):
+  [17402](https://github.com/civicrm/civicrm-core/pull/17402))**
+
+  Ensures the unsubscribe form can only be submitted once.
+
+### CiviMember
+
+- **Recurring contribution fails with "ipn_payment_callback_exception", for
+  membership auto-renewal via PayPalPro.
+  ([dev/core#1608](https://lab.civicrm.org/dev/core/-/issues/1608):
+  [17355](https://github.com/civicrm/civicrm-core/pull/17355))**
+
+- **Don't hide disabled memberships from the contact membership tab
+  ([dev/membership#24](https://lab.civicrm.org/dev/membership/-/issues/24):
+  [17143](https://github.com/civicrm/civicrm-core/pull/17143) and
+  [17435](https://github.com/civicrm/civicrm-core/pull/17435))**
+
+  This resolves a problem where memberships of disabled membership types would
+  not appear on the membership tab but would throw a duplicate membership
+  warning when creating a new membership with the same membership organization.
+  This also adds a unit test to lock in the correct behavior.
+
+- **Update MembershipType.name to be a required field
+  ([17463](https://github.com/civicrm/civicrm-core/pull/17463))**
+
+- **Fix PHP notice when is_override is not set
+  ([17273](https://github.com/civicrm/civicrm-core/pull/17273))**
+
+### Joomla Integration
+
+- **Upgrade from 5.26.2 to 5.27 RC Fails
+  ([dev/joomla/#28](https://lab.civicrm.org/dev/joomla/-/issues/28):
+  [17710](https://github.com/civicrm/civicrm-core/pull/17710))**
+
+### WordPress Integration
+
+- **Styling issues after upgrade to WordPress 5.3
+  ([dev/wordpress#46](https://lab.civicrm.org/dev/wordpress/-/issues/46):
+  [16967](https://github.com/civicrm/civicrm-core/pull/16967))**
+
+- **WP - Change definitions of `cms.root`, `civicrm.root`
+  ([17360](https://github.com/civicrm/civicrm-core/pull/17360))**
+
+  The base logic for computing path/URL defaults is in Civi/Core/Paths.php.
+
+  For CiviCRM on WordPress, certain variables (which are easier to compute via
+  WordPress APIs) are overridden in `CRM_Utils_System_WordPress`.
+
+  On WordPress, the path/URL logic for has been split: there is one code path
+  for normal CiviCRM pages which is based on WordPress APIs, and there is
+  another for older, standalone scripts in the `extern` folder which is based on
+  the same logic as before.
+
+- **Declare minimum WP and PHP versions required for installation
+  ([201](https://github.com/civicrm/civicrm-wordpress/pull/201))**
+
+- **wp-rest - Adjust extern URL. Be more defensive about 'query' part.
+  ([192](https://github.com/civicrm/civicrm-wordpress/pull/192))**
+
+  This resolves issues with tracking URLs via the `wp-rest` endpoint that are
+  then modified with `hook_civicrm_alterExternUrl`.
+
+## <a name="misc"></a>Miscellany
+
+- **Handle less common dynamic reference cases
+  ([17653](https://github.com/civicrm/civicrm-core/pull/17653))**
+
+- **Use apiv4, cache infra for basicTypes
+  ([17387](https://github.com/civicrm/civicrm-core/pull/17387))**
+
+- **Cleanup PriceFieldValue BAO
+  ([17306](https://github.com/civicrm/civicrm-core/pull/17306))**
+
+- **Clarify definition of amount field in civicrm_contribution_recur table.
+  ([17311](https://github.com/civicrm/civicrm-core/pull/17311))**
+
+- **Call completeOrder directly from contribute status update form
+  ([17347](https://github.com/civicrm/civicrm-core/pull/17347))**
+
+- **Switch docblock code to use markdown syntax.
+  ([17304](https://github.com/civicrm/civicrm-core/pull/17304))**
+
+- **Preliminary cleanup on form
+  ([17345](https://github.com/civicrm/civicrm-core/pull/17345))**
+
+- **Add getBAOClassName utiltiy function
+  ([17268](https://github.com/civicrm/civicrm-core/pull/17268))**
+
+- **CRM/Logging - Remove obsolete cache static clear in test
+  ([17269](https://github.com/civicrm/civicrm-core/pull/17269))**
+
+- **Remove incorrect retrievals of tax-term setting
+  ([17183](https://github.com/civicrm/civicrm-core/pull/17183))**
+
+- **CiviEvent Dashboard and Manage Events: clean up disused url template vars
+  ([17378](https://github.com/civicrm/civicrm-core/pull/17378))**
+
+- **Set default using spec for activity_date_time
+  ([17390](https://github.com/civicrm/civicrm-core/pull/17390))**
+
+- **Convert remaining Authorize.net test to use guzzle
+  ([17473](https://github.com/civicrm/civicrm-core/pull/17473))**
+
+- **Various BAO create/add cleanups to use writeRecord()
+  ([17308](https://github.com/civicrm/civicrm-core/pull/17308))**
+
+- **Remove instantiation of transaction from payment express ipn class
+  ([17348](https://github.com/civicrm/civicrm-core/pull/17348))**
+
+- **Remove null code
+  ([17381](https://github.com/civicrm/civicrm-core/pull/17381))**
+
+- **Remove validation bypass
+  ([17384](https://github.com/civicrm/civicrm-core/pull/17384))**
+
+- **Simplify recurring contribution template inheritance
+  ([17178](https://github.com/civicrm/civicrm-core/pull/17178))**
+
+- **Remove $_REQUEST passed into CRM_Utils_Request::retrieve NFC
+  ([17377](https://github.com/civicrm/civicrm-core/pull/17377))**
+
+- **Use now instead of date for activity API3 spec
+  ([17411](https://github.com/civicrm/civicrm-core/pull/17411))**
+
+- **Typo Fix ([17409](https://github.com/civicrm/civicrm-core/pull/17409))**
+
+- **Fix logic for job log cleanup and make SQL safer
+  ([17203](https://github.com/civicrm/civicrm-core/pull/17203))**
+
+- **Remove always-true if block.
+  ([17415](https://github.com/civicrm/civicrm-core/pull/17415))**
+
+- **Remove No longer required HTTP_Request Package
+  ([293](https://github.com/civicrm/civicrm-packages/pull/293))**
+
+- **Remove code that still seems unnecessary.
+  ([17342](https://github.com/civicrm/civicrm-core/pull/17342))**
+
+- **Undo variable variable.
+  ([17371](https://github.com/civicrm/civicrm-core/pull/17371))**
+
+- **Use non-deprecated method
+  ([17370](https://github.com/civicrm/civicrm-core/pull/17370))**
+
+- **Remove last reference to Payment->_processorName in core & remove from
+  processors ([17474](https://github.com/civicrm/civicrm-core/pull/17474))**
+
+- **Remove unused singleton from core payment processors
+  ([17468](https://github.com/civicrm/civicrm-core/pull/17468))**
+
+- **Test fixes to support apiv4 add of MembershipType
+  ([17488](https://github.com/civicrm/civicrm-core/pull/17488))**
+
+- **Merge code - remove UPDATE IGNORE+Delete & just use reliable update
+  ([17072](https://github.com/civicrm/civicrm-core/pull/17072))**
+
+- **Remove unneeded parameters in {ts} on membership form
+  ([17326](https://github.com/civicrm/civicrm-core/pull/17326))**
+
+- **Remove unused 'reset' param, other NFC tidy up:
+  ([17392](https://github.com/civicrm/civicrm-core/pull/17392))**
+
+- **Fix setLanguage docblock, other nfc changes
+  ([17356](https://github.com/civicrm/civicrm-core/pull/17356))**
+
+- **[REF] Simplify contributionIDs code
+  ([17441](https://github.com/civicrm/civicrm-core/pull/17441))**
+
+- **[REF] Extract getAttachments
+  ([17405](https://github.com/civicrm/civicrm-core/pull/17405))**
+
+- **[REF] Preliminary cleanup for #17339
+  ([17417](https://github.com/civicrm/civicrm-core/pull/17417))**
+
+- **[REF] Remove unreachable block.
+  ([17407](https://github.com/civicrm/civicrm-core/pull/17407))**
+
+- **[REF] Pass an array of correct params to the function to create a recurring
+  contribution. ([17398](https://github.com/civicrm/civicrm-core/pull/17398))**
+
+- **[REF] Code simplification & unit test on suppressed emails in task
+  ([17393](https://github.com/civicrm/civicrm-core/pull/17393))**
+
+- **[REF] duplicate function.
+  ([17400](https://github.com/civicrm/civicrm-core/pull/17400))**
+
+- **[REF] Pass params not-by-reference
+  ([17406](https://github.com/civicrm/civicrm-core/pull/17406))**
+
+- **[REF] Refactor to switch from pear HTTP Request class to using Guzzle
+  ([17420](https://github.com/civicrm/civicrm-core/pull/17420))**
+
+- **[REF] [Reports] Ensure that all contribution pages including disabled ones
+  are return… ([17357](https://github.com/civicrm/civicrm-core/pull/17357))**
+
+- **[REF] Cleanup and extract string functions specific to entity names.
+  ([17330](https://github.com/civicrm/civicrm-core/pull/17330))**
+
+- **[REF] Stop passing  by reference.
+  ([17349](https://github.com/civicrm/civicrm-core/pull/17349))**
+
+- **[REF] Simplify subject code.
+  ([17436](https://github.com/civicrm/civicrm-core/pull/17436))**
+
+- **[REF] Implement required fields database changes that have occured du…
+  ([17465](https://github.com/civicrm/civicrm-core/pull/17465), [17725](https://github.com/civicrm/civicrm-core/pull/17725))**
+
+- **[REF] Bump Pear Log version to fix compatability with Drupal 8 / Drup…
+  ([17460](https://github.com/civicrm/civicrm-core/pull/17460))**
+
+- **[REF] Convert one of the http calls in Authorize.net class to use guzzle.
+  ([17449](https://github.com/civicrm/civicrm-core/pull/17449))**
+
+- **[REF] Minor simplification
+  ([17443](https://github.com/civicrm/civicrm-core/pull/17443))**
+
+- **[REF] Grant BAO cleanup
+  ([17264](https://github.com/civicrm/civicrm-core/pull/17264))**
+
+- **[REF] CRM_Core_DAO_AllCoreTables::getBriefName to accept BAO name
+  ([17331](https://github.com/civicrm/civicrm-core/pull/17331))**
+
+- **[REF] Missing semicolon on smtp settings page, code cleanup in file-on-case,
+  add test ([17335](https://github.com/civicrm/civicrm-core/pull/17335))**
+
+- **[REF] Extract createFollowUpActivities
+  ([17481](https://github.com/civicrm/civicrm-core/pull/17481))**
+
+- **[REF] Switch to an api call to get the contact details.
+  ([17442](https://github.com/civicrm/civicrm-core/pull/17442))**
+
+- **[REF] Minor code simplification
+  ([17373](https://github.com/civicrm/civicrm-core/pull/17373))**
+
+- **[REF] Call completeOrder directly from event status update form
+  ([17346](https://github.com/civicrm/civicrm-core/pull/17346))**
+
+- **[REF] Remove never-passed params from function signature
+  ([17464](https://github.com/civicrm/civicrm-core/pull/17464))**
+
+- **[REF] Fix serialized membership field handling in RelationshipTest
+  ([17469](https://github.com/civicrm/civicrm-core/pull/17469))**
+
+- **[REF] Minor tidy up
+  ([17302](https://github.com/civicrm/civicrm-core/pull/17302))**
+
+- **[REF] Remove always NULL param modified_id
+  ([17489](https://github.com/civicrm/civicrm-core/pull/17489))**
+
+- **[REF] Add contribution_id field to search using metadata instead of h…
+  ([17286](https://github.com/civicrm/civicrm-core/pull/17286))**
+
+- **[REF] Fix unit test failures on MySQL 5.6 due to Custom Field table being
+  created without Dynamic Row Format
+  ([17586](https://github.com/civicrm/civicrm-core/pull/17586))**
+
+- **[REF] Start to convert Dummy processor to use PropertyBag
+  ([17452](https://github.com/civicrm/civicrm-core/pull/17452))**
+
+- **[Ref] remove calls to, and deprecate, pending function
+  ([17490](https://github.com/civicrm/civicrm-core/pull/17490))**
+
+- **[REF] Cleanup MailingAB BAO
+  ([17309](https://github.com/civicrm/civicrm-core/pull/17309))**
+
+- **[TEST] Update test to support for 8.0.19 change
+  ([17354](https://github.com/civicrm/civicrm-core/pull/17354))**
+
+- **[TEST] Fix test failure on Drupal 8 E2E PrevNextTest by only includin…
+  ([17358](https://github.com/civicrm/civicrm-core/pull/17358))**
+
+- **[TEST] dev/core#1766 - Remove flakiness from testGetFromTo tests
+  ([dev/core#1766](https://lab.civicrm.org/dev/core/-/issues/1766):
+  [17338](https://github.com/civicrm/civicrm-core/pull/17338))**
+
+- **[NFC/TEST] dev/core#1784 - Unit tests for contact delete/restore
+  ([dev/core#1784](https://lab.civicrm.org/dev/core/-/issues/1784):
+  [17437](https://github.com/civicrm/civicrm-core/pull/17437))**
+
+- **NFC - Change array() to short syntax []
+  ([17470](https://github.com/civicrm/civicrm-core/pull/17470))**
+
+- **[NFC] Update Karma to be latest version
+  ([17439](https://github.com/civicrm/civicrm-core/pull/17439))**
+
+- **(NFC) CRM_Utils_Url - Fix declared type
+  ([17320](https://github.com/civicrm/civicrm-core/pull/17320))**
+
+- **[NFC] Remove some more of the old  cvs blocks
+  ([17383](https://github.com/civicrm/civicrm-core/pull/17383))**
+
+- **NFC Add a bit more clarity to createEmailActivity function
+  ([17391](https://github.com/civicrm/civicrm-core/pull/17391))**
+
+- **[NFC] Add comment that deprecated code is actually still reachable
+  ([17340](https://github.com/civicrm/civicrm-core/pull/17340))**
+
+- **(NFC) Remove some cvs docblock help
+  ([17322](https://github.com/civicrm/civicrm-core/pull/17322))**
+
+- **[NFC] Add comment regarding lower case for Polish states
+  ([17327](https://github.com/civicrm/civicrm-core/pull/17327))**
+
+- **(NFC) RegionTest - Small cleanups
+  ([17270](https://github.com/civicrm/civicrm-core/pull/17270))**
+
+- **[NFC] Add sr-only class for content only for screen readers
+  ([17255](https://github.com/civicrm/civicrm-core/pull/17255))**
+
+- **NFC Change hardcoded ID to constant
+  ([17275](https://github.com/civicrm/civicrm-core/pull/17275))**
+
+- **[NFC] Fix use of single quotes, strict operator
+  ([17492](https://github.com/civicrm/civicrm-core/pull/17492))**
+
+## <a name="credits"></a>Credits
+
+This release was developed by the following code authors:
+
+a-n The Artists Information Company - William Mortada; AGH Strategies - Alice
+Frumin, Andrew Hunt; Andrei Mondoc; British Humanist Association - Andrew West;
+Caltha - Tomasz Pietrzkowski; Christian Wach; Circle Interactive - Pradeep
+Nayak; CiviCRM - Coleman Watts, Tim Otten; CiviDesk - Yashodha Chaku;
+CompuCorp - René Olivo; Coop SymbioTIC - Mathieu Lutfy; Dave D; Dominic Hargreaves;
+Electronic Frontier Foundation - Mark Burdett; Freeform Solutions - Herb van den
+Dool; Fuzion - Jitendra Purohit; Greenpeace Central and Eastern Europe - Patrick
+Figel; JMA Consulting - Edsel Lopez, Seamus Lee; John Kingsnorth; Joinery -
+Allen Shaw; Kartik Kathuria; Lighthouse Design and Consulting - Brian
+Shaughnessy; Megaphone Technology Consulting - Jon Goldberg; MJCO - Mikey
+O'Toole; MJW Consulting - Matthew Wire; Palante Technology Cooperative - Morgan
+Robinson; Semper IT - Karin Gerritsen; Tadpole Collective - Kevin Cristiano;
+Wikimedia Foundation - Eileen McNaughton
+
+Most authors also reviewed code for this release; in addition, the following
+reviewers contributed their comments:
+
+Agileware - Justin Freeman; Artful Robot - Rich Lott; Betty Dolfing; CiviCoop -
+Jaap Jansma; iXiam - Luciano Spiegel; JMA Consulting - Monish Deb; Kartik
+Kathuria; Korlon - Stuart Gaston; Megaphone Technology Consulting - Dennis P.
+Osorio; Squiffle Consulting - Aidan Saunders
+
+## <a name="feedback"></a>Feedback
+
+These release notes are edited by Alice Frumin and Andrew Hunt.  If you'd like
+to provide feedback on them, please log in to https://chat.civicrm.org/civicrm
+and contact `@agh1`.
diff --git a/civicrm/settings/Core.setting.php b/civicrm/settings/Core.setting.php
index 349f266bde10d5184d4c4589738d88f8851c2390..0f1f8aab13f28e675025d1f310916db5bddaf983 100644
--- a/civicrm/settings/Core.setting.php
+++ b/civicrm/settings/Core.setting.php
@@ -239,6 +239,28 @@ return [
     'description' => NULL,
     'help_text' => NULL,
   ],
+  'defaultExternUrl' => [
+    'group_name' => 'CiviCRM Preferences',
+    'group' => 'core',
+    'name' => 'defaultExternUrl',
+    'type' => 'String',
+    'quick_form_type' => 'Select',
+    'html_type' => 'Select',
+    'html_attributes' => [
+      'class' => 'crm-select2',
+    ],
+    'default' => 'router',
+    'add' => '5.27',
+    'title' => ts('Extern URL Style'),
+    'is_domain' => 1,
+    'is_contact' => 0,
+    'description' => ts('This setting provides transitional support. It should be set to "Prefer normal router." If your deployment requires "Prefer standalone script", then please ensure that the issue is tracked in <code>lab.civicrm.org</code>.'),
+    'help_text' => NULL,
+    'options' => [
+      'standalone' => ts('Prefer standalone scripts'),
+      'router' => ts('Prefer normal router'),
+    ],
+  ],
   'activity_assignee_notification' => [
     'group_name' => 'CiviCRM Preferences',
     'group' => 'core',
@@ -522,7 +544,7 @@ return [
     'html_type' => 'text',
     'default' => NULL,
     'add' => '4.3',
-    'title' => ts('Recaptcha Options'),
+    'title' => ts('reCAPTCHA Options'),
     'is_domain' => 1,
     'is_contact' => 0,
     'description' => ts('You can specify the reCAPTCHA theme options as comma separated data.(eg: theme:\'blackglass\', lang : \'fr\' ). Check the available options at <a href="https://developers.google.com/recaptcha/docs/display#config">Customizing the Look and Feel of reCAPTCHA</a>.'),
@@ -541,7 +563,7 @@ return [
     'html_type' => 'text',
     'default' => NULL,
     'add' => '4.3',
-    'title' => ts('Recaptcha Site Key'),
+    'title' => ts('reCAPTCHA Site Key'),
     'is_domain' => 1,
     'is_contact' => 0,
     'description' => NULL,
@@ -575,7 +597,7 @@ return [
     'html_type' => 'text',
     'default' => NULL,
     'add' => '4.3',
-    'title' => ts('Recaptcha Secret Key'),
+    'title' => ts('reCAPTCHA Secret Key'),
     'is_domain' => 1,
     'is_contact' => 0,
     'description' => NULL,
diff --git a/civicrm/settings/Mailing.setting.php b/civicrm/settings/Mailing.setting.php
index a2a5bc06c8e2e4328c0f13fd2d75afed16e172a5..2e2640956a7fc7bb5fc93b1ccb939007c0640a68 100644
--- a/civicrm/settings/Mailing.setting.php
+++ b/civicrm/settings/Mailing.setting.php
@@ -341,4 +341,32 @@ return [
     'description' => ts('Allow sending email from the logged in contact\'s email address.'),
     'help_text' => 'CiviCRM allows you to send email from the domain from email addresses and the logged in contact id addresses by default. Disable this if you only want to allow the domain from addresses to be used.',
   ],
+  'url_tracking_default' => [
+    'group_name' => 'Mailing Preferences',
+    'group' => 'mailing',
+    'name' => 'url_tracking_default',
+    'type' => 'Boolean',
+    'html_type' => 'checkbox',
+    'quick_form_type' => 'CheckBox',
+    'default' => '1',
+    'title' => ts('Enable click-through tracking by default'),
+    'is_domain' => 1,
+    'is_contact' => 0,
+    'description' => ts('If checked, mailings will have click-through tracking enabled by default.'),
+    'help_text' => NULL,
+  ],
+  'open_tracking_default' => [
+    'group_name' => 'Mailing Preferences',
+    'group' => 'mailing',
+    'name' => 'open_tracking_default',
+    'type' => 'Boolean',
+    'html_type' => 'checkbox',
+    'quick_form_type' => 'CheckBox',
+    'default' => '1',
+    'title' => ts('Enable open tracking by default'),
+    'is_domain' => 1,
+    'is_contact' => 0,
+    'description' => ts('If checked, mailings will have open tracking enabled by default.'),
+    'help_text' => NULL,
+  ],
 ];
diff --git a/civicrm/settings/Search.setting.php b/civicrm/settings/Search.setting.php
index 427bc8a6c6bf86eba7acc67954ba09b763d92d4f..c9a02e963e4cff03120890ab9360a8e3526e03e2 100644
--- a/civicrm/settings/Search.setting.php
+++ b/civicrm/settings/Search.setting.php
@@ -65,7 +65,7 @@ return [
     'html_type' => 'text',
     'default' => 'simple',
     'add' => '4.5',
-    'title' => ts('How to handle full-tet queries'),
+    'title' => ts('How to handle full-text queries'),
     'is_domain' => 1,
     'is_contact' => 0,
     'help_text' => NULL,
diff --git a/civicrm/setup/plugins/installDatabase/BootstrapCivi.civi-setup.php b/civicrm/setup/plugins/installDatabase/BootstrapCivi.civi-setup.php
index b2c0a17446b9663682e07c5bad5f3e87df89dda7..3592e35435cc5bd2c0f1fa40ff51e0dd4caa20bc 100644
--- a/civicrm/setup/plugins/installDatabase/BootstrapCivi.civi-setup.php
+++ b/civicrm/setup/plugins/installDatabase/BootstrapCivi.civi-setup.php
@@ -2,7 +2,15 @@
 /**
  * @file
  *
- * Perform the first system bootstrap.
+ * Perform the full bootstrap.
+ *
+ * GOAL: All the standard services (database, DAOs, translations, settings, etc) should be loaded.
+ *
+ * MECHANICS: This basically calls `CRM_Core_Config::singleton(TRUE,TRUE)`.
+ *
+ * NOTE: This is technically a *reboot*. `Preboot` started things off, but it
+ * booted with `CRM_Core_Config::singleton($loadFromDB==FALSE)`. Now, the DB is
+ * populated, so we can teardown the preboot stuff and start again with `$loadFromDB==TRUE`.
  */
 
 if (!defined('CIVI_SETUP')) {
@@ -13,19 +21,9 @@ if (!defined('CIVI_SETUP')) {
   ->addListener('civi.setup.installDatabase', function (\Civi\Setup\Event\InstallDatabaseEvent $e) {
     \Civi\Setup::log()->info(sprintf('[%s] Bootstrap CiviCRM', basename(__FILE__)));
 
-    if (!defined('CIVICRM_SETTINGS_PATH')) {
-      define('CIVICRM_SETTINGS_PATH', $e->getModel()->settingsPath);
-    }
-
-    if (realpath(CIVICRM_SETTINGS_PATH) !== realpath($e->getModel()->settingsPath)) {
-      throw new \RuntimeException(sprintf("Cannot boot: The civicrm.settings.php path appears inconsistent (%s vs %s)", CIVICRM_SETTINGS_PATH, $e->getModel()->settingsPath));
-    }
-
-    include_once CIVICRM_SETTINGS_PATH;
-
-    require_once 'CRM/Core/ClassLoader.php';
-    CRM_Core_ClassLoader::singleton()->register();
+    \CRM_Core_I18n::$SQL_ESCAPER = NULL;
+    unset(\Civi::$statics['testPreInstall']);
 
-    CRM_Core_Config::singleton(TRUE);
+    CRM_Core_Config::singleton(TRUE, TRUE);
 
   }, \Civi\Setup::PRIORITY_MAIN - 200);
diff --git a/civicrm/setup/plugins/installDatabase/InstallSchema.civi-setup.php b/civicrm/setup/plugins/installDatabase/InstallSchema.civi-setup.php
index fec273ce0735c67586fb724a40dee9ee5505b648..1455edab09e7442e6929676d054562660fc3c6ff 100644
--- a/civicrm/setup/plugins/installDatabase/InstallSchema.civi-setup.php
+++ b/civicrm/setup/plugins/installDatabase/InstallSchema.civi-setup.php
@@ -68,11 +68,6 @@ class InstallSchemaPlugin implements \Symfony\Component\EventDispatcher\EventSub
     $sqlPath = $model->srcPath . DIRECTORY_SEPARATOR . 'sql';
     $spec = $this->loadSpecification($model->srcPath);
 
-    $conn = \Civi\Setup\DbUtil::connect($model->db);
-    \CRM_Core_I18n::$SQL_ESCAPER = function($text) use ($conn) {
-      return $conn->escape_string($text);
-    };
-
     \Civi\Setup::log()->info(sprintf('[%s] Load basic tables', basename(__FILE__)));
     \Civi\Setup\DbUtil::sourceSQL($model->db, \Civi\Setup\SchemaGenerator::generateCreateSql($model->srcPath, $spec->database, $spec->tables));
 
@@ -89,11 +84,6 @@ class InstallSchemaPlugin implements \Symfony\Component\EventDispatcher\EventSub
       \Civi\Setup::log()->info(sprintf('[%s] Load basic data', basename(__FILE__)));
       \Civi\Setup\DbUtil::sourceSQL($model->db, \Civi\Setup\SchemaGenerator::generateBasicData($model->srcPath));
     }
-
-    require_once $model->settingsPath;
-    \Civi\Core\Container::boot(TRUE);
-
-    \CRM_Core_I18n::$SQL_ESCAPER = NULL;
   }
 
   /**
diff --git a/civicrm/setup/plugins/installDatabase/Preboot.civi-setup.php b/civicrm/setup/plugins/installDatabase/Preboot.civi-setup.php
new file mode 100644
index 0000000000000000000000000000000000000000..116a50246739bd92123a695595b4107e9d89528e
--- /dev/null
+++ b/civicrm/setup/plugins/installDatabase/Preboot.civi-setup.php
@@ -0,0 +1,49 @@
+<?php
+/**
+ * @file
+ *
+ * Perform an initial, partial bootstrap.
+ *
+ * GOAL: This should provide sufficient services for `InstallSchema` to generate
+ * For example, `ts()` needs to be online.
+ *
+ * MECHANICS: This basically loads `civicrm.settings.php` and calls
+ * `CRM_Core_Config::singleton(FALSE,TRUE)`.
+ */
+
+if (!defined('CIVI_SETUP')) {
+  exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+  ->addListener('civi.setup.checkRequirements', function (\Civi\Setup\Event\CheckRequirementsEvent $e) {
+
+  });
+
+\Civi\Setup::dispatcher()
+  ->addListener('civi.setup.installDatabase', function (\Civi\Setup\Event\InstallDatabaseEvent $e) {
+    \Civi\Setup::log()->info(sprintf('[%s] Load minimal (non-DB) services', basename(__FILE__)));
+
+    if (!defined('CIVICRM_SETTINGS_PATH')) {
+      define('CIVICRM_SETTINGS_PATH', $e->getModel()->settingsPath);
+    }
+
+    if (realpath(CIVICRM_SETTINGS_PATH) !== realpath($e->getModel()->settingsPath)) {
+      throw new \RuntimeException(sprintf("Cannot boot: The civicrm.settings.php path appears inconsistent (%s vs %s)", CIVICRM_SETTINGS_PATH, $e->getModel()->settingsPath));
+    }
+
+    include_once CIVICRM_SETTINGS_PATH;
+
+    require_once 'CRM/Core/ClassLoader.php';
+    CRM_Core_ClassLoader::singleton()->register();
+
+    $conn = \Civi\Setup\DbUtil::connect($e->getModel()->db);
+    \CRM_Core_I18n::$SQL_ESCAPER = function($text) use ($conn) {
+      return $conn->escape_string($text);
+    };
+
+    \Civi::$statics['testPreInstall'] = 1;
+
+    CRM_Core_Config::singleton(FALSE, TRUE);
+
+  }, \Civi\Setup::PRIORITY_PREPARE);
diff --git a/civicrm/sql/civicrm.mysql b/civicrm/sql/civicrm.mysql
index 22d81d0476fe8c60a5025a923cd0c8aabd495fc2..0f15db4a6e6a129d2b28f04121621e217e579f58 100644
--- a/civicrm/sql/civicrm.mysql
+++ b/civicrm/sql/civicrm.mysql
@@ -940,7 +940,7 @@ CREATE TABLE `civicrm_mailing_abtest` (
      `mailing_id_a` int unsigned    COMMENT 'The first experimental mailing (\"A\" condition)',
      `mailing_id_b` int unsigned    COMMENT 'The second experimental mailing (\"B\" condition)',
      `mailing_id_c` int unsigned    COMMENT 'The final, general mailing (derived from A or B)',
-     `domain_id` int unsigned    COMMENT 'Which site is this mailing for',
+     `domain_id` int unsigned NOT NULL   COMMENT 'Which site is this mailing for',
      `testing_criteria` varchar(32)    ,
      `winner_criteria` varchar(32)    ,
      `specific_url` varchar(255)    COMMENT 'What specific url to track',
@@ -1815,6 +1815,7 @@ CREATE TABLE `civicrm_custom_field` (
      `note_rows` int unsigned    COMMENT 'Number of rows in Note Field',
      `column_name` varchar(255)    COMMENT 'Name of the column that holds the values for this field.',
      `option_group_id` int unsigned    COMMENT 'For elements with options, the option group id that is used',
+     `serialize` int unsigned    COMMENT 'Serialization method - a non-null value indicates a multi-valued field.',
      `filter` varchar(255)    COMMENT 'Stores Contact Get API params contact reference custom fields. May be used for other filters in the future.',
      `in_selector` tinyint   DEFAULT 0 COMMENT 'Should the multi-record custom field values be displayed in tab table listing' 
 ,
@@ -2219,7 +2220,7 @@ CREATE TABLE `civicrm_option_value` (
      `value` varchar(512) NOT NULL   COMMENT 'The actual value stored (as a foreign key) in the data record. Functions which need lookup option_value.title should use civicrm_option_value.option_group_id plus civicrm_option_value.value as the key.',
      `name` varchar(255)    COMMENT 'Stores a fixed (non-translated) name for this option value. Lookup functions should use the name as the key for the option value row.',
      `grouping` varchar(255)    COMMENT 'Use to sort and/or set display properties for sub-set(s) of options within an option group. EXAMPLE: Use for college_interest field, to differentiate partners from non-partners.',
-     `filter` int unsigned   DEFAULT NULL COMMENT 'Bitwise logic can be used to create subsets of options within an option_group for different uses.',
+     `filter` int unsigned   DEFAULT 0 COMMENT 'Bitwise logic can be used to create subsets of options within an option_group for different uses.',
      `is_default` tinyint   DEFAULT 0 COMMENT 'Is this the default option for the group?',
      `weight` int unsigned NOT NULL   COMMENT 'Controls display sort order.',
      `description` text    COMMENT 'Optional description.',
@@ -3039,7 +3040,7 @@ CREATE TABLE `civicrm_membership_type` (
 
      `id` int unsigned NOT NULL AUTO_INCREMENT  COMMENT 'Membership Id',
      `domain_id` int unsigned NOT NULL   COMMENT 'Which Domain is this match entry for',
-     `name` varchar(128)    COMMENT 'Name of Membership Type',
+     `name` varchar(128) NOT NULL   COMMENT 'Name of Membership Type',
      `description` varchar(255)    COMMENT 'Description of Membership Type',
      `member_of_contact_id` int unsigned NOT NULL   COMMENT 'Owner organization for this membership type. FK to Contact ID',
      `financial_type_id` int unsigned NOT NULL   COMMENT 'If membership is paid by a contribution - what financial type should be used. FK to civicrm_financial_type.id',
@@ -4041,7 +4042,7 @@ CREATE TABLE `civicrm_contribution_recur` (
 
      `id` int unsigned NOT NULL AUTO_INCREMENT  COMMENT 'Contribution Recur ID',
      `contact_id` int unsigned NOT NULL   COMMENT 'Foreign key to civicrm_contact.id.',
-     `amount` decimal(20,2) NOT NULL   COMMENT 'Amount to be contributed or charged each recurrence.',
+     `amount` decimal(20,2) NOT NULL   COMMENT 'Amount to be collected (including any sales tax) by payment processor each recurrence.',
      `currency` varchar(3)   DEFAULT NULL COMMENT '3 character string, value from config setting or input via user.',
      `frequency_unit` varchar(8)   DEFAULT 'month' COMMENT 'Time units for recurrence of payment.',
      `frequency_interval` int unsigned NOT NULL   COMMENT 'Number of time units for recurrence of payment.',
@@ -4355,8 +4356,8 @@ CREATE TABLE `civicrm_price_field_value` (
 
      `id` int unsigned NOT NULL AUTO_INCREMENT  COMMENT 'Price Field Value',
      `price_field_id` int unsigned NOT NULL   COMMENT 'FK to civicrm_price_field',
-     `name` varchar(255)    COMMENT 'Price field option name',
-     `label` varchar(255)    COMMENT 'Price field option label',
+     `name` varchar(255) NOT NULL   COMMENT 'Price field option name',
+     `label` varchar(255) NOT NULL   COMMENT 'Price field option label',
      `description` text   DEFAULT NULL COMMENT 'Price field option description.',
      `help_pre` text   DEFAULT NULL COMMENT 'Price field option pre help text.',
      `help_post` text   DEFAULT NULL COMMENT 'Price field option post field help.',
diff --git a/civicrm/sql/civicrm_data.mysql b/civicrm/sql/civicrm_data.mysql
index 477f8b1bd53fd8f9aced71cc921bd4cd2cb5c073..e8692dec6f1297ee43514cc7a6ba35a564159b66 100644
--- a/civicrm/sql/civicrm_data.mysql
+++ b/civicrm/sql/civicrm_data.mysql
@@ -399,6 +399,7 @@ INSERT INTO civicrm_state_province (id, country_id, abbreviation, name) VALUES
 (1231, 1101, "DL", "Delhi"),
 (1232, 1101, "LD", "Lakshadweep"),
 (1233, 1101, "PY", "Pondicherry"),
+-- Note we believe all lower-case is correct for Poland. See https://github.com/civicrm/civicrm-core/pull/17107
 (1300, 1172, "MZ", "mazowieckie"),
 (1301, 1172, "PM", "pomorskie"),
 (1302, 1172, "DS", "dolnośląskie"),
@@ -23896,4 +23897,4 @@ INSERT INTO `civicrm_report_instance`
     ( `domain_id`, `title`, `report_id`, `description`, `permission`, `form_values`)
 VALUES
     (  @domainID, 'Survey Details', 'survey/detail', 'Detailed report for canvassing, phone-banking, walk lists or other surveys.', 'access CiviReport', 'a:39:{s:6:"fields";a:2:{s:9:"sort_name";s:1:"1";s:6:"result";s:1:"1";}s:22:"assignee_contact_id_op";s:2:"eq";s:25:"assignee_contact_id_value";s:0:"";s:12:"sort_name_op";s:3:"has";s:15:"sort_name_value";s:0:"";s:17:"street_number_min";s:0:"";s:17:"street_number_max";s:0:"";s:16:"street_number_op";s:3:"lte";s:19:"street_number_value";s:0:"";s:14:"street_name_op";s:3:"has";s:17:"street_name_value";s:0:"";s:15:"postal_code_min";s:0:"";s:15:"postal_code_max";s:0:"";s:14:"postal_code_op";s:3:"lte";s:17:"postal_code_value";s:0:"";s:7:"city_op";s:3:"has";s:10:"city_value";s:0:"";s:20:"state_province_id_op";s:2:"in";s:23:"state_province_id_value";a:0:{}s:13:"country_id_op";s:2:"in";s:16:"country_id_value";a:0:{}s:12:"survey_id_op";s:2:"in";s:15:"survey_id_value";a:0:{}s:12:"status_id_op";s:2:"eq";s:15:"status_id_value";s:1:"1";s:11:"custom_1_op";s:2:"in";s:14:"custom_1_value";a:0:{}s:11:"custom_2_op";s:2:"in";s:14:"custom_2_value";a:0:{}s:17:"custom_3_relative";s:1:"0";s:13:"custom_3_from";s:0:"";s:11:"custom_3_to";s:0:"";s:11:"description";s:75:"Detailed report for canvassing, phone-banking, walk lists or other surveys.";s:13:"email_subject";s:0:"";s:8:"email_to";s:0:"";s:8:"email_cc";s:0:"";s:10:"permission";s:17:"access CiviReport";s:6:"groups";s:0:"";s:9:"domain_id";i:1;}');
-UPDATE civicrm_domain SET version = '5.26.2';
+UPDATE civicrm_domain SET version = '5.27.0';
diff --git a/civicrm/sql/civicrm_generated.mysql b/civicrm/sql/civicrm_generated.mysql
index 55c163ab73c6ad07889e3584f0795a7ec82ba9a3..64fb5084e424984869ac1a37c273686af2ca0588 100644
--- a/civicrm/sql/civicrm_generated.mysql
+++ b/civicrm/sql/civicrm_generated.mysql
@@ -399,7 +399,7 @@ UNLOCK TABLES;
 
 LOCK TABLES `civicrm_domain` WRITE;
 /*!40000 ALTER TABLE `civicrm_domain` DISABLE KEYS */;
-INSERT INTO `civicrm_domain` (`id`, `name`, `description`, `version`, `contact_id`, `locales`, `locale_custom_strings`) VALUES (1,'Default Domain Name',NULL,'5.26.2',1,NULL,'a:1:{s:5:\"en_US\";a:0:{}}');
+INSERT INTO `civicrm_domain` (`id`, `name`, `description`, `version`, `contact_id`, `locales`, `locale_custom_strings`) VALUES (1,'Default Domain Name',NULL,'5.27.0',1,NULL,'a:1:{s:5:\"en_US\";a:0:{}}');
 /*!40000 ALTER TABLE `civicrm_domain` ENABLE KEYS */;
 UNLOCK TABLES;
 
diff --git a/civicrm/templates/CRM/ACL/Page/ACLBasic.tpl b/civicrm/templates/CRM/ACL/Page/ACLBasic.tpl
index 1c3787efd06273706464610ef2cd05f347635ca7..8ecb0eee8a52b3ec189a32da62fd5f0bdfb2df17 100644
--- a/civicrm/templates/CRM/ACL/Page/ACLBasic.tpl
+++ b/civicrm/templates/CRM/ACL/Page/ACLBasic.tpl
@@ -39,7 +39,7 @@
 
         {if $action ne 1 and $action ne 2}
       <div class="action-link">
-      <a href="{crmURL q="action=add&reset=1"}" id="newACL"><i class="crm-i fa-plus-circle"></i> {ts}Add ACL{/ts}</a>
+      <a href="{crmURL q="action=add&reset=1"}" id="newACL"><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Add ACL{/ts}</a>
         </div>
         {/if}
     </div>
diff --git a/civicrm/templates/CRM/Activity/Form/Activity.tpl b/civicrm/templates/CRM/Activity/Form/Activity.tpl
index a013a7cb74a999051f0dfbdf102279a3dd5ddd5c..a6f20a34505daaa71a942def575a6dd87b821232 100644
--- a/civicrm/templates/CRM/Activity/Form/Activity.tpl
+++ b/civicrm/templates/CRM/Activity/Form/Activity.tpl
@@ -80,12 +80,12 @@
         {if $action neq 4}
           {if !$form.target_contact_id.frozen}
             <a href="#" class="crm-hover-button" id="swap_target_assignee" title="{ts}Swap Target and Assignee Contacts{/ts}" style="position:relative; bottom: 1em;">
-              <i class="crm-i fa-random"></i>
+              <i class="crm-i fa-random" aria-hidden="true"></i>
             </a>
           {/if}
           {if $activityAssigneeNotification}
             <br />
-            <span id="notify_assignee_msg" class="description"><i class="crm-i fa-paper-plane"></i> {ts}A copy of this activity will be emailed to each Assignee.{/ts} {help id="sent_copy_email"}</span>
+            <span id="notify_assignee_msg" class="description"><i class="crm-i fa-paper-plane" aria-hidden="true"></i> {ts}A copy of this activity will be emailed to each Assignee.{/ts} {help id="sent_copy_email"}</span>
           {/if}
         {/if}
       </td>
@@ -244,7 +244,7 @@
       {if ($context eq 'fulltext' || $context eq 'search') && $searchKey}
         {assign var='urlParams' value="reset=1&atype=$atype&action=update&reset=1&id=$entityID&cid=$contactId&context=$context&key=$searchKey"}
       {/if}
-      <a href="{crmURL p='civicrm/activity/add' q=$urlParams}" class="edit button" title="{ts}Edit{/ts}"><span><i class="crm-i fa-pencil"></i> {ts}Edit{/ts}</span></a>
+      <a href="{crmURL p='civicrm/activity/add' q=$urlParams}" class="edit button" title="{ts}Edit{/ts}"><span><i class="crm-i fa-pencil" aria-hidden="true"></i> {ts}Edit{/ts}</span></a>
     {/if}
 
     {if call_user_func(array('CRM_Core_Permission','check'), 'delete activities')}
@@ -252,11 +252,11 @@
       {if ($context eq 'fulltext' || $context eq 'search') && $searchKey}
         {assign var='urlParams' value="reset=1&atype=$atype&action=delete&reset=1&id=$entityID&cid=$contactId&context=$context&key=$searchKey"}
       {/if}
-      <a href="{crmURL p='civicrm/contact/view/activity' q=$urlParams}" class="delete button" title="{ts}Delete{/ts}"><span><i class="crm-i fa-trash"></i> {ts}Delete{/ts}</span></a>
+      <a href="{crmURL p='civicrm/contact/view/activity' q=$urlParams}" class="delete button" title="{ts}Delete{/ts}"><span><i class="crm-i fa-trash" aria-hidden="true"></i> {ts}Delete{/ts}</span></a>
     {/if}
   {/if}
   {if $action eq 4 and $context != 'case' and call_user_func(array('CRM_Case_BAO_Case','checkPermission'), $activityId, 'File On Case', $atype)}
-    <a href="#" onclick="fileOnCase('file', {$activityId}, null, this); return false;" class="cancel button" title="{ts}File On Case{/ts}"><span><i class="crm-i fa-clipboard"></i> {ts}File on Case{/ts}</span></a>
+    <a href="#" onclick="fileOnCase('file', {$activityId}, null, this); return false;" class="cancel button" title="{ts}File On Case{/ts}"><span><i class="crm-i fa-clipboard" aria-hidden="true"></i> {ts}File on Case{/ts}</span></a>
     {include file="CRM/Case/Form/ActivityToCase.tpl"}
   {/if}
   {include file="CRM/common/formButtons.tpl" location="bottom"}
diff --git a/civicrm/templates/CRM/Activity/Form/ActivityLinks.tpl b/civicrm/templates/CRM/Activity/Form/ActivityLinks.tpl
index 1b734956cc05951903f7b806d5f40b9b792983e8..689b4e3cf1b2a8c060189ddb7791af54a7053de0 100644
--- a/civicrm/templates/CRM/Activity/Form/ActivityLinks.tpl
+++ b/civicrm/templates/CRM/Activity/Form/ActivityLinks.tpl
@@ -43,7 +43,7 @@
 {foreach from=$activityTypes key=k item=act}
 <li class="crm-activity-type_{$k}">
   <a href="{$act.url}" data-tab="activity">
-    <i class="crm-i {$act.icon}"></i> {$act.label}
+    <i class="crm-i {$act.icon}" aria-hidden="true"></i> {$act.label}
   </a>
 </li>
 {/foreach}
diff --git a/civicrm/templates/CRM/Activity/Form/ActivityView.tpl b/civicrm/templates/CRM/Activity/Form/ActivityView.tpl
index b19a243c02ab15aa6de62d50d250f951038ba4d6..4f68416e217b1e56855e9064e66c7874264884f4 100644
--- a/civicrm/templates/CRM/Activity/Form/ActivityView.tpl
+++ b/civicrm/templates/CRM/Activity/Form/ActivityView.tpl
@@ -22,7 +22,7 @@
        {/if}
        {if $values.mailingId}
            <tr>
-                <td class="label">{ts}With Contact{/ts}</td><td class="view-value"><a href="{$values.mailingId}" title="{ts}View Mailing Report{/ts}">&raquo;{ts}Mailing Report{/ts}</a></td>
+                <td class="label">{ts}With Contact{/ts}</td><td class="view-value"><a href="{$values.mailingId}" title="{ts}View Mailing Report{/ts}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i>{ts}Mailing Report{/ts}</a></td>
            </tr>
        {/if}
         <tr>
@@ -84,7 +84,7 @@
                                   {$mailingReport.mailing.body_text|mb_truncate:30|escape|nl2br}
                                   <br />
                                   {if $values.mailingId}
-                                    <strong><a class="crm-popup" href='{$textViewURL}'>&raquo; {ts}View complete message{/ts}</a></strong>
+                                    <strong><a class="crm-popup" href='{$textViewURL}'><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}View complete message{/ts}</a></strong>
                                   {/if}
                               </td>
                           </tr>
@@ -97,7 +97,7 @@
                                   {$mailingReport.mailing.body_html|mb_truncate:30|escape|nl2br}
                                   <br/>
                                   {if $values.mailingId}
-                                    <strong><a class="crm-popup" href='{$htmlViewURL}'>&raquo; {ts}View complete message{/ts}</a></strong>
+                                    <strong><a class="crm-popup" href='{$htmlViewURL}'><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}View complete message{/ts}</a></strong>
                                   {/if}
                               </td>
                           </tr>
@@ -130,4 +130,3 @@
      </table>
      <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location="bottom"}</div>
 </div>
-
diff --git a/civicrm/templates/CRM/Activity/Form/Task/Batch.tpl b/civicrm/templates/CRM/Activity/Form/Task/Batch.tpl
index fc002b78793203e1ec61ea35c6a5159adf50d5c3..992de310c27df4c25a4078687c0bf656e1be307f 100644
--- a/civicrm/templates/CRM/Activity/Form/Task/Batch.tpl
+++ b/civicrm/templates/CRM/Activity/Form/Task/Batch.tpl
@@ -21,7 +21,7 @@
            {/foreach}
 
              {foreach from=$fields item=field key=fieldName}
-                <td><img  src="{$config->resourceBase}i/copy.png" alt="{ts 1=$field.title}Click to copy %1 from row one to all rows.{/ts}" fname="{$field.name}" class="action-icon" title="{ts}Click here to copy the value in row one to ALL rows.{/ts}" />{$field.title}</td>
+                <td>{copyIcon name=$field.name title=$field.title}{$field.title}</td>
              {/foreach}
             </tr>
           </thead>
diff --git a/civicrm/templates/CRM/Activity/Import/Form/Preview.tpl b/civicrm/templates/CRM/Activity/Import/Form/Preview.tpl
index f0a3568a146b98a7c2e863f3943ed331a1145680..5b846a62cc13ac90579567a583816941aa20e4b5 100644
--- a/civicrm/templates/CRM/Activity/Import/Form/Preview.tpl
+++ b/civicrm/templates/CRM/Activity/Import/Form/Preview.tpl
@@ -48,7 +48,7 @@
         <td class="data">{$invalidRowCount}</td>
         <td class="explanation">{ts}Rows with invalid data in one or more fields. These rows will be skipped (not imported).{/ts}
             {if $invalidRowCount}
-                <div class="action-link"><a href="{$downloadErrorRecordsUrl}">&raquo; {ts}Download Errors{/ts}</a></div>
+                <div class="action-link"><a href="{$downloadErrorRecordsUrl}"><i class="crm-i fa-download" aria-hidden="true"></i> {ts}Download Errors{/ts}</a></div>
             {/if}
         </td>
     </tr>
diff --git a/civicrm/templates/CRM/Admin/Form/Options.tpl b/civicrm/templates/CRM/Admin/Form/Options.tpl
index b41ad148abc24d4a49f5353e0f465c94bf92e1ba..2a566c9b653ada3c07fbfc7953d4d1af84b3e8e0 100644
--- a/civicrm/templates/CRM/Admin/Form/Options.tpl
+++ b/civicrm/templates/CRM/Admin/Form/Options.tpl
@@ -58,7 +58,7 @@
           <td class="label">{$form.value.label}</td>
           <td>{$form.value.html}<br />
             {if $action == 2}
-              <span class="description"><i class="crm-i fa-exclamation-triangle"></i> {ts}Changing the Value field will unlink records which have been marked with this option. This change can not be undone except by restoring the previous value.{/ts}</span>
+              <span class="description"><i class="crm-i fa-exclamation-triangle" aria-hidden="true"></i> {ts}Changing the Value field will unlink records which have been marked with this option. This change can not be undone except by restoring the previous value.{/ts}</span>
             {/if}
           </td>
         </tr>
diff --git a/civicrm/templates/CRM/Admin/Form/Preferences/Display.tpl b/civicrm/templates/CRM/Admin/Form/Preferences/Display.tpl
index 2f86a7039dd6635a94ffa1d1eecd82e024ba2f65..049a5ebf9975f78a4a4fe002de5724784583f32b 100644
--- a/civicrm/templates/CRM/Admin/Form/Preferences/Display.tpl
+++ b/civicrm/templates/CRM/Admin/Form/Preferences/Display.tpl
@@ -162,7 +162,7 @@
         {$form.editor_id.html}
         &nbsp;
         <span class="crm-button crm-icon-button" style="display:inline-block;vertical-align:middle;float:none!important;">
-          <i class="crm-i fa-wrench" style="margin: 0 -18px 0 2px;"></i>
+          <i class="crm-i fa-wrench" style="margin: 0 -18px 0 2px;" aria-hidden="true"></i>
           {$form.ckeditor_config.html}
         </span>
       </td>
diff --git a/civicrm/templates/CRM/Admin/Form/Setting/Miscellaneous.tpl b/civicrm/templates/CRM/Admin/Form/Setting/Miscellaneous.tpl
index fc863329550ccf867f285ca8b9147ecfd995c706..68ace19f261d974b8db8f2e75acdb617dd86e916 100644
--- a/civicrm/templates/CRM/Admin/Form/Setting/Miscellaneous.tpl
+++ b/civicrm/templates/CRM/Admin/Form/Setting/Miscellaneous.tpl
@@ -77,10 +77,10 @@
       </tr>
     </table>
 
-    <h3>{ts}reCAPTCHA Keys{/ts}</h3>
-
+    <h3>{ts}reCAPTCHA v2{/ts}</h3>
     <div class="description">
-      {ts 1='href="https://www.google.com/recaptcha" target="_blank"'}reCAPTCHA is a free service that helps prevent automated abuse of your site. To use reCAPTCHA on public-facing CiviCRM forms: sign up at <a %1>Google's reCaptcha site</a>; enter the provided public and private reCAPTCHA keys here; then enable reCAPTCHA under Advanced Settings in any Profile.{/ts}
+      {ts 1='href="https://www.google.com/recaptcha" target="_blank"'}reCAPTCHA is a free service that helps prevent automated abuse of your site. To use it on public-facing CiviCRM forms: sign up at <a %1>Google's reCaptcha site</a>; enter the provided public and private keys here; then enable reCAPTCHA under Advanced Settings in any Profile.{/ts}
+      <br/><strong>{ts}Only the reCAPTCHA v2 checkbox type is supported.{/ts}</strong>
     </div>
     <table class="form-layout">
       <tr class="crm-miscellaneous-form-block-recaptchaPublicKey">
diff --git a/civicrm/templates/CRM/Admin/Form/Setting/Smtp.tpl b/civicrm/templates/CRM/Admin/Form/Setting/Smtp.tpl
index bde961cb5a93597df711a38cc0e3be48eb522cc4..789a85bd570be41cb68fc7d137be15f45de09090 100644
--- a/civicrm/templates/CRM/Admin/Form/Setting/Smtp.tpl
+++ b/civicrm/templates/CRM/Admin/Form/Setting/Smtp.tpl
@@ -93,7 +93,7 @@
     CRM.$(function($) {
       var mailSetting = $("input[name='outBound_option']:checked").val( );
 
-      var archiveWarning = "{/literal}{ts escape='js'}WARNING: You are switching from a testing mode (Redirect to Database) to a live mode. Check Mailings > Archived Mailings, and delete any test mailings that are not in Completed status prior to running the mailing cron job for the first time. This will ensure that test mailings are not actually sent out.{/ts}{literal}"
+      var archiveWarning = "{/literal}{ts escape='js'}WARNING: You are switching from a testing mode (Redirect to Database) to a live mode. Check Mailings > Archived Mailings, and delete any test mailings that are not in Completed status prior to running the mailing cron job for the first time. This will ensure that test mailings are not actually sent out.{/ts}{literal}";
 
         showHideMailOptions( $("input[name='outBound_option']:checked").val( ) ) ;
 
diff --git a/civicrm/templates/CRM/Admin/Form/Setting/Url.hlp b/civicrm/templates/CRM/Admin/Form/Setting/Url.hlp
index a2d1b1a86b247c09bbd709e09a3989245a952dce..e01986593a7791874842c809b4b5978a5399f811 100644
--- a/civicrm/templates/CRM/Admin/Form/Setting/Url.hlp
+++ b/civicrm/templates/CRM/Admin/Form/Setting/Url.hlp
@@ -70,6 +70,16 @@
 <p>{ts}You can modify the look and feel of CiviCRM by adding your own stylesheet. For small to medium sized modifications, use your css file to override some of the styles in civicrm.css. Or if you need to make drastic changes, you can choose to disable civicrm.css completely.{/ts}</p>
 {/htxt}
 
+{htxt id='id-defaultExternUrl'}
+<p>{ts}CiviCRM generates callback URLs for external services.{/ts}</p>
+<p>{ts}Some callback URLs are being migrated to a different style. During the transition, you may indicate a preferred style, such as:{/ts}</p>
+<ul>
+    <li>{ts}"Standalone Scripts" - In the traditional style, each callback URL is a standalone PHP script. You may wish to use this style if you need to maximize performance or if you need continuity.{/ts}</li>
+    <li>{ts}"Router" - In the newer style, each callback URL is defined like a normal CiviCRM page. You may wish to use this style for greater consistency or portability.{/ts}</li>
+</ul>
+<p>{ts}This setting only affects the default URL produced by "civicrm-core". Extensions and add-ons may override specific URLs.{/ts}</p>
+{/htxt}
+
 {htxt id='id-url_vars'}
 {ts}URL Variables{/ts}
   <table>
diff --git a/civicrm/templates/CRM/Admin/Form/Setting/Url.tpl b/civicrm/templates/CRM/Admin/Form/Setting/Url.tpl
index 3ecb7699b760d3699e74fdc76e3d9ebf5434090f..c6df140ff65ed0730365687e01d4bb3322acf1e1 100644
--- a/civicrm/templates/CRM/Admin/Form/Setting/Url.tpl
+++ b/civicrm/templates/CRM/Admin/Form/Setting/Url.tpl
@@ -77,6 +77,15 @@
             <p class="description font-red">{ts}{$verifySSL_description}{/ts}</p>
         </td>
     </tr>
+    <tr class="crm-url-form-block-defaultExternUrl">
+        <td class="label">
+            {$form.defaultExternUrl.label} {help id='id-defaultExternUrl'}
+        </td>
+        <td>
+            {$form.defaultExternUrl.html}<br/>
+            <p class="description font-red">{ts}{$settings_fields.defaultExternUrl.description}{/ts}</p>
+        </td>
+    </tr>
 </table>
 <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location="bottom"}</div>
 </div>
diff --git a/civicrm/templates/CRM/Admin/Form/WordReplacements.tpl b/civicrm/templates/CRM/Admin/Form/WordReplacements.tpl
index 1ae4182ddcf8a5bb3d7c55063c6634b955ecfb82..ead2551283cf6b52cb3a852a3e0059c160142b6c 100644
--- a/civicrm/templates/CRM/Admin/Form/WordReplacements.tpl
+++ b/civicrm/templates/CRM/Admin/Form/WordReplacements.tpl
@@ -45,7 +45,7 @@
               {/section}
             </tbody>
           </table>
-          &nbsp;&nbsp;&nbsp;<a class="action-item crm-hover-button buildStringOverrideRow" href="#"><i class="crm-i fa-plus-circle"></i> {ts}Add row{/ts}</a>
+          &nbsp;&nbsp;&nbsp;<a class="action-item crm-hover-button buildStringOverrideRow" href="#"><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Add row{/ts}</a>
         </td>
       </tr>
     </table>
diff --git a/civicrm/templates/CRM/Admin/Page/APIExplorer.js b/civicrm/templates/CRM/Admin/Page/APIExplorer.js
index 7b8e83d49964bb33e368207f4fa9867d898fd7b6..4d6df95b2b274c0db88901e3c8f3c13e2e837eb0 100644
--- a/civicrm/templates/CRM/Admin/Page/APIExplorer.js
+++ b/civicrm/templates/CRM/Admin/Page/APIExplorer.js
@@ -200,10 +200,10 @@
     var $row = $('tr:last-child', '#api-params');
     $('.api-chain-entity', $row).crmSelect2({
       formatSelection: function(item) {
-        return '<i class="crm-i fa-link"></i> API ' +
+        return '<i class="crm-i fa-link" aria-hidden="true"></i> API ' +
           ($(item.element).hasClass('strikethrough') ? '<span class="strikethrough">' + item.text + '</span>' : item.text);
       },
-      placeholder: '<i class="crm-i fa-link"></i> ' + ts('Entity'),
+      placeholder: '<i class="crm-i fa-link" aria-hidden="true"></i> ' + ts('Entity'),
       allowClear: false,
       escapeMarkup: function(m) {return m;}
     })
@@ -677,7 +677,7 @@
         .addClass('crm-error')
         .css('width', '82%')
         .attr('title', msg)
-        .before('<i class="crm-i fa-exclamation-triangle crm-i-red" title="'+msg+'"></i> ')
+        .before('<i class="crm-i fa-exclamation-triangle crm-i-red" title="'+msg+'" aria-hidden="true"></i> ')
         .tooltip();
     }
   }
diff --git a/civicrm/templates/CRM/Admin/Page/APIExplorer.tpl b/civicrm/templates/CRM/Admin/Page/APIExplorer.tpl
index 7234f9f13d0a6df93d1a628472c4196f1147897e..1e1b8eb497da1501bbc2e14e17dc516a28516f9e 100644
--- a/civicrm/templates/CRM/Admin/Page/APIExplorer.tpl
+++ b/civicrm/templates/CRM/Admin/Page/APIExplorer.tpl
@@ -210,13 +210,13 @@
 <div id="mainTabContainer">
   <ul>
     <li class="ui-corner-all" title="GUI to build and execute API calls">
-      <a href="#explorer-tab"><i class="crm-i fa-search"></i> {ts}Explorer{/ts}</a>
+      <a href="#explorer-tab"><i class="crm-i fa-search" aria-hidden="true"></i> {ts}Explorer{/ts}</a>
     </li>
     <li class="ui-corner-all" title="Auto-generated examples from the test suite">
-      <a href="#examples-tab"><i class="crm-i fa-book"></i> {ts}Examples{/ts}</a>
+      <a href="#examples-tab"><i class="crm-i fa-book" aria-hidden="true"></i> {ts}Examples{/ts}</a>
     </li>
     <li class="ui-corner-all" title="API source-code and code-level documentation">
-      <a href="#docs-tab"><i class="crm-i fa-code"></i> {ts}Code Docs{/ts}</a>
+      <a href="#docs-tab"><i class="crm-i fa-code" aria-hidden="true"></i> {ts}Code Docs{/ts}</a>
     </li>
   </ul>
 
@@ -263,9 +263,9 @@
         <tbody id="api-params"></tbody>
       </table>
       <div id="api-param-buttons" style="display: none;">
-        <a href="#" class="crm-hover-button" id="api-params-add"><i class="crm-i fa-plus"></i> {ts}Add Parameter{/ts}</a>
-        <a href="#" class="crm-hover-button" id="api-option-add"><i class="crm-i fa-cog"></i> {ts}Add Option{/ts}</a>
-        <a href="#" class="crm-hover-button" id="api-chain-add"><i class="crm-i fa-link"></i> {ts}Chain API Call{/ts}</a>
+        <a href="#" class="crm-hover-button" id="api-params-add"><i class="crm-i fa-plus" aria-hidden="true"></i> {ts}Add Parameter{/ts}</a>
+        <a href="#" class="crm-hover-button" id="api-option-add"><i class="crm-i fa-cog" aria-hidden="true"></i> {ts}Add Option{/ts}</a>
+        <a href="#" class="crm-hover-button" id="api-chain-add"><i class="crm-i fa-link" aria-hidden="true"></i> {ts}Chain API Call{/ts}</a>
         {help id="api-chain"}
       </div>
       <div id="api-generated-wraper">
@@ -286,7 +286,7 @@
       </div>
       <div class="crm-submit-buttons">
         <span class="crm-button crm-i-button">
-          <i class="crm-i fa-bolt"></i><input type="submit" value="{ts}Execute{/ts}" class="crm-form-submit" accesskey="S" title="{ts}Execute API call and display results{/ts}"/>
+          <i class="crm-i fa-bolt" aria-hidden="true"></i><input type="submit" value="{ts}Execute{/ts}" class="crm-form-submit" accesskey="S" title="{ts}Execute API call and display results{/ts}"/>
         </span>
       </div>
 
@@ -350,9 +350,9 @@
 <script type="text/template" id="api-param-tpl">
   <tr class="api-param-row">
     <td>
-      <i class="crm-i api-sort-handle fa-arrows"></i>
+      <i class="crm-i api-sort-handle fa-arrows" aria-hidden="true"></i>
       <input style="width: 90%;" class="crm-form-text api-param-name api-input" value="<%= name %>" placeholder="{ts}Parameter{/ts}" />
-      <div class="api-and-or"><span><span class="api-and">{ts}AND{/ts}</span> <i class="crm-i fa-toggle-on"></i> <span class="api-or">{ts}OR{/ts}</span></span></div>
+      <div class="api-and-or"><span><span class="api-and">{ts}AND{/ts}</span> <i class="crm-i fa-toggle-on" aria-hidden="true"></i> <span class="api-or">{ts}OR{/ts}</span></span></div>
     </td>
     <td>
       {literal}
@@ -371,7 +371,7 @@
     </td>
     <td>
       <input style="width: 85%;" class="crm-form-text api-param-value api-input" placeholder="{ts}Value{/ts}"/>
-      <a class="crm-hover-button api-param-remove" href="#"><i class="crm-i fa-times"></i></a>
+      <a class="crm-hover-button api-param-remove" href="#"><i class="crm-i fa-times" aria-hidden="true"></i></a>
     </td>
   </tr>
 </script>
@@ -399,7 +399,7 @@
     </td>
     <td>
       <input style="width: 85%;" class="crm-form-text api-option-value api-input" placeholder="{ts}Value{/ts}"/>
-      <a class="crm-hover-button api-param-remove" href="#"><i class="crm-i fa-times"></i></a>
+      <a class="crm-hover-button api-param-remove" href="#"><i class="crm-i fa-times" aria-hidden="true"></i></a>
     </td>
   </tr>
 </script>
@@ -407,7 +407,7 @@
 <script type="text/template" id="api-chain-tpl">
   <tr class="api-chain-row">
     <td>
-      <i class="crm-i api-sort-handle fa-arrows"></i>
+      <i class="crm-i api-sort-handle fa-arrows" aria-hidden="true"></i>
       <select style="width: 90%;" class="crm-form-select api-chain-entity">
         <option value=""></option>
         {foreach from=$entities.values item=entity}
@@ -424,7 +424,7 @@
     </td>
     <td>
       <input style="width: 85%;" class="crm-form-text api-param-value api-input" value="{ldelim}{rdelim}" placeholder="{ts}API Params{/ts}"/>
-      <a class="crm-hover-button api-param-remove" href="#"><i class="crm-i fa-times"></i></a>
+      <a class="crm-hover-button api-param-remove" href="#"><i class="crm-i fa-times" aria-hidden="true"></i></a>
     </td>
   </tr>
 </script>
@@ -444,7 +444,7 @@
   <ul class="fa-ul">
     <% _.forEach(joins, function(join, name) { %>
       <li <% if(join.checked) { %>class="join-enabled"<% } if(join.disabled) { %>class="join-not-available"<% }%>>
-        <i class="fa-li crm-i fa-reply fa-rotate-180"></i>
+        <i class="fa-li crm-i fa-reply fa-rotate-180" aria-hidden="true"></i>
         <label for="select-join-<%= name %>" class="api-checkbox-label">
           <input type="checkbox" id="select-join-<%= name %>" value="<%= name %>" data-entity="<%= join.entity %>" <% if(join.checked) { %>checked<% } if(join.disabled) { %>disabled<% } %>/>
           <%- join.title %>
diff --git a/civicrm/templates/CRM/Admin/Page/Access.tpl b/civicrm/templates/CRM/Admin/Page/Access.tpl
index bbb735156fbcd042f438e4c614b77a686b415dac..e71a8227323fc957f620ada396b0c33bac7682b1 100644
--- a/civicrm/templates/CRM/Admin/Page/Access.tpl
+++ b/civicrm/templates/CRM/Admin/Page/Access.tpl
@@ -18,20 +18,20 @@
 
     <table class="report">
         <tr>
-            <td class="nowrap"><a href="{$ufAccessURL}" {$jAccessParams} id="adminAccess">&raquo; {ts 1=$config->userFramework}%1 Access Control{/ts}</a></td>
+            <td class="nowrap"><a href="{$ufAccessURL}" {$jAccessParams} id="adminAccess"><i class="crm-i fa-chevron-right fa-fw" aria-hidden="true"></i> {ts 1=$config->userFramework}%1 Access Control{/ts}</a></td>
             <td>{ts}Grant access to CiviCRM components and other CiviCRM permissions.{/ts}</td>
         </tr>
         <tr><td colspan="2" class="separator"><strong>{ts}Use following steps if you need to control View and/or Edit permissions for specific contact groups, specific profiles or specific custom data fields.{/ts}</strong></td></tr>
     <tr>
-        <td class="nowrap"><a href="{crmURL p='civicrm/admin/options/acl_role' q="reset=1"}" id="editACLRoles">&raquo; {ts}1. Manage Roles{/ts}</a></td>
+        <td class="nowrap"><a href="{crmURL p='civicrm/admin/options/acl_role' q="reset=1"}" id="editACLRoles"><i class="crm-i fa-users fa-fw" aria-hidden="true"></i> {ts}1. Manage Roles{/ts}</a></td>
         <td>{ts}Each CiviCRM ACL Role is assigned a set of permissions. Use this link to create or edit the different roles needed for your site.{/ts}</td>
     </tr>
     <tr>
-        <td class="nowrap"><a href="{crmURL p='civicrm/acl/entityrole' q="reset=1"}" id="editRoleAssignments">&raquo; {ts}2. Assign Users to CiviCRM ACL Roles{/ts}</a></td>
+        <td class="nowrap"><a href="{crmURL p='civicrm/acl/entityrole' q="reset=1"}" id="editRoleAssignments"><i class="crm-i fa-user-plus fa-fw" aria-hidden="true"></i> {ts}2. Assign Users to CiviCRM ACL Roles{/ts}</a></td>
         <td>{ts}Once you have defined CiviCRM ACL Roles and granted ACLs to those Roles, use this link to assign users to role(s).{/ts}</td>
     </tr>
     <tr>
-        <td class="nowrap"><a href="{crmURL p='civicrm/acl' q="reset=1"}" id="editACLs">&raquo; {ts}3. Manage ACLs{/ts}</a></td>
+        <td class="nowrap"><a href="{crmURL p='civicrm/acl' q="reset=1"}" id="editACLs"><i class="crm-i fa-id-card-o fa-fw" aria-hidden="true"></i> {ts}3. Manage ACLs{/ts}</a></td>
         <td>{ts}ACLs define permission to do an operation on a set of data, and grant that permission to a CiviCRM ACL Role. Use this link to create or edit the ACLs for your site.{/ts}</td>
     </tr>
     </table>
diff --git a/civicrm/templates/CRM/Admin/Page/Admin.tpl b/civicrm/templates/CRM/Admin/Page/Admin.tpl
index d88e58c9239372b5ae5f7634ddeb4b803bf027bc..8c9154b7f8f1288d0de34dc25300c3e29c9a425f 100644
--- a/civicrm/templates/CRM/Admin/Page/Admin.tpl
+++ b/civicrm/templates/CRM/Admin/Page/Admin.tpl
@@ -18,61 +18,18 @@
     </div>
 {/if}
 
-<div id="help" class="description section-hidden-border">
-{capture assign=plusImg}<img src="{$config->resourceBase}i/TreePlus.gif" alt="{ts}plus sign{/ts}" style="vertical-align: bottom; height: 20px; width: 20px;" />{/capture}
-{ts 1=$plusImg}Administer your CiviCRM site using the links on this page. Click %1 for descriptions of the options in each section.{/ts}
-</div>
-
-{strip}
 <div class="crm-content-block">
-{foreach from=$adminPanel key=groupName item=group name=adminLoop}
- <div id="id_{$groupName}_show" class="section-hidden{if $smarty.foreach.adminLoop.last eq false} section-hidden-border{/if}">
-    <table class="form-layout">
-    <tr>
-        <td width="20%" class="font-size11pt" style="vertical-align: top;">{$group.show} {$group.title}</td>
-        <td width="80%" style="white-space: nowrap;">
-
-            <table class="form-layout" width="100%">
-            <tr>
-         <td width="50%" style="padding: 0px;">
-                {foreach from=$group.fields item=panelItem  key=panelName name=groupLoop}
-                    &raquo;&nbsp;<a href="{$panelItem.url}"{if $panelItem.extra} {$panelItem.extra}{/if} id="idc_{$panelItem.id}">{$panelItem.title}</a><br />
-                    {if $smarty.foreach.groupLoop.iteration EQ $group.perColumn}
-                         </td><td width="50%" style="padding: 0px;">
-                    {/if}
-                {/foreach}
-                </td>
-            </tr>
-            </table>
-        </td>
-    </tr>
-    </table>
- </div>
-
- <div id="id_{$groupName}">
-    <fieldset><legend><strong>{$group.hide}{$group.title}</strong></legend>
-        <table class="form-layout">
-
-        {foreach from=$group.fields item=panelItem  key=panelName name=groupLoop}
-            <tr class="{cycle values="odd-row,even-row" name=$groupName}">
-                <td style="vertical-align: top; width:24px;">
-                    <a href="{$panelItem.url}"{if $panelItem.extra} {$panelItem.extra}{/if} ><img src="{$config->resourceBase}i/{if $panelItem.icon}{$panelItem.icon}{else}admin/small/option.png{/if}" alt="{$panelItem.title|escape}"/></a>
-                </td>
-                <td class="report font-size11pt" style="vertical-align: text-top;" width="20%">
-                    <a href="{$panelItem.url}"{if $panelItem.extra} {$panelItem.extra}{/if} id="id_{$panelItem.id}">{$panelItem.title}</a>
-                </td>
-                <td class="description"  style="vertical-align: text-top;" width="75%">
-                    {$panelItem.desc}
-                </td>
-            </tr>
-        {/foreach}
-
-        </table>
-    </fieldset>
+{foreach from=$adminPanel key=groupName item=group}
+<div id="admin-section-{$groupName}">
+  <h3>{$group.title}</h3>
+  <div class="admin-section-items">
+    {foreach from=$group.fields item=panelItem  key=panelName}
+    <dl>
+      <dt><a href="{$panelItem.url}"{if $panelItem.extra} {$panelItem.extra}{/if} id="id_{$panelItem.id}">{$panelItem.title}</a></dt>
+      <dd>{$panelItem.desc}</dd>
+    </dl>
+    {/foreach}
   </div>
+</div>
 {/foreach}
-{/strip}
-
-{* Include Javascript to hide and display the appropriate blocks as directed by the php code *}
-{include file="CRM/common/showHide.tpl"}
 </div>
diff --git a/civicrm/templates/CRM/Admin/Page/CKEditorConfig.tpl b/civicrm/templates/CRM/Admin/Page/CKEditorConfig.tpl
index abc2c1fc3c5d48494b2b8d0d258ef89dd10c3512..14af566be27a39197e590abce48a285869c94c5e 100644
--- a/civicrm/templates/CRM/Admin/Page/CKEditorConfig.tpl
+++ b/civicrm/templates/CRM/Admin/Page/CKEditorConfig.tpl
@@ -100,10 +100,10 @@
 
     <div class="crm-submit-buttons">
       <span class="crm-button crm-i-button">
-        <i class="crm-i fa-wrench"></i> <input type="submit" value="{ts}Save{/ts}" name="save" class="crm-form-submit" accesskey="S"/>
+        <i class="crm-i fa-wrench" aria-hidden="true"></i> <input type="submit" value="{ts}Save{/ts}" name="save" class="crm-form-submit" accesskey="S"/>
       </span>
       <span class="crm-button crm-i-button">
-        <i class="crm-i fa-times"></i> <input type="submit" value="{ts}Revert to Default{/ts}" name="revert" class="crm-form-submit" onclick="return confirm('{$revertConfirm}');"/>
+        <i class="crm-i fa-times" aria-hidden="true"></i> <input type="submit" value="{ts}Revert to Default{/ts}" name="revert" class="crm-form-submit" onclick="return confirm('{$revertConfirm}');"/>
       </span>
     </div>
     <input type="hidden" value="{$preset}" name="preset" />
diff --git a/civicrm/templates/CRM/Admin/Page/Job.tpl b/civicrm/templates/CRM/Admin/Page/Job.tpl
index c0812feff152bbb4f2554864174e78675212caf3..cb2d310b7c79c87a85b3a02fb4b322bac321a9bd 100644
--- a/civicrm/templates/CRM/Admin/Page/Job.tpl
+++ b/civicrm/templates/CRM/Admin/Page/Job.tpl
@@ -67,7 +67,7 @@
         {ts}There are no jobs configured.{/ts}
      </div>
      <div class="action-link">
-       <a href="{crmURL p='civicrm/admin/job' q="action=add&reset=1"}" id="newJob-nojobs" class="button"><span><i class="crm-i fa-plus-circle"></i> {ts}Add New Scheduled Job{/ts}</span></a>
+       <a href="{crmURL p='civicrm/admin/job' q="action=add&reset=1"}" id="newJob-nojobs" class="button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Add New Scheduled Job{/ts}</span></a>
      </div>
 
 {/if}
diff --git a/civicrm/templates/CRM/Admin/Page/JobLog.tpl b/civicrm/templates/CRM/Admin/Page/JobLog.tpl
index d3547dd03703c07efd0bc34198ab254022fbc702..12b1b687b778d8174d5a7df13a4bf8b775fa4a28 100644
--- a/civicrm/templates/CRM/Admin/Page/JobLog.tpl
+++ b/civicrm/templates/CRM/Admin/Page/JobLog.tpl
@@ -18,7 +18,7 @@
 {/if}
 
   <div class="action-link">
-    <a href="{crmURL p='civicrm/admin/job' q="reset=1"}" id="jobsList-top" class="button"><span><i class="crm-i fa-chevron-left"></i> {ts}Back to Scheduled Jobs Listing{/ts}</span></a>
+    <a href="{crmURL p='civicrm/admin/job' q="reset=1"}" id="jobsList-top" class="button"><span><i class="crm-i fa-chevron-left" aria-hidden="true"></i> {ts}Back to Scheduled Jobs Listing{/ts}</span></a>
   </div>
 
 {if $rows}
@@ -59,6 +59,6 @@
 {/if}
 
   <div class="action-link">
-    <a href="{crmURL p='civicrm/admin/job' q="reset=1"}" id="jobsList-bottom" class="button"><span><i class="crm-i fa-chevron-left"></i> {ts}Back to Scheduled Jobs Listing{/ts}</span></a>
+    <a href="{crmURL p='civicrm/admin/job' q="reset=1"}" id="jobsList-bottom" class="button"><span><i class="crm-i fa-chevron-left" aria-hidden="true"></i> {ts}Back to Scheduled Jobs Listing{/ts}</span></a>
   </div>
 </div>
diff --git a/civicrm/templates/CRM/Admin/Page/LabelFormats.tpl b/civicrm/templates/CRM/Admin/Page/LabelFormats.tpl
index 2baf4ae13d4b707e75fd5cd8c975bfe96cf02de3..d2dd488eea672277a83b3552d3aff4dbce9deab2 100644
--- a/civicrm/templates/CRM/Admin/Page/LabelFormats.tpl
+++ b/civicrm/templates/CRM/Admin/Page/LabelFormats.tpl
@@ -53,8 +53,7 @@
               <td class="crm-labelFormat-name">{$row.groupName}</td>
               <td class="crm-labelFormat-order nowrap">{$row.weight}</td>
               <td class="crm-labelFormat-description">{$row.grouping}</td>
-              <td class="crm-labelFormat-is_default">{if $row.is_default eq 1}
-              <img src="{$config->resourceBase}i/check.gif" alt="{ts}Default{/ts}"/>{/if}&nbsp;</td>
+              <td class="crm-labelFormat-is_default">{icon condition=$row.is_default}{ts}Default{/ts}{/icon}&nbsp;</td>
               <td class="crm-labelFormat-is_reserved">{if $row.is_reserved eq 1}{ts}Yes{/ts}{else}{ts}No{/ts}{/if}
                 &nbsp;</td>
               <td>{$row.action|replace:'xx':$row.id}</td>
diff --git a/civicrm/templates/CRM/Admin/Page/LocationType.tpl b/civicrm/templates/CRM/Admin/Page/LocationType.tpl
index 5d891e12b20e70d13d1375f5aabe32563a2d4ecd..1eb9600caa00c4f765cf88226b7a6802ad8d3205 100644
--- a/civicrm/templates/CRM/Admin/Page/LocationType.tpl
+++ b/civicrm/templates/CRM/Admin/Page/LocationType.tpl
@@ -40,7 +40,7 @@
         <td class="crmf-vcard_name crm-editable">{$row.vcard_name}</td>
         <td class="crmf-description crm-editable">{$row.description}</td>
         <td id="row_{$row.id}_status" class="crmf-is_active">{if $row.is_active eq 1} {ts}Yes{/ts} {else} {ts}No{/ts} {/if}</td>
-        <td class="crmf-is_default" >{if $row.is_default eq 1}<img src="{$config->resourceBase}i/check.gif" alt="{ts}Default{/ts}" />{/if}&nbsp;</td>
+        <td class="crmf-is_default">{icon condition=$row.is_default}{ts}Default{/ts}{/icon}&nbsp;</td>
         <td>{$row.action|replace:'xx':$row.id}</td>
     </tr>
     {/foreach}
diff --git a/civicrm/templates/CRM/Admin/Page/Navigation.tpl b/civicrm/templates/CRM/Admin/Page/Navigation.tpl
index 76b9f951127ab3edab8a1a3c4ab69109c0278211..b101137c5b5faf9467ee33ee10102af0904b3265 100644
--- a/civicrm/templates/CRM/Admin/Page/Navigation.tpl
+++ b/civicrm/templates/CRM/Admin/Page/Navigation.tpl
@@ -29,7 +29,7 @@
     <div>
       <a href="#" class="nav-reset crm-hover-button">
         {* TODO: fa-broom would be better, but not implemented yet. https://github.com/FortAwesome/Font-Awesome/issues/239 *}
-        <i class="crm-i fa-undo"></i> {ts}Cleanup reports menu{/ts}
+        <i class="crm-i fa-undo" aria-hidden="true"></i> {ts}Cleanup reports menu{/ts}
       </a>
     </div>
     <div class="spacer"></div>
diff --git a/civicrm/templates/CRM/Admin/Page/OptionGroup.tpl b/civicrm/templates/CRM/Admin/Page/OptionGroup.tpl
index 87ddaf9846a9be2d1c504de79aad1026710c689d..ca9abf725160cfb20d62b7fe32b4d19f9a9d14f8 100644
--- a/civicrm/templates/CRM/Admin/Page/OptionGroup.tpl
+++ b/civicrm/templates/CRM/Admin/Page/OptionGroup.tpl
@@ -13,7 +13,7 @@
 {else}
 <div class="help">
     {ts}CiviCRM stores configurable choices for various drop-down fields as 'option groups'. You can click <strong>Options</strong> to view the available choices.{/ts}
-    <p><i class="crm-i fa-exclamation-triangle"></i> {ts}WARNING: Many option groups are used programatically and values should be added or modified with caution.{/ts}</p>
+    <p><i class="crm-i fa-exclamation-triangle" aria-hidden="true"></i> {ts}WARNING: Many option groups are used programatically and values should be added or modified with caution.{/ts}</p>
 </div>
 {/if}
 
diff --git a/civicrm/templates/CRM/Admin/Page/Options.tpl b/civicrm/templates/CRM/Admin/Page/Options.tpl
index 6c8a0e709dc39eb5f7eb382cbc1ce653b6b49f31..1ecc70b42ffcbc5ad8d8af53b02cc23519a19bc7 100644
--- a/civicrm/templates/CRM/Admin/Page/Options.tpl
+++ b/civicrm/templates/CRM/Admin/Page/Options.tpl
@@ -115,7 +115,7 @@
         {foreach from=$rows item=row}
           <tr id="option_value-{$row.id}" class="crm-admin-options crm-admin-options_{$row.id} crm-entity {cycle values="odd-row,even-row"}{if NOT $row.is_active} disabled{/if}">
             {if !empty($hasIcons)}
-              <td class="crm-admin-options-icon"><i class="crm-i {$row.icon}"></i></td>
+              <td class="crm-admin-options-icon"><i class="crm-i {$row.icon}" aria-hidden="true"></i></td>
             {/if}
             {if $showComponent}
               <td class="crm-admin-options-component_name">{$row.component_name}</td>
@@ -131,13 +131,13 @@
               <td>{$row.financial_account}</td>
             {/if}
             {if $showCounted}
-              <td class="center crm-admin-options-filter">{if $row.filter eq 1}<img src="{$config->resourceBase}i/check.gif" alt="{ts}Counted{/ts}" />{/if}</td>
+              <td class="center crm-admin-options-filter">{icon condition=$row.filter}{ts}Counted{/ts}{/icon}</td>
             {/if}
             {if $showVisibility}<td class="crm-admin-visibility_label">{$row.visibility_label}</td>{/if}
             <td class="crm-admin-options-description crm-editable" data-field="description" data-type="textarea">{$row.description}</td>
             <td class="nowrap crm-admin-options-order">{$row.weight}</td>
             {if $showIsDefault}
-              <td class="crm-admin-options-is_default" align="center">{if $row.is_default eq 1}<img src="{$config->resourceBase}i/check.gif" alt="{ts}Default{/ts}" />{/if}&nbsp;</td>
+              <td class="crm-admin-options-is_default" align="center">{icon condition=$row.is_default}{ts}Default{/ts}{/icon}&nbsp;</td>
             {/if}
             <td class="crm-admin-options-is_reserved">{if $row.is_reserved eq 1} {ts}Yes{/ts} {else} {ts}No{/ts} {/if}</td>
             <td class="crm-admin-options-is_active" id="row_{$row.id}_status">{if $row.is_active eq 1} {ts}Yes{/ts} {else} {ts}No{/ts} {/if}</td>
diff --git a/civicrm/templates/CRM/Admin/Page/ParticipantStatusType.tpl b/civicrm/templates/CRM/Admin/Page/ParticipantStatusType.tpl
index 620e411f7102635a510a0ed7d376206a24731e8d..92e1eedec30220ea1a012b2c9e96a8ca5a060f28 100644
--- a/civicrm/templates/CRM/Admin/Page/ParticipantStatusType.tpl
+++ b/civicrm/templates/CRM/Admin/Page/ParticipantStatusType.tpl
@@ -33,9 +33,9 @@
           <td class="crmf-label crm-editable" data-field="label">{$row.label}</td>
           <td class="crmf-name">{$row.name} ({$row.id})</td>
           <td class="crmf-class {if !$row.is_reserved} crm-editable {/if}" data-type="select">{$row.class}</td>
-          <td class="center crmf-is_reserved">{if $row.is_reserved}<img src="{$config->resourceBase}i/check.gif" alt="{ts}Reserved{/ts}" />{/if}</td>
+          <td class="center crmf-is_reserved">{icon condition=$row.is_reserved}{ts}Reserved{/ts}{/icon}</td>
         <td id="row_{$row.id}_status" class="crmf-is_active">{if $row.is_active eq 1} {ts}Yes{/ts} {else} {ts}No{/ts} {/if}</td>
-          <td class="center crmf-is_counted">{if $row.is_counted} <img src="{$config->resourceBase}i/check.gif" alt="{ts}Counted{/ts}" />{/if}</td>
+          <td class="center crmf-is_counted">{icon condition=$row.is_counted}{ts}Counted{/ts}{/icon}</td>
           <td class="crmf-weight">{$row.weight}</td>
           <td class="crmf-visibility">{$row.visibility}</td>
           <td>{$row.action|replace:'xx':$row.id}</td>
diff --git a/civicrm/templates/CRM/Admin/Page/PaymentProcessor.tpl b/civicrm/templates/CRM/Admin/Page/PaymentProcessor.tpl
index 308e508d9e1780770c3eed22a77167b2e25508fe..ba6d55da20f0a335cea327f70853104b13596bc2 100644
--- a/civicrm/templates/CRM/Admin/Page/PaymentProcessor.tpl
+++ b/civicrm/templates/CRM/Admin/Page/PaymentProcessor.tpl
@@ -42,8 +42,7 @@
             <td class="crmf-description">{$row.description}</td>
             <td class="crmf-financial_account_id">{$row.financialAccount}</td>
             <td class="crmf-is_active center">{if $row.is_active eq 1} {ts}Yes{/ts} {else} {ts}No{/ts} {/if}</td>
-            <td class="crmf-is_default center">
-              {if $row.is_default eq 1}<img src="{$config->resourceBase}i/check.gif" alt="{ts}Default{/ts}"/>{/if}&nbsp;
+            <td class="crmf-is_default center">{icon condition=$row.is_default}{ts}Default{/ts}{/icon}&nbsp;
             </td>
             <td>{$row.action|replace:'xx':$row.id}</td>
         </tr>
diff --git a/civicrm/templates/CRM/Admin/Page/PaymentProcessorType.tpl b/civicrm/templates/CRM/Admin/Page/PaymentProcessorType.tpl
index b1261e7dac492ed2c7caa59ec8f06f28b07df0c3..7580755844c99b0f72911842767b874f7a4f775a 100644
--- a/civicrm/templates/CRM/Admin/Page/PaymentProcessorType.tpl
+++ b/civicrm/templates/CRM/Admin/Page/PaymentProcessorType.tpl
@@ -36,7 +36,7 @@
           <td class="crm-paymentProcessorType-title crm-editable" data-field="title">{$row.title}</td>
             <td class="crm-paymentProcessorType-description">{$row.description}</td>
           <td id="row_{$row.id}_status" class="crm-paymentProcessorType-is_active">{if $row.is_active eq 1} {ts}Yes{/ts} {else} {ts}No{/ts} {/if}</td>
-            <td class="crm-paymentProcessorType-is_default">{if $row.is_default eq 1}<img src="{$config->resourceBase}i/check.gif" alt="{ts}Default{/ts}" />{/if}&nbsp;</td>
+            <td class="crm-paymentProcessorType-is_default">{icon condition=$row.is_default}{ts}Default{/ts}{/icon}&nbsp;</td>
           <td>{$row.action}</td>
         </tr>
         {/foreach}
@@ -45,7 +45,7 @@
 
         {if $action ne 1 and $action ne 2}
         <div class="action-link">
-          <a href="{crmURL q="action=add&reset=1"}" id="newPaymentProcessor">&raquo; {ts}New Payment Processor{/ts}</a>
+          <a href="{crmURL q="action=add&reset=1"}" id="newPaymentProcessor"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}New Payment Processor{/ts}</a>
           {crmButton p="civicrm/admin" q="reset=1" class="cancel" icon="times"}{ts}Done{/ts}{/crmButton}
         </div>
         {/if}
diff --git a/civicrm/templates/CRM/Admin/Page/PdfFormats.tpl b/civicrm/templates/CRM/Admin/Page/PdfFormats.tpl
index 5b58ded34b2a0221a04d7db720f556d9ff9a0142..1b419c5752bdd62eabb55d024a971e9ac2eb8849 100644
--- a/civicrm/templates/CRM/Admin/Page/PdfFormats.tpl
+++ b/civicrm/templates/CRM/Admin/Page/PdfFormats.tpl
@@ -50,7 +50,7 @@
         <tr id="row_{$row.id}" class="crm-pdfFormat {cycle values="odd-row,even-row"} {$row.class}">
             <td class="crm-pdfFormat-name">{$row.name}</td>
             <td class="crm-pdfFormat-description">{$row.description}</td>
-            <td class="crm-pdfFormat-is_default">{if $row.is_default eq 1}<img src="{$config->resourceBase}i/check.gif" alt="{ts}Default{/ts}" />{/if}&nbsp;</td>
+            <td class="crm-pdfFormat-is_default">{icon condition=$row.is_default}{ts}Default{/ts}{/icon}&nbsp;</td>
           <td class="crm-pdfFormat-order nowrap">{$row.weight}</td>
           <td>{$row.action|replace:'xx':$row.id}</td>
         </tr>
diff --git a/civicrm/templates/CRM/Admin/Page/Setting.tpl b/civicrm/templates/CRM/Admin/Page/Setting.tpl
index 2a792694588c8a901456fb2a813cf4db96b93751..fe2a904ad5e354b4f84b52ae2cb25b82f5c682df 100644
--- a/civicrm/templates/CRM/Admin/Page/Setting.tpl
+++ b/civicrm/templates/CRM/Admin/Page/Setting.tpl
@@ -13,72 +13,72 @@
 </div>
 <table class="report">
 <tr>
-    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/component' q='reset=1'}" id="idComponents">&raquo; {ts}Enable Components{/ts}</a></td>
+    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/component' q='reset=1'}" id="idComponents"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Enable Components{/ts}</a></td>
     <td>{ts}Enable CiviContribute, CiviPledge, CiviEvent, CiviMember, CiviMail, CiviCase, CiviReport and/or CiviGrant components.{/ts}</td>
 </tr>
 
 <tr>
-    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/preferences/display' q='reset=1'}" id="idPreferences">&raquo; {ts}Site Preferences{/ts}</a></td>
+    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/preferences/display' q='reset=1'}" id="idPreferences"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Site Preferences{/ts}</a></td>
     <td>{ts}Configure screen and form elements for Viewing Contacts, Editing Contacts, Advanced Search, Contact Dashboard and WYSIWYG Editor.{/ts}</td>
 </tr>
 
 <tr>
-    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/path' q='reset=1'}" id="idPath">&raquo; {ts}Directories{/ts}</a></td>
+    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/path' q='reset=1'}" id="idPath"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Directories{/ts}</a></td>
     <td>{ts}Configure directories in your file system for temporary uploads, images, custom files and custom templates.{/ts}</td>
 </tr>
 
 <tr>
-    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/url' q='reset=1'}" id="idUrls">&raquo; {ts}Resource URLs{/ts}</a></td>
+    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/url' q='reset=1'}" id="idUrls"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Resource URLs{/ts}</a></td>
     <td>{ts}URLs used to access CiviCRM resources (CSS files, Javascript files, images, etc.). Enable secure URLs.{/ts}</td>
 </tr>
 
 <tr>
-    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/smtp' q='reset=1'}" id="idSMTP">&raquo; {ts}Outbound Email (SMTP/Sendmail){/ts}</a></td>
+    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/smtp' q='reset=1'}" id="idSMTP"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Outbound Email (SMTP/Sendmail){/ts}</a></td>
     <td>{ts}Settings for outbound email - either SMTP server, port and authentication or Sendmail path and argument.{/ts}</td>
 </tr>
 
 <tr>
-    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/mapping' q='reset=1'}" id="idMapping">&raquo; {ts}Mapping and Geocoding{/ts}</a></td>
+    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/mapping' q='reset=1'}" id="idMapping"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Mapping and Geocoding{/ts}</a></td>
     <td>{ts}Configure a mapping provider (e.g. Google or Yahoo) to display maps for contact addresses and event locations.{/ts}</td>
 </tr>
 
 <tr>
-    <td class="nowrap"><a href="{crmURL p='civicrm/admin/paymentProcessor' q='reset=1'}" id="idPayments">&raquo; {ts}Payment Processors{/ts}</a></td>
+    <td class="nowrap"><a href="{crmURL p='civicrm/admin/paymentProcessor' q='reset=1'}" id="idPayments"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Payment Processors{/ts}</a></td>
     <td>{ts}Select and configure one or more payment processing services for online contributions, events and / or membership fees.{/ts}</td>
 </tr>
 
 <tr>
-    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/localization' q='reset=1'}" id="idLocale">&raquo; {ts}Localization{/ts}</a></td>
+    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/localization' q='reset=1'}" id="idLocale"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Localization{/ts}</a></td>
     <td>{ts}Localization settings include user language, default currency and available countries for address input.{/ts}</td>
 </tr>
 
 <tr>
-    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/preferences/address' q='reset=1'}" id="idAddress">&raquo; {ts}Address Settings{/ts}</a></td>
+    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/preferences/address' q='reset=1'}" id="idAddress"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Address Settings{/ts}</a></td>
     <td>{ts}Format addresses in mailing labels, input forms and screen display. Configure optional Address Standardization provider.{/ts}</td>
 </tr>
 
 <tr>
-    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/search' q='reset=1'}" id="idMisc">&raquo; {ts}Search Settings{/ts}</a></td>
+    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/search' q='reset=1'}" id="idMisc"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Search Settings{/ts}</a></td>
     <td>{ts}Configure Contact Search behavior.{/ts}</td>
 </tr>
 
 <tr>
-    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/date' q='reset=1'}" id="idDates">&raquo; {ts}Date Formats{/ts}</a></td>
+    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/date' q='reset=1'}" id="idDates"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Date Formats{/ts}</a></td>
     <td>{ts}Configure input and display formats for Date fields.{/ts}</td>
 </tr>
 
 <tr>
-    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/uf' q='reset=1'}" id="idUF">&raquo; {ts 1=$config->userFramework}%1 Integration Settings{/ts}</a></td>
+    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/uf' q='reset=1'}" id="idUF"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts 1=$config->userFramework}%1 Integration Settings{/ts}</a></td>
     <td>{ts 1=$config->userFramework}%1 version and user table name.{/ts}</td>
 </tr>
 
 <tr>
-    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/misc' q='reset=1'}" id="idMisc">&raquo; {ts}Miscellaneous Settings{/ts}</a></td>
+    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/misc' q='reset=1'}" id="idMisc"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Miscellaneous Settings{/ts}</a></td>
     <td>{ts}Dashboard caching time, move to trash / undelete, change logging, version checking and reCAPTCHA (prevents automated abuse of public forms).{/ts}</td>
 </tr>
 
 <tr>
-    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/debug' q='reset=1'}" id="idDebug">&raquo; {ts}Debugging{/ts}</a></td>
+    <td class="nowrap"><a href="{crmURL p='civicrm/admin/setting/debug' q='reset=1'}" id="idDebug"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Debugging{/ts}</a></td>
     <td>{ts}Enable debugging features including display of template variables and backtracing.{/ts}</td>
 </tr>
 </table>
diff --git a/civicrm/templates/CRM/Badge/Form/Layout.tpl b/civicrm/templates/CRM/Badge/Form/Layout.tpl
index da1b73149e96ea2a0a4a5f6b359176fce7aaa78b..3aeda9184fc8fece5461cd7729b77bcab7ca6a32 100644
--- a/civicrm/templates/CRM/Badge/Form/Layout.tpl
+++ b/civicrm/templates/CRM/Badge/Form/Layout.tpl
@@ -36,7 +36,7 @@
          <table>
            <tr>
             <td>{$form.image_1.html}
-               <a href="#" class="crm-hover-button clear-image" title="{ts}Clear{/ts}"><i class="crm-i fa-times"></i></a>
+               <a href="#" class="crm-hover-button clear-image" title="{ts}Clear{/ts}"><i class="crm-i fa-times" aria-hidden="true"></i></a>
              <br/>
              <span class="description">{ts}Click above and select a file by double clicking on it.{/ts}</span>
             </td>
@@ -56,7 +56,7 @@
          <table>
           <tr>
            <td>{$form.image_2.html}
-              <a href="#" class="crm-hover-button clear-image" title="{ts}Clear{/ts}"><i class="crm-i fa-times"></i></a>
+              <a href="#" class="crm-hover-button clear-image" title="{ts}Clear{/ts}"><i class="crm-i fa-times" aria-hidden="true"></i></a>
             <br/>
             <span class="description">{ts}Click above and select a file by double clicking on it.{/ts}</span>
            </td>
diff --git a/civicrm/templates/CRM/Badge/Page/Layout.tpl b/civicrm/templates/CRM/Badge/Page/Layout.tpl
index a2ab0d69d2bfa10b8b8cd52fc3ed1ca96204b6fd..a69b549b34743ce264edd15373539664d27ef015 100644
--- a/civicrm/templates/CRM/Badge/Page/Layout.tpl
+++ b/civicrm/templates/CRM/Badge/Page/Layout.tpl
@@ -38,10 +38,7 @@
               <td id="row_{$row.id}_status" class="crm-badge-layout-is_active">
                 {if $row.is_active eq 1} {ts}Yes{/ts} {else} {ts}No{/ts} {/if}
               </td>
-              <td class="crm-badge-layout-is_default">
-                {if $row.is_default eq 1}
-                <img src="{$config->resourceBase}i/check.gif" alt="{ts}Default{/ts}"/>
-                {/if}&nbsp;
+              <td class="crm-badge-layout-is_default">{icon condition=$row.is_default}{ts}Default{/ts}{/icon}&nbsp;
               </td>
               <td>{$row.action|replace:'xx':$row.id}</td>
             </tr>
diff --git a/civicrm/templates/CRM/Batch/Form/Entry.tpl b/civicrm/templates/CRM/Batch/Form/Entry.tpl
index 31c3e6963e74356ec8cddcc9ad65eab8d0f7a71b..70c0a1c3506996113f34e5d7419882642921be83 100644
--- a/civicrm/templates/CRM/Batch/Form/Entry.tpl
+++ b/civicrm/templates/CRM/Batch/Form/Entry.tpl
@@ -20,7 +20,7 @@
   </div>
   {if $batchAmountMismatch}
     <div class="status message status-warning">
-      <i class="crm-i fa-exclamation-triangle"></i> {ts}Total for amounts entered below does not match the expected batch total.{/ts}
+      <i class="crm-i fa-exclamation-triangle" aria-hidden="true"></i> {ts}Total for amounts entered below does not match the expected batch total.{/ts}
     </div>
     <span class="crm-button crm-button_qf_Entry_upload_force-save">
       {$form._qf_Entry_upload_force.html}
@@ -55,10 +55,7 @@
       {foreach from=$fields item=field key=fieldName}
         <div class="crm-grid-cell">
           {if $field.name|substr:0:11 ne 'soft_credit' and $field.name ne 'trxn_id'}
-          <img src="{$config->resourceBase}i/copy.png"
-               alt="{ts 1=$field.title}Click to copy %1 from row one to all rows.{/ts}"
-               fname="{$field.name}" class="action-icon"
-               title="{ts}Click here to copy the value in row one to ALL rows.{/ts}"/>
+          {copyIcon name=$field.name title=$field.title}
           {/if}{$field.title}
         </div>
       {/foreach}
diff --git a/civicrm/templates/CRM/Campaign/Form/ResultOptions.tpl b/civicrm/templates/CRM/Campaign/Form/ResultOptions.tpl
index b94652962c1c672a100a9d6dda4ab7dc92993949..32b4ae8c5944ee884a8cd061b5371544b9508952 100644
--- a/civicrm/templates/CRM/Campaign/Form/ResultOptions.tpl
+++ b/civicrm/templates/CRM/Campaign/Form/ResultOptions.tpl
@@ -43,7 +43,7 @@
   <tr id="optionField_{$index}" class="form-item {cycle values="odd-row,even-row"}">
         <td>
         {if $index GT 1}
-            <a onclick="showHideRow({$index}); return false;" name="optionField_{$index}" href="#" class="form-link"><i class="crm-i fa-trash" title="{ts}hide field or section{/ts}"></i></a>
+            <a onclick="showHideRow({$index}); return false;" name="optionField_{$index}" href="#" class="form-link"><i class="crm-i fa-trash" title="{ts}hide field or section{/ts}" aria-hidden="true"></i></a>
         {/if}
         </td>
       <td>
@@ -60,7 +60,7 @@
     {/section}
     </table>
   <div id="optionFieldLink" class="add-remove-link">
-        <a onclick="showHideRow(); return false;" name="optionFieldLink" href="#" class="form-link"><i class="crm-i fa-plus-circle"></i> {ts}add another choice{/ts}</a>
+        <a onclick="showHideRow(); return false;" name="optionFieldLink" href="#" class="form-link"><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}add another choice{/ts}</a>
     </div>
   <span id="additionalOption" class="description">
     {ts}If you need additional options - you can add them after you Save your current entries.{/ts}
diff --git a/civicrm/templates/CRM/Campaign/Form/Search/Petition.tpl b/civicrm/templates/CRM/Campaign/Form/Search/Petition.tpl
index e14be7e538ff6a2caf5c3b88bb1bf44e0c2a3d92..35414668910eb632810b12329866e5d553edae60 100644
--- a/civicrm/templates/CRM/Campaign/Form/Search/Petition.tpl
+++ b/civicrm/templates/CRM/Campaign/Form/Search/Petition.tpl
@@ -198,6 +198,7 @@ function loadPetitionList( )
 
          //add id for yes/no column.
          CRM.$(nRow).children().eq(8).attr( 'id', rowId + '_status' );
+         CRM.$(nRow).children().eq(6).html(CRM.utils.formatIcon('fa-check', ts('Default'), nRow.cells[6].innerText));
 
          return nRow;
     },
diff --git a/civicrm/templates/CRM/Campaign/Form/Search/Survey.tpl b/civicrm/templates/CRM/Campaign/Form/Search/Survey.tpl
index 1177d3a5ceccec75402be6bc9da2fb466e4dba5f..09a53a4ecea0fb293d4327f6d1ba3b51a0dff730 100644
--- a/civicrm/templates/CRM/Campaign/Form/Search/Survey.tpl
+++ b/civicrm/templates/CRM/Campaign/Form/Search/Survey.tpl
@@ -210,6 +210,7 @@ function loadSurveyList( )
 
          //add id for yes/no column.
          CRM.$(nRow).children().eq(11).attr( 'id', rowId + '_status' );
+         CRM.$(nRow).children().eq(9).html(CRM.utils.formatIcon('fa-check', ts('Default'), nRow.cells[9].innerText));
 
          return nRow;
     },
diff --git a/civicrm/templates/CRM/Campaign/Form/Task/Interview.tpl b/civicrm/templates/CRM/Campaign/Form/Task/Interview.tpl
index 7da55ab1e4a337dc6f707cd45adbb015e616adbb..9c927d7b4cc295242060c07da56debdf731fc616 100644
--- a/civicrm/templates/CRM/Campaign/Form/Task/Interview.tpl
+++ b/civicrm/templates/CRM/Campaign/Form/Task/Interview.tpl
@@ -51,7 +51,7 @@
           <tr id="optionField_{$index}" class="form-item {cycle values="odd-row,even-row"}">
             <td>
               {if $index GT 1}
-                <a onclick="hideRow({$index}); return false;" name="orderBy_{$index}" href="#" class="form-link"><img src="{$config->resourceBase}i/TreeMinus.gif" class="action-icon" alt="{ts}hide field or section{/ts}"/></a>
+                <a onclick="hideRow({$index}); return false;" name="orderBy_{$index}" href="#" class="form-link">{icon icon="fa-trash"}{ts}hide field or section{/ts}{/icon}</a>
               {/if}
             </td>
             <td> {$form.order_bys.$index.column.html}</td>
@@ -65,7 +65,7 @@
         {/section}
       </table>
       <div id="optionFieldLink" class="add-remove-link">
-        <a onclick="showHideRow(); return false;" name="optionFieldLink" href="#" class="form-link"><img src="{$config->resourceBase}i/TreePlus.gif" class="action-icon" alt="{ts}show field or section{/ts}"/>{ts}another column{/ts}</a>
+        <a onclick="showHideRow(); return false;" name="optionFieldLink" href="#" class="form-link"><i class="crm-i fa-plus action-icon" aria-hidden="true"></i> {ts}another column{/ts}</a>
       </div>
 
       <script type="text/javascript">
@@ -116,12 +116,12 @@
             {if $field.skipDisplay}
               {continue}
             {/if}
-            <th><img  src="{$config->resourceBase}i/copy.png" alt="{ts 1=$field.title}Click to copy %1 from row one to all rows.{/ts}" fname="{$field.name}" class="action-icon" title="{ts}Click here to copy the value in row one to ALL rows.{/ts}" />{$field.title}</th>
+            <th>{copyIcon name=$field.name title=$field.title}{$field.title}</th>
           {/foreach}
         {/if}
 
-        <th><img  src="{$config->resourceBase}i/copy.png" alt="{ts 1=note}Click to copy %1 from row one to all rows.{/ts}" fname="note" class="action-icon" title="{ts}Click here to copy the value in row one to ALL rows.{/ts}" />{ts}Note{/ts}</th>
-        <th><img  src="{$config->resourceBase}i/copy.png" alt="{ts 1=result}Click to copy %1 from row one to all rows.{/ts}" fname="result" class="action-icon" title="{ts}Click here to copy the value in row one to ALL rows.{/ts}" />{ts}Result{/ts}</th>
+        <th>{capture assign="tsNote"}{ts}Note{/ts}{/capture}{copyIcon name=note title=$tsNote}{$tsNote}</th>
+        <th>{capture assign="tsResult"}{ts}Result{/ts}{/capture}{copyIcon name=result title=$tsResult}{$tsResult}</th>
         <th><a id="interview_voter_button" class='button' style="float:left;" href="#" title={ts}Vote{/ts} onclick="registerInterviewforall( ); return false;">{ts}Record Responses for All{/ts}</a></th>
       </tr>
       </thead>
@@ -311,7 +311,7 @@ var surveyActivityIds = {/literal}{$surveyActivityIds}{literal};
         if (interview.errors[error]) errorList =  errorList + '<li>' + interview.errors[error] + '</li>';
       }
       if ( errorList ) {
-        var allErrors = '<i class="crm-i fa-exclamation-triangle crm-i-red"></i> {/literal}{ts}Please correct the following errors in the survey fields below:{/ts}{literal}<ul>' + errorList + '</ul>';
+        var allErrors = '<i class="crm-i fa-exclamation-triangle crm-i-red" aria-hidden="true"></i> {/literal}{ts}Please correct the following errors in the survey fields below:{/ts}{literal}<ul>' + errorList + '</ul>';
         CRM.$('#responseErrors').show( ).html(allErrors);
       }
     }
diff --git a/civicrm/templates/CRM/Campaign/Page/Petition.tpl b/civicrm/templates/CRM/Campaign/Page/Petition.tpl
index 1dfd26a368a86cdd210e2315bbe71f8912ac20b4..595c8582e74ca29c6a3c38d6b8b62c2b0b5fb006 100644
--- a/civicrm/templates/CRM/Campaign/Page/Petition.tpl
+++ b/civicrm/templates/CRM/Campaign/Page/Petition.tpl
@@ -12,7 +12,7 @@
 {if $surveys}
   <div class="action-link">
     <a href="{$addSurveyUrl}" class="button">
-      <span><i class="crm-i fa-plus-circle"></i> {ts}Add Survey{/ts}</span>
+      <span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Add Survey{/ts}</span>
     </a>
   </div>
  {include file="CRM/common/enableDisableApi.tpl"}
@@ -40,7 +40,7 @@
           <td>{$survey.release_frequency}</td>
           <td>{$survey.max_number_of_contacts}</td>
           <td>{$survey.default_number_of_contacts}</td>
-          <td>{if $survey.is_default}<img src="{$config->resourceBase}i/check.gif" alt="{ts}Default{/ts}" /> {/if}</td>
+          <td>{icon condition=$survey.is_default}{ts}Default{/ts}{/icon}</td>
           <td id="row_{$survey.id}_status">{if $survey.is_active}{ts}Yes{/ts}{else}{ts}No{/ts}{/if}</td>
      <td class="crm-report-optionList-action">{$survey.action}</td>
         </tr>
@@ -55,6 +55,6 @@
 {/if}
 <div class="action-link">
   <a href="{$addSurveyUrl}" class="button">
-    <span><i class="crm-i fa-plus-circle"></i> {ts}Add Survey{/ts}</span>
+    <span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Add Survey{/ts}</span>
   </a>
 </div>
diff --git a/civicrm/templates/CRM/Case/Form/Activity.tpl b/civicrm/templates/CRM/Case/Form/Activity.tpl
index 285153e6db4ac48e119f46792941f36709f81c64..7df7770d7044f43a612f603354859c093bf79b00 100644
--- a/civicrm/templates/CRM/Case/Form/Activity.tpl
+++ b/civicrm/templates/CRM/Case/Form/Activity.tpl
@@ -18,7 +18,7 @@
 
   {if $action eq 8 or $action eq 32768 }
   <div class="messages status no-popup">
-    <i class="crm-i fa-info-circle"></i> &nbsp;
+    <i class="crm-i fa-info-circle" aria-hidden="true"></i> &nbsp;
     {if $action eq 8}
       {* activityTypeName means label here not name, but it's ok because label is desired here (dev/core#1116-ok-label) *}
       {ts 1=$activityTypeName}Click Delete to move this &quot;%1&quot; activity to the Trash.{/ts}
@@ -75,7 +75,7 @@
 
                     {if $action eq 1 or $action eq 2}
                       <br />
-                      <a href="#" class="crm-with-contact">&raquo; {ts}With other contact(s){/ts}</a>
+                      <a href="#" class="crm-with-contact"><i class="crm-i fa-user-plus" aria-hidden="true"></i> {ts}With other contact(s){/ts}</a>
                     {/if}
                   </td>
                 </tr>
@@ -87,7 +87,7 @@
                       {$form.target_contact_id.html}
                       <br/>
                       <a href="#" class="crm-with-contact">
-                        &raquo; {if not $multiClient}{ts}With client{/ts}{else}{ts}With client(s){/ts}{/if}
+                        <i class="crm-i fa-user" aria-hidden="true"></i> {if not $multiClient}{ts}With client{/ts}{else}{ts}With client(s){/ts}{/if}
                       </a>
                     </td>
                   </tr>
@@ -110,7 +110,7 @@
                   <td>{$form.assignee_contact_id.html}
                     {if $activityAssigneeNotification}
                       <br />
-                      <span id="notify_assignee_msg" class="description"><i class="crm-i fa-paper-plane"></i> {ts}A copy of this activity will be emailed to each Assignee.{/ts}</span>
+                      <span id="notify_assignee_msg" class="description"><i class="crm-i fa-paper-plane" aria-hidden="true"></i> {ts}A copy of this activity will be emailed to each Assignee.{/ts}</span>
                     {/if}
                   </td>
                 </tr>
diff --git a/civicrm/templates/CRM/Case/Form/ActivityTab.tpl b/civicrm/templates/CRM/Case/Form/ActivityTab.tpl
index b489a8037aad828c64601213d3022547b08a801d..32ee2e940932e4c554a6e6d9fa3ae07494c8849e 100644
--- a/civicrm/templates/CRM/Case/Form/ActivityTab.tpl
+++ b/civicrm/templates/CRM/Case/Form/ActivityTab.tpl
@@ -15,46 +15,44 @@
       {ts}Activities{/ts}
     </div>
 
-    <div id="activities" class="crm-accordion-body">
-    <div class="crm-accordion-wrapper crm-accordion-inner crm-search_filters-accordion collapsed">
-      <div class="crm-accordion-header">
+    <div id="activities" class="crm-accordion-body padded">
+    <div class="crm-collapsible crm-search_filters-accordion collapsed">
+      <div class="collapsible-title">
         {ts}Search Filters{/ts}
-      </div><!-- /.crm-accordion-header -->
-      <div class="crm-accordion-body">
-        <table class="no-border form-layout-compressed" id="searchOptions">
-          <tr>
-            <td class="crm-case-caseview-form-block-repoter_id"colspan="2"><label for="reporter_id">{ts}Reporter/Role{/ts}</label><br />
-              {$form.reporter_id.html|crmAddClass:twenty}
-            </td>
-            <td class="crm-case-caseview-form-block-status_id"><label for="status_id">{$form.status_id.label}</label><br />
-              {$form.status_id.html}
-            </td>
-          </tr>
-          <tr>
-            <td class="crm-case-caseview-form-block-activity_date_low">
-              {assign var=activitylow  value=activity_date_low_$caseID}
-              {$form.$activitylow.label}<br />
-              {$form.$activitylow.html}
-            </td>
-            <td class="crm-case-caseview-form-block-activity_date_high">
-              {assign var=activityhigh  value=activity_date_high_$caseID}
-              {$form.$activityhigh.label}<br />
-              {$form.$activityhigh.html}
-            </td>
-            <td class="crm-case-caseview-form-block-activity_type_filter_id">
-              {$form.activity_type_filter_id.label}<br />
-              {$form.activity_type_filter_id.html}
+      </div>
+      <table class="no-border form-layout-compressed" id="searchOptions">
+        <tr>
+          <td class="crm-case-caseview-form-block-repoter_id"colspan="2"><label for="reporter_id">{ts}Reporter/Role{/ts}</label><br />
+            {$form.reporter_id.html|crmAddClass:twenty}
+          </td>
+          <td class="crm-case-caseview-form-block-status_id"><label for="status_id">{$form.status_id.label}</label><br />
+            {$form.status_id.html}
+          </td>
+        </tr>
+        <tr>
+          <td class="crm-case-caseview-form-block-activity_date_low">
+            {assign var=activitylow  value=activity_date_low_$caseID}
+            {$form.$activitylow.label}<br />
+            {$form.$activitylow.html}
+          </td>
+          <td class="crm-case-caseview-form-block-activity_date_high">
+            {assign var=activityhigh  value=activity_date_high_$caseID}
+            {$form.$activityhigh.label}<br />
+            {$form.$activityhigh.html}
+          </td>
+          <td class="crm-case-caseview-form-block-activity_type_filter_id">
+            {$form.activity_type_filter_id.label}<br />
+            {$form.activity_type_filter_id.html}
+          </td>
+        </tr>
+        {if $form.activity_deleted}
+          <tr class="crm-case-caseview-form-block-activity_deleted">
+            <td>
+              {$form.activity_deleted.html}{$form.activity_deleted.label}
             </td>
           </tr>
-          {if $form.activity_deleted}
-            <tr class="crm-case-caseview-form-block-activity_deleted">
-              <td>
-                {$form.activity_deleted.html}{$form.activity_deleted.label}
-              </td>
-            </tr>
-          {/if}
-        </table>
-      </div><!-- /.crm-accordion-body -->
+        {/if}
+      </table>
     </div><!-- /.crm-accordion-wrapper -->
 {/if}
 
diff --git a/civicrm/templates/CRM/Case/Form/ActivityToCase.tpl b/civicrm/templates/CRM/Case/Form/ActivityToCase.tpl
index 0bc1ad634b2079ef8b4a20aae2639796e27051fa..8d9b69525263d0d31157b7c6e3de8ba78470e1de 100644
--- a/civicrm/templates/CRM/Case/Form/ActivityToCase.tpl
+++ b/civicrm/templates/CRM/Case/Form/ActivityToCase.tpl
@@ -73,7 +73,7 @@
             var caseUrl = destUrl + selectedCaseId + '&cid=' + contactId + context;
 
             var statusMsg = {/literal}'{ts escape='js' 1='%1'}Activity has been filed to %1 case.{/ts}'{literal};
-            CRM.alert(ts(statusMsg, {1: '<a href="' + caseUrl + '">' + caseTitle + '</a>'}), '{/literal}{ts escape="js"}Saved{/ts}{literal}', 'success');
+            CRM.alert(ts(statusMsg, {1: '<a href="' + caseUrl + '">' + CRM._.escape(caseTitle) + '</a>'}), '{/literal}{ts escape="js"}Saved{/ts}{literal}', 'success');
             CRM.refreshParent(a);
           }
         }
diff --git a/civicrm/templates/CRM/Case/Form/ActivityView.tpl b/civicrm/templates/CRM/Case/Form/ActivityView.tpl
index 5a5e1455495139570a6103635dda689891e9a190..9bc3c3d781e4114c53b00742714c96d97bada383 100644
--- a/civicrm/templates/CRM/Case/Form/ActivityView.tpl
+++ b/civicrm/templates/CRM/Case/Form/ActivityView.tpl
@@ -36,9 +36,9 @@
             {if $smarty.foreach.report.first AND ( $activityID OR $parentID OR $latestRevisionID )} {* Add a cell to first row with links to prior revision listing and Prompted by (parent) as appropriate *}
               <td>{$row.value}</td>
               <td style="padding-right: 50px; text-align: right; font-size: .9em;">
-                {if $activityID}<a class="open-inline-noreturn" href="{crmURL p='civicrm/case/activity/view' h=0 q="cid=$contactID&aid=$activityID&revs=1"}">&raquo; {ts}List all revisions{/ts}</a>{if !$latestRevisionID}<br />{ts}(this is the current revision){/ts}{/if}<br />{/if}
-                {if $latestRevisionID}<a class="open-inline-noreturn" href="{crmURL p='civicrm/case/activity/view' h=0 q="cid=$contactID&aid=$latestRevisionID"}">&raquo; {ts}View current revision{/ts}</a><br /><span style="color: red;">{ts}(this is not the current revision){/ts}</span><br />{/if}
-                {if $parentID}<a class="open-inline-noreturn" href="{crmURL p='civicrm/case/activity/view' h=0 q="cid=$contactID&aid=$parentID"}">&raquo; {ts}Prompted by{/ts}</a>{/if}
+                {if $activityID}<a class="open-inline-noreturn" href="{crmURL p='civicrm/case/activity/view' h=0 q="cid=$contactID&aid=$activityID&revs=1"}"><i class="crm-i fa-history" aria-hidden="true"></i> {ts}List all revisions{/ts}</a>{if !$latestRevisionID}<br />{ts}(this is the current revision){/ts}{/if}<br />{/if}
+                {if $latestRevisionID}<a class="open-inline-noreturn" href="{crmURL p='civicrm/case/activity/view' h=0 q="cid=$contactID&aid=$latestRevisionID"}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}View current revision{/ts}</a><br /><span style="color: red;">{ts}(this is not the current revision){/ts}</span><br />{/if}
+                {if $parentID}<a class="open-inline-noreturn" href="{crmURL p='civicrm/case/activity/view' h=0 q="cid=$contactID&aid=$parentID"}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Prompted by{/ts}</a>{/if}
               </td>
             {else}
               <td colspan="2">{if $row.label eq 'Details'}{$row.value|crmStripAlternatives|nl2br|purify}{elseif $row.type eq 'Date'}{$row.value|crmDate}{else}{$row.value}{/if}</td>
diff --git a/civicrm/templates/CRM/Case/Form/CaseView.tpl b/civicrm/templates/CRM/Case/Form/CaseView.tpl
index 524da11842e9f0afe1bbb78d3caeb499cf3b8d65..86e23f57af2caeba63596c64373341d1854304e8 100644
--- a/civicrm/templates/CRM/Case/Form/CaseView.tpl
+++ b/civicrm/templates/CRM/Case/Form/CaseView.tpl
@@ -27,7 +27,7 @@
             <a href="{crmURL p='civicrm/contact/view' q="action=view&reset=1&cid=`$client.contact_id`"}" title="{ts}View contact record{/ts}">{$client.display_name}</a>{if not $smarty.foreach.clients.last}, &nbsp; {/if}
           {/foreach}
           <a href="#addClientDialog" class="crm-hover-button case-miniform" title="{ts}Add Client{/ts}" data-key="{crmKey name='civicrm/case/ajax/addclient'}">
-            <i class="crm-i fa-user-plus"></i>
+            <i class="crm-i fa-user-plus" aria-hidden="true"></i>
           </a>
           <div id="addClientDialog" class="hiddenElement">
             <input name="add_client_id" placeholder="{ts}- select contact -{/ts}" class="huge" data-api-params='{ldelim}"params": {ldelim}"contact_type": "{$contactType}"{rdelim}{rdelim}' />
@@ -45,7 +45,7 @@
             {foreach from=$caseRoles.client item=client}
               <tr class="crm-case-caseview-display_name">
                 <td class="label-left bold" style="padding: 0px; border: none;">
-                  <a href="{crmURL p='civicrm/contact/view' q="action=view&reset=1&cid=`$client.contact_id`"}" title="{ts}View contact record{/ts}">{$client.display_name}</a>{if $client.email}{crmAPI var='email_type_id' entity='OptionValue' action='getsingle' return="value" name="Email" option_group_id="activity_type"}<span class="crm-case-caseview-email"><a class="crm-hover-button crm-popup" href="{crmURL p='civicrm/activity/email/add' q="reset=1&action=add&atype=`$email_type_id.value`&cid=`$client.contact_id`&caseid=`$caseId`"}" title="{ts 1=$client.email|escape}Email: %1{/ts}"><i class="crm-i fa-envelope"></i></a></span>{/if}
+                  <a href="{crmURL p='civicrm/contact/view' q="action=view&reset=1&cid=`$client.contact_id`"}" title="{ts}View contact record{/ts}">{$client.display_name}</a>{if $client.email}{crmAPI var='email_type_id' entity='OptionValue' action='getsingle' return="value" name="Email" option_group_id="activity_type"}<span class="crm-case-caseview-email"><a class="crm-hover-button crm-popup" href="{crmURL p='civicrm/activity/email/add' q="reset=1&action=add&atype=`$email_type_id.value`&cid=`$client.contact_id`&caseid=`$caseId`"}" title="{ts 1=$client.email|escape}Email: %1{/ts}"><i class="crm-i fa-envelope" aria-hidden="true"></i></a></span>{/if}
                 </td>
               </tr>
               {if $client.phone}
@@ -69,13 +69,13 @@
         <span class="crm-case-summary-label">{ts}Subject{/ts}:</span>&nbsp;<span class="crm-editable" data-field="subject">{$caseDetails.case_subject}</span>
       </td>
       <td class="crm-case-caseview-case_type label">
-        <span class="crm-case-summary-label">{ts}Type{/ts}:</span>&nbsp;{$caseDetails.case_type}&nbsp;<a class="crm-hover-button crm-popup"  href="{crmURL p='civicrm/case/activity' q="action=add&reset=1&cid=`$contactId`&caseid=`$caseId`&selectedChild=activity&atype=`$changeCaseTypeId`"}" title="{ts}Change case type (creates activity record){/ts}"><i class="crm-i fa-pencil"></i></a>
+        <span class="crm-case-summary-label">{ts}Type{/ts}:</span>&nbsp;{$caseDetails.case_type}&nbsp;<a class="crm-hover-button crm-popup"  href="{crmURL p='civicrm/case/activity' q="action=add&reset=1&cid=`$contactId`&caseid=`$caseId`&selectedChild=activity&atype=`$changeCaseTypeId`"}" title="{ts}Change case type (creates activity record){/ts}"><i class="crm-i fa-pencil" aria-hidden="true"></i></a>
       </td>
       <td class="crm-case-caseview-case_status label">
-        <span class="crm-case-summary-label">{ts}Status{/ts}:</span>&nbsp;{$caseDetails.case_status}&nbsp;<a class="crm-hover-button crm-popup"  href="{crmURL p='civicrm/case/activity' q="action=add&reset=1&cid=`$contactId`&caseid=`$caseId`&selectedChild=activity&atype=`$changeCaseStatusId`"}" title="{ts}Change case status (creates activity record){/ts}"><i class="crm-i fa-pencil"></i></a>
+        <span class="crm-case-summary-label">{ts}Status{/ts}:</span>&nbsp;{$caseDetails.case_status}&nbsp;<a class="crm-hover-button crm-popup"  href="{crmURL p='civicrm/case/activity' q="action=add&reset=1&cid=`$contactId`&caseid=`$caseId`&selectedChild=activity&atype=`$changeCaseStatusId`"}" title="{ts}Change case status (creates activity record){/ts}"><i class="crm-i fa-pencil" aria-hidden="true"></i></a>
       </td>
       <td class="crm-case-caseview-case_start_date label">
-        <span class="crm-case-summary-label">{ts}Open Date{/ts}:</span>&nbsp;{$caseDetails.case_start_date|crmDate}&nbsp;<a class="crm-hover-button crm-popup"  href="{crmURL p='civicrm/case/activity' q="action=add&reset=1&cid=`$contactId`&caseid=`$caseId`&selectedChild=activity&atype=`$changeCaseStartDateId`"}" title="{ts}Change case start date (creates activity record){/ts}"><i class="crm-i fa-pencil"></i></a>
+        <span class="crm-case-summary-label">{ts}Open Date{/ts}:</span>&nbsp;{$caseDetails.case_start_date|crmDate}&nbsp;<a class="crm-hover-button crm-popup"  href="{crmURL p='civicrm/case/activity' q="action=add&reset=1&cid=`$contactId`&caseid=`$caseId`&selectedChild=activity&atype=`$changeCaseStartDateId`"}" title="{ts}Change case start date (creates activity record){/ts}"><i class="crm-i fa-pencil" aria-hidden="true"></i></a>
       </td>
       <td class="crm-case-caseview-{$caseID} label">
         <span class="crm-case-summary-label">{ts}ID{/ts}:</span>&nbsp;{$caseID}
@@ -103,15 +103,15 @@
     <div>
       <p>
         {if $hasAccessToAllCases}
-          <a class="crm-hover-button action-item no-popup" href="{crmURL p='civicrm/case/report/print' q="all=1&redact=0&cid=$contactID&caseID=$caseId&asn="}"><i class="crm-i fa-print"></i> {ts}Print Report{/ts}</a>
+          <a class="crm-hover-button action-item no-popup" href="{crmURL p='civicrm/case/report/print' q="all=1&redact=0&cid=$contactID&caseID=$caseId&asn="}"><i class="crm-i fa-print" aria-hidden="true"></i> {ts}Print Report{/ts}</a>
         {/if}
 
         {if !empty($exportDoc)}
-          <a class="crm-hover-button action-item" href="{$exportDoc}"><i class="crm-i fa-file-pdf-o"></i> {ts}Export Document{/ts}</a>
+          <a class="crm-hover-button action-item" href="{$exportDoc}"><i class="crm-i fa-file-pdf-o" aria-hidden="true"></i> {ts}Export Document{/ts}</a>
         {/if}
 
         {if $mergeCases}
-          <a href="#mergeCasesDialog" class="action-item no-popup crm-hover-button case-miniform"><i class="crm-i fa-compress"></i> {ts}Merge Case{/ts}</a>
+          <a href="#mergeCasesDialog" class="action-item no-popup crm-hover-button case-miniform"><i class="crm-i fa-compress" aria-hidden="true"></i> {ts}Merge Case{/ts}</a>
           {$form._qf_CaseView_next_merge_case.html}
           <span id="mergeCasesDialog" class="hiddenElement">
             {$form.merge_case_id.html}
@@ -119,7 +119,7 @@
         {/if}
 
         {if call_user_func(array('CRM_Core_Permission','giveMeAllACLs'))}
-          <a class="action-item crm-hover-button medium-popup" href="{crmURL p='civicrm/contact/view/case/editClient' h=1 q="reset=1&action=update&id=$caseID&cid=$contactID"}"><i class="crm-i fa-user"></i> {ts}Assign to Another Client{/ts}</a>
+          <a class="action-item crm-hover-button medium-popup" href="{crmURL p='civicrm/contact/view/case/editClient' h=1 q="reset=1&action=update&id=$caseID&cid=$contactID"}"><i class="crm-i fa-user" aria-hidden="true"></i> {ts}Assign to Another Client{/ts}</a>
         {/if}
       </p>
     </div>
@@ -136,7 +136,7 @@
 
       {if $hasAccessToAllCases}
         <div class="crm-submit-buttons">
-          <a class="button case-miniform" href="#addCaseRoleDialog" data-key="{crmKey name='civicrm/ajax/relation'}" rel="#caseRoles-selector-{$caseID}"><i class="crm-i fa-plus-circle"></i> {ts}Add new role{/ts}</a>
+          <a class="button case-miniform" href="#addCaseRoleDialog" data-key="{crmKey name='civicrm/ajax/relation'}" rel="#caseRoles-selector-{$caseID}"><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Add new role{/ts}</a>
         </div>
         <div id="addCaseRoleDialog" class="hiddenElement">
           <div>{$form.role_type.label}</div>
@@ -219,7 +219,7 @@
   {if !empty($globalGroupInfo.id)}
     <div class="crm-submit-buttons">
       <a class="button case-miniform" href="#addMembersToGroupDialog" rel="#globalRelationships-selector-{$caseId}" data-group_id="{$globalGroupInfo.id}">
-        <i class="crm-i fa-plus-circle"></i> {ts 1=$globalGroupInfo.title}Add members to %1{/ts}
+        <i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts 1=$globalGroupInfo.title}Add members to %1{/ts}
       </a>
     </div>
     <div id="addMembersToGroupDialog" class="hiddenElement">
diff --git a/civicrm/templates/CRM/Case/Form/Selector.tpl b/civicrm/templates/CRM/Case/Form/Selector.tpl
index bba4976b9f2af6a7ebe69d1ff634826e0a7b7f9a..e84e64afbbfc1cc8b844cc0a0844f08a5ed62d5f 100644
--- a/civicrm/templates/CRM/Case/Form/Selector.tpl
+++ b/civicrm/templates/CRM/Case/Form/Selector.tpl
@@ -61,7 +61,7 @@
     {* Dashboard only lists 10 most recent cases. *}
     {if $context EQ 'dashboard' and $limit and $pager->_totalItems GT $limit }
       <tr class="even-row">
-        <td colspan="10"><a href="{crmURL p='civicrm/case/search' q='reset=1'}">&raquo; {ts}Find more cases{/ts}... </a></td>
+        <td colspan="10"><a href="{crmURL p='civicrm/case/search' q='reset=1'}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Find more cases{/ts}... </a></td>
       </tr>
     {/if}
 
diff --git a/civicrm/templates/CRM/Case/Page/DashBoard.tpl b/civicrm/templates/CRM/Case/Page/DashBoard.tpl
index 9d83c4c0f1bfce187a46f6b9f1da51dc4d041e08..f8bcfce4db509f302773f5a2219779ec1f3341d3 100644
--- a/civicrm/templates/CRM/Case/Page/DashBoard.tpl
+++ b/civicrm/templates/CRM/Case/Page/DashBoard.tpl
@@ -18,9 +18,9 @@
 
     <div class="crm-submit-buttons crm-case-dashboard-buttons">
       {if $newClient and $allowToAddNewCase}
-        <a href="{$newCaseURL}" class="button"><span><i class="crm-i fa-plus-circle"></i> {ts}Add Case{/ts}</span></a>
+        <a href="{$newCaseURL}" class="button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Add Case{/ts}</span></a>
       {/if}
-      <a class="button no-popup" name="find_my_cases" href="{crmURL p="civicrm/case/search" q="reset=1&case_owner=2&force=1"}"><span><i class="crm-i fa-search"></i> {ts}Find My Cases{/ts}</span></a>
+      <a class="button no-popup" name="find_my_cases" href="{crmURL p="civicrm/case/search" q="reset=1&case_owner=2&force=1"}"><span><i class="crm-i fa-search" aria-hidden="true"></i> {ts}Find My Cases{/ts}</span></a>
 
       <div class="crm-case-dashboard-switch-view-buttons">
         {if $myCases}
diff --git a/civicrm/templates/CRM/Case/Page/DashboardSelector.tpl b/civicrm/templates/CRM/Case/Page/DashboardSelector.tpl
index c9d46d9b467b276cdf356cb067d3e71500cb9b15..30392c8d53c46ecf68f002d655a3ba9f18d33cdb 100644
--- a/civicrm/templates/CRM/Case/Page/DashboardSelector.tpl
+++ b/civicrm/templates/CRM/Case/Page/DashboardSelector.tpl
@@ -7,7 +7,6 @@
  | and copyright information, see https://civicrm.org/licensing       |
  +--------------------------------------------------------------------+
 *}
-{capture assign=expandIconURL}<img src="{$config->resourceBase}i/TreePlus.gif" alt="{ts}open section{/ts}"/>{/capture}
 {strip}
 <table class="case-selector-{$list} crm-ajax-table" data-page-length='10'>
 <thead>
diff --git a/civicrm/templates/CRM/Case/Page/Tab.tpl b/civicrm/templates/CRM/Case/Page/Tab.tpl
index 971157d0f3bc8e7dda78a43e9552d853b98bbfa7..047fd3dc379b92a4b5e4a276e6d63526f5c8ea56 100644
--- a/civicrm/templates/CRM/Case/Page/Tab.tpl
+++ b/civicrm/templates/CRM/Case/Page/Tab.tpl
@@ -44,7 +44,7 @@
           call_user_func(array('CRM_Core_Permission','check'), 'add cases') ) AND
         $allowToAddNewCase}
         <div class="action-link">
-        <a accesskey="N" href="{$newCaseURL}" class="button no-popup"><span><i class="crm-i fa-plus-circle"></i> {ts}Add Case{/ts}</span></a>
+        <a accesskey="N" href="{$newCaseURL}" class="button no-popup"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Add Case{/ts}</span></a>
         </div>
     {/if}
 
diff --git a/civicrm/templates/CRM/Contact/Form/Contact.tpl b/civicrm/templates/CRM/Contact/Form/Contact.tpl
index f69d5afb2b87c6ef55cb8223db82e743c8023011..2acbff7dccbd4e024b3765ba06675a93dff54081 100644
--- a/civicrm/templates/CRM/Contact/Form/Contact.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Contact.tpl
@@ -16,7 +16,7 @@
   {/if}
   <div class="crm-form-block crm-search-form-block">
     {if call_user_func(array('CRM_Core_Permission','check'), 'administer CiviCRM') }
-      <a href='{crmURL p="civicrm/admin/setting/preferences/display" q="reset=1"}' title="{ts}Click here to configure the panes.{/ts}"><i class="crm-i fa-wrench"></i></a>
+      <a href='{crmURL p="civicrm/admin/setting/preferences/display" q="reset=1"}' title="{ts}Click here to configure the panes.{/ts}"><i class="crm-i fa-wrench" aria-hidden="true"></i></a>
     {/if}
     <span style="float:right;"><a href="#expand" id="expand">{ts}Expand all tabs{/ts}</a></span>
     <div class="crm-submit-buttons">
diff --git a/civicrm/templates/CRM/Contact/Form/Domain.tpl b/civicrm/templates/CRM/Contact/Form/Domain.tpl
index c5543452ca0a6ed73cf582b6c0e0a821c4514c64..258107b9b8bbd078e91bc486e655d002c972fc0c 100644
--- a/civicrm/templates/CRM/Contact/Form/Domain.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Domain.tpl
@@ -46,7 +46,7 @@
 
     {if ($action eq 4)}
     <div class="action-link">
-    <a href="{crmURL q="action=update&reset=1"}" id="editDomainInfo">&raquo; {ts}Edit Domain Information{/ts}</a>
+    <a href="{crmURL q="action=update&reset=1"}" id="editDomainInfo"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Edit Domain Information{/ts}</a>
     </div>
     {/if}
 {if !($action eq 4)}
diff --git a/civicrm/templates/CRM/Contact/Form/Edit/Address.tpl b/civicrm/templates/CRM/Contact/Form/Edit/Address.tpl
index 05e1c382227e9677b0b1b1a184c2477b79f48f84..0181f55f956f7b38638a57a67e82555dcb03cf07 100644
--- a/civicrm/templates/CRM/Contact/Form/Edit/Address.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Edit/Address.tpl
@@ -65,7 +65,7 @@
 
   {if $className eq 'CRM_Contact_Form_Contact'}
       <div id="addMoreAddress{$blockId}" class="crm-add-address-wrapper">
-          <a href="#" class="button" onclick="buildAdditionalBlocks( 'Address', '{$className}' );return false;"><span><i class="crm-i fa-plus-circle"></i> {ts}Another Address{/ts}</span></a>
+          <a href="#" class="button" onclick="buildAdditionalBlocks( 'Address', '{$className}' );return false;"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Another Address{/ts}</span></a>
       </div>
   {/if}
 
diff --git a/civicrm/templates/CRM/Contact/Form/Merge.tpl b/civicrm/templates/CRM/Contact/Form/Merge.tpl
index 9b56d73ec70980458d68a183e8c04884ea97809b..6c2902a06bb1eee8f53c4d7b7b73229caa9b6d9c 100644
--- a/civicrm/templates/CRM/Contact/Form/Merge.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Merge.tpl
@@ -31,24 +31,24 @@
   </div>
 
   <div class="action-link">
-    {if $prev}<a href="{$prev}" class="crm-hover-button action-item"><i class="crm-i fa-chevron-left"></i> {ts}Previous{/ts}</a>{/if}
-    {if $next}<a href="{$next}" class="crm-hover-button action-item">{ts}Next{/ts} <i class="crm-i fa-chevron-right"></i></a>{/if}
+    {if $prev}<a href="{$prev}" class="crm-hover-button action-item"><i class="crm-i fa-chevron-left" aria-hidden="true"></i> {ts}Previous{/ts}</a>{/if}
+    {if $next}<a href="{$next}" class="crm-hover-button action-item">{ts}Next{/ts} <i class="crm-i fa-chevron-right" aria-hidden="true"></i></a>{/if}
     <a href="{$flip}" class="action-item crm-hover-button">
-      <i class="crm-i fa-random"></i>
+      <i class="crm-i fa-random" aria-hidden="true"></i>
       {ts}Flip between original and duplicate contacts.{/ts}
     </a>
   </div>
 
   <div class="action-link">
     <a href="#" class="action-item crm-hover-button crm-notDuplicate" title={ts}Mark this pair as not a duplicate.{/ts} onClick="processDupes( {$main_cid|escape}, {$other_cid|escape}, 'dupe-nondupe', 'merge-contact', '{$browseUrl}' );return false;">
-      <i class="crm-i fa-times-circle"></i>
+      <i class="crm-i fa-times-circle" aria-hidden="true"></i>
       {ts}Mark this pair as not a duplicate.{/ts}
     </a>
   </div>
 
   <div class="action-link">
     <a href="javascript:void(0);" class="action-item crm-hover-button toggle_equal_rows">
-      <i class="crm-i fa-eye-slash"></i>
+      <i class="crm-i fa-eye-slash" aria-hidden="true"></i>
       {ts}Show/hide rows with the same data on each contact record.{/ts}
     </a>
   </div>
diff --git a/civicrm/templates/CRM/Contact/Form/Search/AdvancedCriteria.tpl b/civicrm/templates/CRM/Contact/Form/Search/AdvancedCriteria.tpl
index 99f5d2e0ed613b4cf43493b3fb57a733cc4364fd..8c7807c4b0ddbebe24743e12673c9481f6471a57 100644
--- a/civicrm/templates/CRM/Contact/Form/Search/AdvancedCriteria.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Search/AdvancedCriteria.tpl
@@ -67,7 +67,7 @@ CRM.$(function($) {
     var body = $('.crm-accordion-body.' + id);
     if (header.length > 0 && body.length > 0 && !body.html()) {
       body.html('<div class="crm-loading-element"><span class="loading-text">{/literal}{ts escape='js'}Loading{/ts}{literal}...</span></div>');
-      header.append('{/literal}<a href="#" class="crm-close-accordion crm-hover-button css_right" title="{ts escape='js'}Remove from search criteria{/ts}"><i class="crm-i fa-times"></i></a>{literal}');
+      header.append('{/literal}<a href="#" class="crm-close-accordion crm-hover-button css_right" title="{ts escape='js'}Remove from search criteria{/ts}"><i class="crm-i fa-times" aria-hidden="true"></i></a>{literal}');
       header.addClass('active');
       CRM.loadPage(url, {target: body, block: false});
     }
@@ -126,7 +126,7 @@ CRM.$(function($) {
         {include file="CRM/common/formButtons.tpl" location="bottom"}
         <div class="crm-submit-buttons reset-advanced-search">
           <a href="{crmURL p='civicrm/contact/search/advanced' q='reset=1'}" id="resetAdvancedSearch" class="crm-hover-button" title="{ts}Clear all search criteria{/ts}">
-            <i class="crm-i fa-undo"></i>
+            <i class="crm-i fa-undo" aria-hidden="true"></i>
             &nbsp;{ts}Reset Form{/ts}
           </a>
         </div>
diff --git a/civicrm/templates/CRM/Contact/Form/Search/Criteria/Fields/sort_name.tpl b/civicrm/templates/CRM/Contact/Form/Search/Criteria/Fields/sort_name.tpl
new file mode 100644
index 0000000000000000000000000000000000000000..cdda1797a99fddfc4383cc558889ec8c6d2d7b5b
--- /dev/null
+++ b/civicrm/templates/CRM/Contact/Form/Search/Criteria/Fields/sort_name.tpl
@@ -0,0 +1,39 @@
+<div id="sortnameselect">
+  <label>{ts}Complete OR Partial Name{/ts} <span class="description">(<a href="#" id='searchbyindivflds'>{ts}search by individual name fields{/ts}</a>)</span></label><br />
+  {$form.sort_name.html}
+</div>
+<div id="indivfldselect">
+  <label>{ts}First/Last Name{/ts}<span class="description"> (<a href="#" id='searchbysortname'>{ts}search by complete or partial name{/ts}</a>)</span></label><br />
+  {$form.first_name.html} {$form.last_name.html}
+</div>
+
+{literal}
+  <script type="text/javascript">
+    CRM.$(function($) {
+      function showIndivFldsSearch() {
+        $('#sortnameselect').hide();
+        $('#indivfldselect').show();
+        $('#sort_name').val('');
+        $('#first_name').removeClass('big').addClass('eight');
+        $('#last_name').removeClass('big').addClass('eight');
+        return false;
+      }
+      function showSortNameSearch() {
+        $('#indivfldselect').hide();
+        $('#sortnameselect').show();
+        $('#first_name').val('');
+        $('#last_name').val('');
+        return false;
+      }
+      $('#searchbyindivflds').click(showIndivFldsSearch);
+      $('#searchbysortname').click(showSortNameSearch);
+
+      if ($('#first_name').val() || $('#last_name').val()) {
+        showIndivFldsSearch();
+      }
+      else {
+        showSortNameSearch();
+      }
+    });
+  </script>
+{/literal}
diff --git a/civicrm/templates/CRM/Contact/Form/Search/Criteria/SearchSettings.tpl b/civicrm/templates/CRM/Contact/Form/Search/Criteria/SearchSettings.tpl
index 47a34fcb0d6e68309e4a2e6ef66c111e4ffb0415..c835f8c2637bf97a92c3420f211350aa401e2d40 100644
--- a/civicrm/templates/CRM/Contact/Form/Search/Criteria/SearchSettings.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Search/Criteria/SearchSettings.tpl
@@ -11,7 +11,7 @@
         </div>
         <div class="crm-submit-buttons reset-advanced-search">
           <a href="{crmURL p='civicrm/contact/search/advanced' q='reset=1'}" id="resetAdvancedSearch" class="crm-hover-button css_right" title="{ts}Clear all search criteria{/ts}">
-            <i class="crm-i fa-undo"></i>
+            <i class="crm-i fa-undo" aria-hidden="true"></i>
             {ts}Reset Form{/ts}
           </a>
         </div>
diff --git a/civicrm/templates/CRM/Contact/Form/Search/Custom/EventDetails.tpl b/civicrm/templates/CRM/Contact/Form/Search/Custom/EventDetails.tpl
index 42a7bc69040474a8f43ee5ec19388413e765adf1..2ad2a0bea4aa83629253b7ed7d0e1511657f24e5 100644
--- a/civicrm/templates/CRM/Contact/Form/Search/Custom/EventDetails.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Search/Custom/EventDetails.tpl
@@ -11,40 +11,39 @@
 {assign var="showBlock" value="'searchForm'"}
 {assign var="hideBlock" value="'searchForm_show','searchForm_hide'"}
 <div class="crm-block crm-form-block crm-search-form-block">
-<div id="searchForm_show" class="form-item">
-    <a href="#" onclick="cj('#searchForm_show').hide(); cj('#searchForm').show(); return false;"><img src="{$config->resourceBase}i/TreePlus.gif" class="action-icon" alt="{ts}open section{/ts}" /></a>
-    <label>{ts}Edit Search Criteria{/ts}</label>
-</div>
-
-<div id="searchForm" class="crm-block crm-form-block crm-contact-custom-search-eventDetails-form-block">
-    <fieldset>
-        <legend><span id="searchForm_hide"><a href="#" onclick="cj('#searchForm').hide(); cj('#searchForm_show').show(); return false;"><img src="{$config->resourceBase}i/TreeMinus.gif" class="action-icon" alt="{ts}close section{/ts}" /></a></span>{ts}Search Criteria{/ts}</legend>
-      <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location="top"}</div>
-        <table class="form-layout-compressed">
-            {* Loop through all defined search criteria fields (defined in the buildForm() function). *}
-            {foreach from=$elements item=element}
-                <tr class="crm-contact-custom-search-eventDetails-form-block-{$element}">
-                    <td class="label">{$form.$element.label}</td>
-                    <td>{$form.$element.html}</td>
+  <div class="crm-accordion-wrapper crm-eventDetails_search-accordion {if $rows}collapsed{/if}">
+    <div class="crm-accordion-header crm-master-accordion-header">
+      {ts}Edit Search Criteria{/ts}
+    </div><!-- /.crm-accordion-header -->
+    <div class="crm-accordion-body">
+      <div id="searchForm" class="crm-block crm-form-block crm-contact-custom-search-eventDetails-form-block">
+          <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location="top"}</div>
+            <table class="form-layout-compressed">
+                {* Loop through all defined search criteria fields (defined in the buildForm() function). *}
+                {foreach from=$elements item=element}
+                    <tr class="crm-contact-custom-search-eventDetails-form-block-{$element}">
+                        <td class="label">{$form.$element.label}</td>
+                        <td>{$form.$element.html}</td>
+                    </tr>
+                {/foreach}
+                <tr class="crm-contact-custom-search-eventDetails-form-block-event_type">
+                    <td class="label">{ts}Event Type{/ts}</td>
+                    <td>
+                        <div class="listing-box">
+                            {foreach from=$form.event_type_id item="event_val"}
+                                <div class="{cycle values="odd-row,even-row"}">
+                                    {$event_val.html}
+                                </div>
+                            {/foreach}
+                        </div>
+                        <div class="spacer"></div>
+                    </td>
                 </tr>
-            {/foreach}
-            <tr class="crm-contact-custom-search-eventDetails-form-block-event_type">
-                <td class="label">{ts}Event Type{/ts}</td>
-                <td>
-                    <div class="listing-box">
-                        {foreach from=$form.event_type_id item="event_val"}
-                            <div class="{cycle values="odd-row,even-row"}">
-                                {$event_val.html}
-                            </div>
-                        {/foreach}
-                    </div>
-                    <div class="spacer"></div>
-                </td>
-            </tr>
-        </table>
-      <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location="bottom"}</div>
-    </fieldset>
-</div>
+            </table>
+          <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location="bottom"}</div>
+      </div>
+    </div>
+  </div>
 
 {if $rowsEmpty}
     {include file="CRM/Contact/Form/Search/Custom/EmptyResults.tpl"}
@@ -118,12 +117,4 @@
     </fieldset>
     {* END Actions/Results section *}
 {/if}
-
-<script type="text/javascript">
-    var showBlock = new Array({$showBlock});
-    var hideBlock = new Array({$hideBlock});
-
-    {* hide and display the appropriate blocks *}
-    on_load_init_blocks( showBlock, hideBlock );
-</script>
 </div>
diff --git a/civicrm/templates/CRM/Contact/Form/Search/Custom/FullText.tpl b/civicrm/templates/CRM/Contact/Form/Search/Custom/FullText.tpl
index 6d15896a9a5bf40f6faabfa75350932c5537b06a..6a7d8b516a80234db780089d3708ffa7d1fc7d83 100644
--- a/civicrm/templates/CRM/Contact/Form/Search/Custom/FullText.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Search/Custom/FullText.tpl
@@ -62,7 +62,7 @@
     {if !$table and $summary.addShowAllLink.Contact}
       <div class="crm-section full-text-view-all-section">
         <a href="{crmURL p='civicrm/contact/search/custom' q="csid=`$csID`&reset=1&force=1&table=Contact&text=$text"}"
-        title="{ts}View all results for contacts{/ts}">&raquo;&nbsp;{ts}View all results for contacts{/ts}</a>
+        title="{ts}View all results for contacts{/ts}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i>&nbsp;{ts}View all results for contacts{/ts}</a>
       </div>{/if}
     {* note we using location="below" because we don't want to use rows per page for now. And therefore don't put location="bottom" for now. *}
     {if $table}{include file="CRM/common/pager.tpl" location="below"}{/if}
@@ -125,7 +125,7 @@
     {if !$table and $summary.addShowAllLink.Activity}
       <div class="crm-section full-text-view-all-section">
         <a href="{crmURL p='civicrm/contact/search/custom' q="csid=`$csID`&reset=1&force=1&table=Activity&text=$text"}"
-        title="{ts}View all results for activities{/ts}">&raquo;&nbsp;{ts}View all results for activities{/ts}</a>
+        title="{ts}View all results for activities{/ts}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i>&nbsp;{ts}View all results for activities{/ts}</a>
       </div>
     {/if}
     {if $table}{include file="CRM/common/pager.tpl" location="below"}{/if}
@@ -182,7 +182,7 @@
     {if !$table and $summary.addShowAllLink.Case}
       <div class="crm-section full-text-view-all-section">
         <a href="{crmURL p='civicrm/contact/search/custom' q="csid=`$csID`&reset=1&force=1&table=Case&text=$text"}"
-        title="{ts}View all results for cases{/ts}">&raquo;&nbsp;{ts}View all results for cases{/ts}</a>
+        title="{ts}View all results for cases{/ts}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i>&nbsp;{ts}View all results for cases{/ts}</a>
       </div>
     {/if}
     {if $table}{include file="CRM/common/pager.tpl" location="below"}{/if}
@@ -236,7 +236,7 @@
     {if !$table and $summary.addShowAllLink.Contribution}
       <div class="crm-section full-text-view-all-section">
         <a href="{crmURL p='civicrm/contact/search/custom' q="csid=`$csID`&reset=1&force=1&table=Contribution&text=$text"}"
-        title="{ts}View all results for contributions{/ts}">&raquo;&nbsp;{ts}View all results for contributions{/ts}</a>
+        title="{ts}View all results for contributions{/ts}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i>&nbsp;{ts}View all results for contributions{/ts}</a>
       </div>
     {/if}
     {if $table}{include file="CRM/common/pager.tpl" location="below"}{/if}
@@ -293,7 +293,7 @@
     {if !$table and $summary.addShowAllLink.Participant}
       <div class="crm-section full-text-view-all-section"><a
         href="{crmURL p='civicrm/contact/search/custom' q="csid=`$csID`&reset=1&force=1&table=Participant&text=$text"}"
-        title="{ts}View all results for participants{/ts}">&raquo;&nbsp;{ts}View all results for participants{/ts}</a>
+        title="{ts}View all results for participants{/ts}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i>&nbsp;{ts}View all results for participants{/ts}</a>
       </div>{/if}
     {if $table}{include file="CRM/common/pager.tpl" location="below"}{/if}
     {* END Actions/Results section *}
@@ -349,7 +349,7 @@
     {if !$table and $summary.addShowAllLink.Membership}
       <div class="crm-section full-text-view-all-section">
         <a href="{crmURL p='civicrm/contact/search/custom' q="csid=`$csID`&reset=1&force=1&table=Membership&text=$text"}"
-        title="{ts}View all results for memberships{/ts}">&raquo;&nbsp;{ts}View all results for memberships{/ts}</a>
+        title="{ts}View all results for memberships{/ts}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i>&nbsp;{ts}View all results for memberships{/ts}</a>
       </div>
     {/if}
     {if $table}{include file="CRM/common/pager.tpl" location="below"}{/if}
@@ -395,7 +395,7 @@
   {if !$table and $summary.addShowAllLink.File}
   <div class="crm-section full-text-view-all-section">
     <a href="{crmURL p='civicrm/contact/search/custom' q="csid=`$csID`&reset=1&force=1&table=File&text=$text"}"
-          title="{ts}View all results for files{/ts}">&raquo;&nbsp;{ts}View all results for files{/ts}</a>
+          title="{ts}View all results for files{/ts}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i>&nbsp;{ts}View all results for files{/ts}</a>
   </div>{/if}
   {if $table}{include file="CRM/common/pager.tpl" location="below"}{/if}
 {* END Actions/Results section *}
diff --git a/civicrm/templates/CRM/Contact/Form/Search/Intro.tpl b/civicrm/templates/CRM/Contact/Form/Search/Intro.tpl
index 8ee7f07c715034a1a842fe7baf2a38f3e925b803..d5d791c3b807d738b57fe467380e43ffa4c34b00 100644
--- a/civicrm/templates/CRM/Contact/Form/Search/Intro.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Search/Intro.tpl
@@ -20,7 +20,7 @@
             {capture assign=editSmartGroupURL}{crmURL p="civicrm/contact/search/advanced" q="reset=1&ssID=`$ssID`"}{/capture}
         {/if}
         <div class="crm-submit-buttons">
-            <a href="{$editSmartGroupURL}" class="button no-popup"><span><i class="crm-i fa-pencil"></i> {ts 1=$group.title}Edit Smart Group Search Criteria for %1{/ts}</span></a>
+            <a href="{$editSmartGroupURL}" class="button no-popup"><span><i class="crm-i fa-pencil" aria-hidden="true"></i> {ts 1=$group.title}Edit Smart Group Search Criteria for %1{/ts}</span></a>
             {help id="id-edit-smartGroup"}
         </div>
     {/if}
@@ -28,7 +28,7 @@
     {if $permissionedForGroup}
         {capture assign=addMembersURL}{crmURL q="context=amtg&amtgID=`$group.id`&reset=1"}{/capture}
         <div class="crm-submit-buttons">
-            <a href="{$addMembersURL}" class="button no-popup"><span><i class="crm-i fa-user-plus"></i> {ts 1=$group.title}Add Contacts to %1{/ts}</span></a>
+            <a href="{$addMembersURL}" class="button no-popup"><span><i class="crm-i fa-user-plus" aria-hidden="true"></i> {ts 1=$group.title}Add Contacts to %1{/ts}</span></a>
             {if $ssID}{help id="id-add-to-smartGroup"}{/if}
         </div>
     {/if}
diff --git a/civicrm/templates/CRM/Contact/Form/Search/ResultTasks.tpl b/civicrm/templates/CRM/Contact/Form/Search/ResultTasks.tpl
index aeb50dc2540d71e7f1f8b2e7fd23f23af989b8ee..1e3e0df3cca72525ceb79e5b5cadf04d1fe95c93 100644
--- a/civicrm/templates/CRM/Contact/Form/Search/ResultTasks.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Search/ResultTasks.tpl
@@ -18,9 +18,9 @@
  <div id="search-status">
   <div class="float-right right">
     {if $action eq 256}
-        <a href="{$advSearchURL}">&raquo; {ts}Advanced Search{/ts}</a><br />
+        <a href="{$advSearchURL}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Advanced Search{/ts}</a><br />
         {if $context eq 'search'} {* Only show Search Builder link for basic search. *}
-            <a href="{$searchBuilderURL}">&raquo; {ts}Search Builder{/ts}</a><br />
+            <a href="{$searchBuilderURL}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Search Builder{/ts}</a><br />
         {/if}
         {if $context eq 'smog'}
             {help id="id-smog-criteria" group_id=$group.id group_title=$group.title ssID=$ssID ssMappingID=$ssMappingID permissionedForGroup=$permissionedForGroup}
@@ -30,9 +30,9 @@
             {help id="id-basic-criteria"}
         {/if}
     {elseif $action eq 512}
-        <a href="{$searchBuilderURL}">&raquo; {ts}Search Builder{/ts}</a><br />
+        <a href="{$searchBuilderURL}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Search Builder{/ts}</a><br />
     {elseif $action eq 8192}
-        <a href="{$advSearchURL}">&raquo; {ts}Advanced Search{/ts}</a><br />
+        <a href="{$advSearchURL}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Advanced Search{/ts}</a><br />
     {/if}
   </div>
 
diff --git a/civicrm/templates/CRM/Contact/Form/Search/table.tpl b/civicrm/templates/CRM/Contact/Form/Search/table.tpl
index beedaa55d707d5adc56f9475472445e5c5d8b834..b3c094596dc49f49f56ba4a2c05933440421302e 100644
--- a/civicrm/templates/CRM/Contact/Form/Search/table.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Search/table.tpl
@@ -25,7 +25,7 @@
                 {$form.value[$x][$i].html|crmAddClass:'required'}
               </span>
               {if $i gt 0 or $x gt 1}
-                &nbsp;<a href="#" class="crm-reset-builder-row crm-hover-button" title="{ts}Remove this row{/ts}"><i class="crm-i fa-times"></i></a>
+                &nbsp;<a href="#" class="crm-reset-builder-row crm-hover-button" title="{ts}Remove this row{/ts}"><i class="crm-i fa-times" aria-hidden="true"></i></a>
               {/if}
             </td>
           </tr>
diff --git a/civicrm/templates/CRM/Contact/Form/Selector.tpl b/civicrm/templates/CRM/Contact/Form/Selector.tpl
index 338c6e0883b901557294975c9242a43ac1d2be58..0641bebefb7c2ccee8228073c6386074d2ca55a3 100644
--- a/civicrm/templates/CRM/Contact/Form/Selector.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Selector.tpl
@@ -10,7 +10,7 @@
 {include file="CRM/common/pager.tpl" location="top"}
 
 {include file="CRM/common/pagerAToZ.tpl"}
-<a href="#" class="crm-selection-reset crm-hover-button"><i class="crm-i fa-times-circle-o"></i> {ts}Reset all selections{/ts}</a>
+<a href="#" class="crm-selection-reset crm-hover-button"><i class="crm-i fa-times-circle-o" aria-hidden="true"></i> {ts}Reset all selections{/ts}</a>
 
 <table summary="{ts}Search results listings.{/ts}" class="selector row-highlight">
   <thead class="sticky">
@@ -81,7 +81,7 @@
             <td><a href="{crmURL p='civicrm/contact/view' q="reset=1&cid=`$row.contact_id`&key=`$qfKey`&context=`$context`"}">{if $row.is_deleted}<del>{/if}{$row.sort_name}{if $row.is_deleted}</del>{/if}</a></td>
             {if $action eq 512 or $action eq 256}
               {if !empty($columnHeaders.street_address)}
-          <td><span title="{$row.street_address|escape}">{$row.street_address|mb_truncate:22:"...":true}{if $row.do_not_mail} <span class="icon privacy-flag do-not-mail"></span>{/if}</span></td>
+          <td><span title="{$row.street_address|escape}">{$row.street_address|mb_truncate:22:"...":true}{privacyFlag field=do_not_mail condition=$row.do_not_mail}</span></td>
         {/if}
         {if !empty($columnHeaders.city)}
                 <td>{$row.city}</td>
@@ -99,20 +99,16 @@
                 {if $row.email}
                     <span title="{$row.email|escape}">
                         {$row.email|mb_truncate:17:"...":true}
-                        {if $row.on_hold}
-                          (On Hold)<span class="status-hold" title="{ts}This email is on hold (probably due to bouncing).{/ts}"></span>
-                        {elseif $row.do_not_email}
-                          <span class="icon privacy-flag do-not-email" title="{ts}Do Not Email{/ts}"></span>
-                        {/if}
+                        {privacyFlag field=do_not_email condition=$row.do_not_email}
+                        {privacyFlag field=on_hold condition=$row.on_hold}
                     </span>
                 {/if}
               </td>
               <td>
                 {if $row.phone}
                   {$row.phone}
-                  {if $row.do_not_phone}
-                    <span class="icon privacy-flag do-not-phone" title="{ts}Do Not Phone{/ts}" ></span>
-                  {/if}
+                  {privacyFlag field=do_not_phone condition=$row.do_not_phone}
+                  {privacyFlag field=do_not_sms condition=$row.do_not_sms}
                 {/if}
               </td>
            {else}
diff --git a/civicrm/templates/CRM/Contact/Form/Task/Batch.tpl b/civicrm/templates/CRM/Contact/Form/Task/Batch.tpl
index 8c62574ea57b74cf909e061ed317c23cc4f27694..2cd61d93b1a1f1df9ccae907fac67afd2491ac50 100644
--- a/civicrm/templates/CRM/Contact/Form/Task/Batch.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Task/Batch.tpl
@@ -19,7 +19,7 @@
       {if $field.skipDisplay}
         {continue}
       {/if}
-      <td><img  src="{$config->resourceBase}i/copy.png" alt="{ts 1=$field.title}Click to copy %1 from row one to all rows.{/ts}" fname="{$field.name}" class="action-icon" title="{ts}Click here to copy the value in row one to ALL rows.{/ts}" />{$field.title}</td>
+      <td>{copyIcon name=$field.name title=$field.title}{$field.title}</td>
     {/foreach}
     </tr>
     </thead>
@@ -78,4 +78,3 @@
 
 {*include batch copy js js file*}
 {include file="CRM/common/batchCopy.tpl"}
-
diff --git a/civicrm/templates/CRM/Contact/Form/Task/Email.tpl b/civicrm/templates/CRM/Contact/Form/Task/Email.tpl
index 9d1bf5989db31208d6502b793eed4852f557fcad..0ab5a8930edbe7f312ce43e4b4089d1c807e8ce8 100644
--- a/civicrm/templates/CRM/Contact/Form/Task/Email.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Task/Email.tpl
@@ -30,14 +30,14 @@
       <td class="label">{$form.cc_id.label}</td>
       <td>
         {$form.cc_id.html}
-        <a class="crm-hover-button clear-cc-link" rel="cc_id" title="{ts}Clear{/ts}" href="#"><i class="crm-i fa-times"></i></a>
+        <a class="crm-hover-button clear-cc-link" rel="cc_id" title="{ts}Clear{/ts}" href="#"><i class="crm-i fa-times" aria-hidden="true"></i></a>
       </td>
     </tr>
     <tr class="crm-contactEmail-form-block-bcc_id" {if !$form.bcc_id.value}style="display:none;"{/if}>
       <td class="label">{$form.bcc_id.label}</td>
       <td>
         {$form.bcc_id.html}
-        <a class="crm-hover-button clear-cc-link" rel="bcc_id" title="{ts}Clear{/ts}" href="#"><i class="crm-i fa-times"></i></a>
+        <a class="crm-hover-button clear-cc-link" rel="bcc_id" title="{ts}Clear{/ts}" href="#"><i class="crm-i fa-times" aria-hidden="true"></i></a>
       </td>
     </tr>
     <tr>
diff --git a/civicrm/templates/CRM/Contact/Import/Form/Preview.tpl b/civicrm/templates/CRM/Contact/Import/Form/Preview.tpl
index ff88428d86cd6fcfbba2d8bfa969ef7701df8f6c..15b30f1fbc271ea861e8dacf4119f66cac231094 100644
--- a/civicrm/templates/CRM/Contact/Import/Form/Preview.tpl
+++ b/civicrm/templates/CRM/Contact/Import/Form/Preview.tpl
@@ -48,7 +48,7 @@
         <td class="data">{$invalidRowCount}</td>
         <td class="explanation">{ts}Rows with invalid data in one or more fields (for example, invalid email address formatting). These rows will be skipped (not imported).{/ts}
             {if $invalidRowCount}
-                <div class="action-link"><a href="{$downloadErrorRecordsUrl}">&raquo; {ts}Download Errors{/ts}</a></div>
+                <div class="action-link"><a href="{$downloadErrorRecordsUrl}"><i class="crm-i fa-download" aria-hidden="true"></i> {ts}Download Errors{/ts}</a></div>
             {/if}
         </td>
     </tr>
@@ -59,7 +59,7 @@
         <td class="data">{$conflictRowCount}</td>
         <td class="explanation">{ts}Rows with conflicting email addresses within this file. These rows will be skipped (not imported).{/ts}
             {if $conflictRowCount}
-                <div class="action-link"><a href="{$downloadConflictRecordsUrl}">&raquo; {ts}Download Conflicts{/ts}</a></div>
+                <div class="action-link"><a href="{$downloadConflictRecordsUrl}"><i class="crm-i fa-download" aria-hidden="true"></i> {ts}Download Conflicts{/ts}</a></div>
             {/if}
         </td>
     </tr>
diff --git a/civicrm/templates/CRM/Contact/Import/Form/Summary.tpl b/civicrm/templates/CRM/Contact/Import/Form/Summary.tpl
index 7b4a540ea93a6b7b0c397a8ab1febdfbedc451fd..0b3577e65c09d7824642d41c5498dda731c9d013 100644
--- a/civicrm/templates/CRM/Contact/Import/Form/Summary.tpl
+++ b/civicrm/templates/CRM/Contact/Import/Form/Summary.tpl
@@ -75,7 +75,7 @@
         <td class="data">{$invalidRowCount}</td>
         <td class="explanation">{ts}Rows with invalid data in one or more fields (for example, invalid email address formatting). These rows will be skipped (not imported).{/ts}
             {if $invalidRowCount}
-                <div class="action-link"><a href="{$downloadErrorRecordsUrl}">&raquo; {ts}Download Errors{/ts}</a></div>
+                <div class="action-link"><a href="{$downloadErrorRecordsUrl}"><i class="crm-i fa-download" aria-hidden="true"></i> {ts}Download Errors{/ts}</a></div>
             {/if}
         </td>
     </tr>
@@ -86,7 +86,7 @@
         <td class="data">{$unMatchCount}</td>
         <td class="explanation">{ts}Rows with mismatched contact IDs... (NOT updated).{/ts}
             {if $unMatchCount}
-                <<div class="action-link"><a href="{$downloadMismatchRecordsUrl}">&raquo; {ts}Download Mismatched Contacts{/ts}</a></div>
+                <<div class="action-link"><a href="{$downloadMismatchRecordsUrl}"><i class="crm-i fa-download" aria-hidden="true"></i> {ts}Download Mismatched Contacts{/ts}</a></div>
             {/if}
         </td>
     </tr>
@@ -97,7 +97,7 @@
         <td class="data">{$conflictRowCount}</td>
         <td class="explanation">{ts}Rows with conflicting email addresses (NOT imported).{/ts}
             {if $conflictRowCount}
-                <div class="action-link"><a href="{$downloadConflictRecordsUrl}">&raquo; {ts}Download Conflicts{/ts}</a></div>
+                <div class="action-link"><a href="{$downloadConflictRecordsUrl}"><i class="crm-i fa-download" aria-hidden="true"></i> {ts}Download Conflicts{/ts}</a></div>
             {/if}
         </td>
     </tr>
@@ -108,7 +108,7 @@
         <td class="data">{$duplicateRowCount}</td>
         <td class="explanation">{ts}Rows which are duplicates of existing CiviCRM contact records.{/ts} {$dupeActionString}
             {if $duplicateRowCount}
-                <div class="action-link"><a href="{$downloadDuplicateRecordsUrl}">&raquo; {ts}Download Duplicates{/ts}</a></div>
+                <div class="action-link"><a href="{$downloadDuplicateRecordsUrl}"><i class="crm-i fa-download" aria-hidden="true"></i> {ts}Download Duplicates{/ts}</a></div>
             {/if}
         </td>
     </tr>
@@ -151,4 +151,3 @@
 
  <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location="bottom"}</div>
 </div>
-
diff --git a/civicrm/templates/CRM/Contact/Page/CustomSearch.tpl b/civicrm/templates/CRM/Contact/Page/CustomSearch.tpl
index f308f9247884be2b17a09cd0867d4e12ef7b843d..4d9a9600d6a2583bb75a6f582147b4b3de31475b 100644
--- a/civicrm/templates/CRM/Contact/Page/CustomSearch.tpl
+++ b/civicrm/templates/CRM/Contact/Page/CustomSearch.tpl
@@ -16,7 +16,7 @@
 {if $rows}
     {foreach from=$rows item=customTitle key=csid}
         <div class="action-link">
-            <a href="{crmURL p="civicrm/contact/search/custom" q="csid=`$csid`&reset=1"}" title="{ts}Use this search{/ts}">&raquo; {$customTitle}</a>
+            <a href="{crmURL p="civicrm/contact/search/custom" q="csid=`$csid`&reset=1"}" title="{ts}Use this search{/ts}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {$customTitle}</a>
         </div>
     {/foreach}
 {else}
diff --git a/civicrm/templates/CRM/Contact/Page/DashBoardDashlet.tpl b/civicrm/templates/CRM/Contact/Page/DashBoardDashlet.tpl
index 694dc6cb08e71b37e0824ce3c9d4abf08473894c..54b26340775944a40c81332c29216753b6984278 100644
--- a/civicrm/templates/CRM/Contact/Page/DashBoardDashlet.tpl
+++ b/civicrm/templates/CRM/Contact/Page/DashBoardDashlet.tpl
@@ -13,11 +13,11 @@
 {$communityMessages}
 <div class="crm-submit-buttons crm-dashboard-controls">
 <a href="#" id="crm-dashboard-configure" class="crm-hover-button show-add">
-  <i class="crm-i fa-wrench"></i> {ts}Configure Your Dashboard{/ts}
+  <i class="crm-i fa-wrench" aria-hidden="true"></i> {ts}Configure Your Dashboard{/ts}
 </a>
 
 <a style="float:right;" href="#" class="crm-hover-button show-refresh" style="margin-left: 6px;">
-  <i class="crm-i fa-refresh"></i> {ts}Refresh Dashboard Data{/ts}
+  <i class="crm-i fa-refresh" aria-hidden="true"></i> {ts}Refresh Dashboard Data{/ts}
 </a>
 
 </div>
diff --git a/civicrm/templates/CRM/Contact/Page/Dashlet.tpl b/civicrm/templates/CRM/Contact/Page/Dashlet.tpl
index 91256848cf35f685b833b8105d19888deeaf1e19..fa1dcfa47ced4b524ab0bd8cd54a1ec197d85bc8 100644
--- a/civicrm/templates/CRM/Contact/Page/Dashlet.tpl
+++ b/civicrm/templates/CRM/Contact/Page/Dashlet.tpl
@@ -16,7 +16,7 @@
     <div id="available-dashlets" class="dash-column">
         {foreach from=$availableDashlets item=row key=dashID}
       <div class="portlet">
-        <div class="portlet-header" id="{$dashID}">{$row.label}{if $admin and !$row.is_reserved}&nbsp;<a class="crm-i fa-times delete-dashlet"></a>{/if}</div>
+        <div class="portlet-header" id="{$dashID}">{$row.label}{if $admin and !$row.is_reserved}&nbsp;<a class="crm-i fa-times delete-dashlet" aria-hidden="true"></a>{/if}</div>
       </div>
         {/foreach}
     </div>
@@ -27,7 +27,7 @@
     <div id="existing-dashlets-col-0" class="dash-column">
         {foreach from=$contactDashlets.0 item=row key=dashID}
       <div class="portlet">
-        <div class="portlet-header" id="{$dashID}">{$row.label}{if $admin and !$row.is_reserved}&nbsp;<a class="crm-i fa-times delete-dashlet"></a>{/if}</div>
+        <div class="portlet-header" id="{$dashID}">{$row.label}{if $admin and !$row.is_reserved}&nbsp;<a class="crm-i fa-times delete-dashlet" aria-hidden="true"></a>{/if}</div>
       </div>
         {/foreach}
     </div>
@@ -35,7 +35,7 @@
     <div id="existing-dashlets-col-1" class="dash-column">
         {foreach from=$contactDashlets.1 item=row key=dashID}
       <div class="portlet">
-        <div class="portlet-header" id="{$dashID}">{$row.label}{if $admin and !$row.is_reserved}&nbsp;<a class="crm-i fa-times delete-dashlet"></a>{/if}</div>
+        <div class="portlet-header" id="{$dashID}">{$row.label}{if $admin and !$row.is_reserved}&nbsp;<a class="crm-i fa-times delete-dashlet" aria-hidden="true"></a>{/if}</div>
       </div>
         {/foreach}
     </div>
diff --git a/civicrm/templates/CRM/Contact/Page/DedupeException.tpl b/civicrm/templates/CRM/Contact/Page/DedupeException.tpl
index 26898114f801de7774088c65d1006cb3f4ddf8b5..2c69cc063c776e0bc2e1efa7c8a3f12aa2046e12 100644
--- a/civicrm/templates/CRM/Contact/Page/DedupeException.tpl
+++ b/civicrm/templates/CRM/Contact/Page/DedupeException.tpl
@@ -21,7 +21,7 @@
         </td>
         <td class="crm-contact-form-block-search">
           <label>&nbsp;</label><br />
-          <button type="submit" class="button crm-button filtercontacts"><span><i class="crm-i fa-search"></i> Find Contacts</span></button>
+          <button type="submit" class="button crm-button filtercontacts"><span><i class="crm-i fa-search" aria-hidden="true"></i> Find Contacts</span></button>
         </td>
       </tr>
     </table>
@@ -60,7 +60,7 @@
             <a href="{crmURL p='civicrm/contact/view' q="reset=1&cid=`$exception.contact_id2`"}" target="_blank">{ $exception.$contact2name }</a>
           </td>
           <td>
-            <a id='duplicateContacts' href="#" title={ts}Remove Exception{/ts} onClick="processDupes( {$exception.contact_id1}, {$exception.contact_id2}, 'nondupe-dupe', 'dedupe-exception' );return false;">&raquo; {ts}Remove Exception{/ts}</a>
+            <a id='duplicateContacts' href="#" title={ts}Remove Exception{/ts} onClick="processDupes( {$exception.contact_id1}, {$exception.contact_id2}, 'nondupe-dupe', 'dedupe-exception' );return false;"><i class="crm-i fa-trash" aria-hidden="true"></i> {ts}Remove Exception{/ts}</a>
           </td>
         </tr>
 
diff --git a/civicrm/templates/CRM/Contact/Page/DedupeFind.tpl b/civicrm/templates/CRM/Contact/Page/DedupeFind.tpl
index d28538584656f782017cc89677888ca1dbd9911a..9f811b99d26680f9bd398aeb7ae2af3f5e873177 100644
--- a/civicrm/templates/CRM/Contact/Page/DedupeFind.tpl
+++ b/civicrm/templates/CRM/Contact/Page/DedupeFind.tpl
@@ -107,31 +107,31 @@
 {elseif $context eq 'conflicts'}
   {if call_user_func(array('CRM_Core_Permission','check'), 'force merge duplicate contacts')}
      {capture assign=backURL}{crmURL p="civicrm/contact/dedupemerge" q="`$urlQuery`&action=map&mode=aggressive" a=1}{/capture}
-     <a href="{$backURL}" title="{ts}Force Merge Selected Duplicates{/ts}" onclick="return confirm('{ts escape="js"}This will run the batch merge process on the selected duplicates. The operation will run in force merge mode - all selected duplicates will be merged into main contacts even in case of any conflicts. Click OK to proceed if you are sure you wish to run this operation.{/ts}');" class="button"><span><i class="crm-i fa-bolt"></i> {ts}Force Merge Selected Duplicates{/ts}</span></a>
+     <a href="{$backURL}" title="{ts}Force Merge Selected Duplicates{/ts}" onclick="return confirm('{ts escape="js"}This will run the batch merge process on the selected duplicates. The operation will run in force merge mode - all selected duplicates will be merged into main contacts even in case of any conflicts. Click OK to proceed if you are sure you wish to run this operation.{/ts}');" class="button"><span><i class="crm-i fa-bolt" aria-hidden="true"></i> {ts}Force Merge Selected Duplicates{/ts}</span></a>
 
      {capture assign=backURL}{crmURL p="civicrm/contact/dedupemerge" q="`$urlQuery`&action=map" a=1}{/capture}
-     <a href="{$backURL}" title="{ts}Safe Merge Selected Duplicates{/ts}" onclick="return confirm('{ts escape="js"}This will run the batch merge process on the selected duplicates. The operation will run in safe mode - only records with no direct data conflicts will be merged. Click OK to proceed if you are sure you wish to run this operation.{/ts}');" class="button"><span><i class="crm-i fa-compress"></i> {ts}Safe Merge Selected Duplicates{/ts}</span></a>
+     <a href="{$backURL}" title="{ts}Safe Merge Selected Duplicates{/ts}" onclick="return confirm('{ts escape="js"}This will run the batch merge process on the selected duplicates. The operation will run in safe mode - only records with no direct data conflicts will be merged. Click OK to proceed if you are sure you wish to run this operation.{/ts}');" class="button"><span><i class="crm-i fa-compress" aria-hidden="true"></i> {ts}Safe Merge Selected Duplicates{/ts}</span></a>
   {/if}
 
   {capture assign=backURL}{crmURL p="civicrm/contact/dedupefind" q="`$urlQuery`&action=update&selected=0" a=1}{/capture}
-   <a href="{$backURL}" title="{ts}List All Duplicates{/ts}" class="button"><span><i class="crm-i fa-refresh"></i> {ts}List All Duplicates{/ts}</span></a>
+   <a href="{$backURL}" title="{ts}List All Duplicates{/ts}" class="button"><span><i class="crm-i fa-refresh" aria-hidden="true"></i> {ts}List All Duplicates{/ts}</span></a>
 {else}
    {capture assign=backURL}{crmURL p="civicrm/contact/dedupefind" q="`$urlQuery`&action=renew" a=1}{/capture}
    <a href="{$backURL}" title="{ts}Refresh List of Duplicates{/ts}" onclick="return confirm('{ts escape="js"}This will refresh the duplicates list. Click OK to proceed.{/ts}');" class="button">
-     <span><i class="crm-i fa-refresh"></i> {ts}Refresh Duplicates{/ts}</span>
+     <span><i class="crm-i fa-refresh" aria-hidden="true"></i> {ts}Refresh Duplicates{/ts}</span>
    </a>
 
   {capture assign=backURL}{crmURL p="civicrm/contact/dedupemerge" q="`$urlQuery`&action=map" a=1}{/capture}
-   <a href="{$backURL}" title="{ts}Batch Merge Duplicate Contacts{/ts}" onclick="return confirm('{ts escape="js"}This will run the batch merge process on the selected duplicates. The operation will run in safe mode - only records with no direct data conflicts will be merged. Click OK to proceed if you are sure you wish to run this operation.{/ts}');" class="button"><span><i class="crm-i fa-compress"></i> {ts}Batch Merge Selected Duplicates{/ts}</span></a>
+   <a href="{$backURL}" title="{ts}Batch Merge Duplicate Contacts{/ts}" onclick="return confirm('{ts escape="js"}This will run the batch merge process on the selected duplicates. The operation will run in safe mode - only records with no direct data conflicts will be merged. Click OK to proceed if you are sure you wish to run this operation.{/ts}');" class="button"><span><i class="crm-i fa-compress" aria-hidden="true"></i> {ts}Batch Merge Selected Duplicates{/ts}</span></a>
 
    {capture assign=backURL}{crmURL p="civicrm/contact/dedupemerge" q=$urlQuery a=1}{/capture}
-   <a href="{$backURL}" title="{ts}Batch Merge Duplicate Contacts{/ts}" onclick="return confirm('{ts escape="js"}This will run the batch merge process on the listed duplicates. The operation will run in safe mode - only records with no direct data conflicts will be merged. Click OK to proceed if you are sure you wish to run this operation.{/ts}');" class="button"><span><i class="crm-i fa-compress"></i> {ts}Batch Merge All Duplicates{/ts}</span></a>
+   <a href="{$backURL}" title="{ts}Batch Merge Duplicate Contacts{/ts}" onclick="return confirm('{ts escape="js"}This will run the batch merge process on the listed duplicates. The operation will run in safe mode - only records with no direct data conflicts will be merged. Click OK to proceed if you are sure you wish to run this operation.{/ts}');" class="button"><span><i class="crm-i fa-compress" aria-hidden="true"></i> {ts}Batch Merge All Duplicates{/ts}</span></a>
 
-   <a href='#' title="{ts}Flip Selected Duplicates{/ts}" class="crm-dedupe-flip-selections button"><span><i class="crm-i fa-exchange"></i> {ts}Flip Selected Duplicates{/ts}</span></a>
+   <a href='#' title="{ts}Flip Selected Duplicates{/ts}" class="crm-dedupe-flip-selections button"><span><i class="crm-i fa-exchange" aria-hidden="true"></i> {ts}Flip Selected Duplicates{/ts}</span></a>
 
    {capture assign=backURL}{crmURL p="civicrm/contact/deduperules" q="reset=1" a=1}{/capture}
    <a href="{$backURL}" class="button crm-button-type-cancel">
-     <span><i class="crm-i fa-times"></i> {ts}Done{/ts}</span>
+     <span><i class="crm-i fa-times" aria-hidden="true"></i> {ts}Done{/ts}</span>
    </a>
 {/if}
 <div style="clear: both;"></div>
diff --git a/civicrm/templates/CRM/Contact/Page/Inline/Address.tpl b/civicrm/templates/CRM/Contact/Page/Inline/Address.tpl
index d507e76d3f9777142106d361473e9fcc8d213196..8861f0ba0f3c5c593d5a2ec838e38fd62d7820d5 100644
--- a/civicrm/templates/CRM/Contact/Page/Inline/Address.tpl
+++ b/civicrm/templates/CRM/Contact/Page/Inline/Address.tpl
@@ -12,7 +12,7 @@
   <div class="crm-clear crm-inline-block-content" {if $permission EQ 'edit'}title="{if $add}{ts}Edit address{/ts}{else}{ts}Add address{/ts}{/if}"{/if}>
     {if $permission EQ 'edit'}
       <div class="crm-edit-help">
-        <span class="crm-i fa-pencil"></span> {if $add}{ts}Edit address{/ts}{else}{ts}Add address{/ts}{/if}
+        <span class="crm-i fa-pencil" aria-hidden="true"></span> {if $add}{ts}Edit address{/ts}{else}{ts}Add address{/ts}{/if}
       </div>
     {/if}
     {if !$add}
@@ -24,14 +24,14 @@
       <div class="crm-summary-row {if $add.is_primary eq 1} primary{/if}">
         <div class="crm-label">
           {ts 1=$add.location_type}%1 Address{/ts}
-          {if $privacy.do_not_mail}<span class="icon privacy-flag do-not-mail" title="{ts}Privacy flag: Do Not Mail{/ts}"></span>{/if}
+          {privacyFlag field=do_not_mail condition=$privacy.do_not_mail}
           {if $config->mapProvider AND
               !empty($add.geo_code_1) AND
               is_numeric($add.geo_code_1) AND
               !empty($add.geo_code_2) AND
               is_numeric($add.geo_code_2)
           }
-          <br /><a href="{crmURL p='civicrm/contact/map' q="reset=1&cid=`$contactId`&lid=`$add.location_type_id`"}" title="{ts 1=`$add.location_type`}Map %1 Address{/ts}"><span class="geotag">{ts}Map{/ts}</span></a>
+          <br /><a href="{crmURL p='civicrm/contact/map' q="reset=1&cid=`$contactId`&lid=`$add.location_type_id`"}" title="{ts 1=`$add.location_type`}Map %1 Address{/ts}"><i class="crm-i fa-map-marker" aria-hidden="true"></i> {ts}Map{/ts}</a>
           {/if}
         </div>
         <div class="crm-content">
diff --git a/civicrm/templates/CRM/Contact/Page/Inline/CommunicationPreferences.tpl b/civicrm/templates/CRM/Contact/Page/Inline/CommunicationPreferences.tpl
index bacf3cad18aa39329ba26df14728afe4885b983f..1093e03c9230c1d6dfb2412166ff55ce6af45587 100644
--- a/civicrm/templates/CRM/Contact/Page/Inline/CommunicationPreferences.tpl
+++ b/civicrm/templates/CRM/Contact/Page/Inline/CommunicationPreferences.tpl
@@ -12,7 +12,7 @@
   <div class="crm-clear crm-inline-block-content"{if $permission EQ 'edit'} title="{ts}Edit communication preferences{/ts}"{/if}>
     {if $permission EQ 'edit'}
     <div class="crm-edit-help">
-      <span class="crm-i fa-pencil"></span> {ts}Edit communication preferences{/ts}
+      <span class="crm-i fa-pencil" aria-hidden="true"></span> {ts}Edit communication preferences{/ts}
     </div>
     {/if}
     <div class="crm-summary-row">
diff --git a/civicrm/templates/CRM/Contact/Page/Inline/ContactInfo.tpl b/civicrm/templates/CRM/Contact/Page/Inline/ContactInfo.tpl
index 9c76fe067356175e519f2c05c4d39e8673874b23..e6f23db3399b1ae5a0ce84121095041785d9b9b0 100644
--- a/civicrm/templates/CRM/Contact/Page/Inline/ContactInfo.tpl
+++ b/civicrm/templates/CRM/Contact/Page/Inline/ContactInfo.tpl
@@ -12,7 +12,7 @@
   <div class="crm-clear crm-inline-block-content" {if $permission EQ 'edit'}title="{ts}Edit info{/ts}"{/if}>
     {if $permission EQ 'edit'}
     <div class="crm-edit-help">
-      <span class="crm-i fa-pencil"></span> {ts}Edit info{/ts}
+      <span class="crm-i fa-pencil" aria-hidden="true"></span> {ts}Edit info{/ts}
     </div>
     {/if}
 
diff --git a/civicrm/templates/CRM/Contact/Page/Inline/ContactName.tpl b/civicrm/templates/CRM/Contact/Page/Inline/ContactName.tpl
index 715c7c5bb9cec7e1cec25e71345ee5e3f5745b8c..f32f169cf7fd09b6df96432f37f98b9fa938d723 100644
--- a/civicrm/templates/CRM/Contact/Page/Inline/ContactName.tpl
+++ b/civicrm/templates/CRM/Contact/Page/Inline/ContactName.tpl
@@ -11,7 +11,7 @@
   <div class="crm-inline-block-content"{if $permission EQ 'edit'} title="{ts}Edit Contact Name{/ts}"{/if}>
     {if $permission EQ 'edit'}
       <div class="crm-edit-help">
-        <span class="crm-i fa-pencil"></span> {ts}Edit name{/ts}
+        <span class="crm-i fa-pencil" aria-hidden="true"></span> {ts}Edit name{/ts}
       </div>
     {/if}
 
diff --git a/civicrm/templates/CRM/Contact/Page/Inline/Demographics.tpl b/civicrm/templates/CRM/Contact/Page/Inline/Demographics.tpl
index 3746377aaf0f7de6d81898a6894996edeceecf67..dee1ed5c2d24a5dcdaf0713bfb1229c6537f6762 100644
--- a/civicrm/templates/CRM/Contact/Page/Inline/Demographics.tpl
+++ b/civicrm/templates/CRM/Contact/Page/Inline/Demographics.tpl
@@ -11,7 +11,7 @@
   <div class="crm-clear crm-inline-block-content" {if $permission EQ 'edit'}title="{ts}Edit demographics{/ts}"{/if}>
     {if $permission EQ 'edit'}
     <div class="crm-edit-help">
-      <span class="crm-i fa-pencil"></span> {ts}Edit demographics{/ts}
+      <span class="crm-i fa-pencil" aria-hidden="true"></span> {ts}Edit demographics{/ts}
     </div>
     {/if}
     <div class="crm-summary-row">
diff --git a/civicrm/templates/CRM/Contact/Page/Inline/Email.tpl b/civicrm/templates/CRM/Contact/Page/Inline/Email.tpl
index 48aa7263cfd711c0ccfe69518e2e6a1a88de390a..ee977d431451740700077352d157462f07d644a6 100644
--- a/civicrm/templates/CRM/Contact/Page/Inline/Email.tpl
+++ b/civicrm/templates/CRM/Contact/Page/Inline/Email.tpl
@@ -12,14 +12,14 @@
   <div class="crm-clear crm-inline-block-content" {if $permission EQ 'edit'}title="{ts}Add or edit email{/ts}"{/if}>
   {if $permission EQ 'edit'}
     <div class="crm-edit-help">
-      <span class="crm-i fa-pencil"></span> {if empty($email)}{ts}Add email{/ts}{else}{ts}Add or edit email{/ts}{/if}
+      <span class="crm-i fa-pencil" aria-hidden="true"></span> {if empty($email)}{ts}Add email{/ts}{else}{ts}Add or edit email{/ts}{/if}
     </div>
   {/if}
   {if empty($email)}
     <div class="crm-summary-row">
       <div class="crm-label">
         {ts}Email{/ts}
-        {if $privacy.do_not_email}<span class="icon privacy-flag do-not-email" title="{ts}Privacy flag: Do Not Email{/ts}"></span>{/if}
+        {if $privacy.do_not_email}{privacyFlag field=do_not_email}{/if}
       </div>
       <div class="crm-content"></div>
     </div>
@@ -29,7 +29,7 @@
     <div class="crm-summary-row {if $item.is_primary eq 1}primary{/if}">
       <div class="crm-label">
         {$item.location_type} {ts}Email{/ts}
-        {if $privacy.do_not_email}<span class="icon privacy-flag do-not-email" title="{ts}Privacy flag: Do Not Email{/ts}"></span>{elseif $item.on_hold}<span class="icon privacy-flag email-hold" title="{ts}Email on hold - generally due to bouncing.{/ts}"></span>{/if}
+        {privacyFlag field=do_not_email condition=$privacy.do_not_email}{privacyFlag field=on_hold condition=$item.on_hold}
       </div>
       <div class="crm-content crm-contact_email">
         {if !$item.on_hold and !$privacy.do_not_email}
diff --git a/civicrm/templates/CRM/Contact/Page/Inline/IM.tpl b/civicrm/templates/CRM/Contact/Page/Inline/IM.tpl
index 120589befe4f6340ee0819ef4137c40c10b05a03..1dc0675caf2bd0e5cd8750195e9b2939935a9f27 100644
--- a/civicrm/templates/CRM/Contact/Page/Inline/IM.tpl
+++ b/civicrm/templates/CRM/Contact/Page/Inline/IM.tpl
@@ -12,7 +12,7 @@
   <div class="crm-clear crm-inline-block-content" {if $permission EQ 'edit'}title="{ts}Add or edit IM{/ts}"{/if}>
     {if $permission EQ 'edit'}
       <div class="crm-edit-help">
-        <span class="crm-i fa-pencil"></span> {if empty($im)}{ts}Add IM{/ts}{else}{ts}Add or edit IM{/ts}{/if}
+        <span class="crm-i fa-pencil" aria-hidden="true"></span> {if empty($im)}{ts}Add IM{/ts}{else}{ts}Add or edit IM{/ts}{/if}
       </div>
     {/if}
     {if empty($im)}
diff --git a/civicrm/templates/CRM/Contact/Page/Inline/OpenID.tpl b/civicrm/templates/CRM/Contact/Page/Inline/OpenID.tpl
index 9fa4ead72e8b768316e8aa34e37c3ee7186e413f..2964d8c46f3aa19ebaac1c9e613b29ade367cefa 100644
--- a/civicrm/templates/CRM/Contact/Page/Inline/OpenID.tpl
+++ b/civicrm/templates/CRM/Contact/Page/Inline/OpenID.tpl
@@ -12,7 +12,7 @@
   <div class="crm-clear crm-inline-block-content" {if $permission EQ 'edit'}title="{ts}Add or edit OpenID{/ts}"{/if}>
     {if $permission EQ 'edit'}
       <div class="crm-edit-help">
-        <span class="crm-i fa-pencil"></span> {if empty($openid)}{ts}Add OpenID{/ts}{else}{ts}Add or edit OpenID{/ts}{/if}
+        <span class="crm-i fa-pencil" aria-hidden="true"></span> {if empty($openid)}{ts}Add OpenID{/ts}{else}{ts}Add or edit OpenID{/ts}{/if}
       </div>
     {/if}
     {if empty($openid)}
diff --git a/civicrm/templates/CRM/Contact/Page/Inline/Phone.tpl b/civicrm/templates/CRM/Contact/Page/Inline/Phone.tpl
index 2b788cb1772160b9dcf988a4d1a7d5b452e083dd..953fa202e42ebd9a97680388b324f76e92fb7477 100644
--- a/civicrm/templates/CRM/Contact/Page/Inline/Phone.tpl
+++ b/civicrm/templates/CRM/Contact/Page/Inline/Phone.tpl
@@ -12,14 +12,15 @@
   <div class="crm-clear crm-inline-block-content" {if $permission EQ 'edit'}title="{ts}Add or edit phone{/ts}"{/if}>
     {if $permission EQ 'edit'}
       <div class="crm-edit-help">
-        <span class="crm-i fa-pencil"></span> {if empty($phone)}{ts}Add phone{/ts}{else}{ts}Add or edit phone{/ts}{/if}
+        <span class="crm-i fa-pencil" aria-hidden="true"></span> {if empty($phone)}{ts}Add phone{/ts}{else}{ts}Add or edit phone{/ts}{/if}
       </div>
     {/if}
     {if empty($phone)}
       <div class="crm-summary-row">
         <div class="crm-label">
           {ts}Phone{/ts}
-          {if $privacy.do_not_phone}<span class="icon privacy-flag do-not-phone" title="{ts}Privacy flag: Do Not Phone{/ts}"></span>{/if}
+          {privacyFlag field=do_not_sms condition=$privacy.do_not_sms}
+          {privacyFlag field=do_not_phone condition=$privacy.do_not_phone}
         </div>
         <div class="crm-content"></div>
       </div>
@@ -28,7 +29,8 @@
       {if $item.phone || $item.phone_ext}
         <div class="crm-summary-row {if $item.is_primary eq 1}primary{/if}">
           <div class="crm-label">
-            {if $privacy.do_not_phone}<span class="icon privacy-flag do-not-phone" title="{ts}Privacy flag: Do Not Phone{/ts}"></span>{/if}
+            {privacyFlag field=do_not_sms condition=$privacy.do_not_sms}
+            {privacyFlag field=do_not_phone condition=$privacy.do_not_phone}
             {$item.location_type} {$item.phone_type}
           </div>
           <div class="crm-content crm-contact_phone">
diff --git a/civicrm/templates/CRM/Contact/Page/Inline/Website.tpl b/civicrm/templates/CRM/Contact/Page/Inline/Website.tpl
index 79e1b75819c962f71177b335e6cf0f1db9f4b4b0..d509655b4d95f70bb82c071eb29d1f76ec32e67a 100644
--- a/civicrm/templates/CRM/Contact/Page/Inline/Website.tpl
+++ b/civicrm/templates/CRM/Contact/Page/Inline/Website.tpl
@@ -12,7 +12,7 @@
   <div class="crm-clear crm-inline-block-content" {if $permission EQ 'edit'}title="{ts}Add or edit website{/ts}"{/if}>
     {if $permission EQ 'edit'}
       <div class="crm-edit-help">
-        <span class="crm-i fa-pencil"></span> {if empty($website)}{ts}Add website{/ts}{else}{ts}Add or edit website{/ts}{/if}
+        <span class="crm-i fa-pencil" aria-hidden="true"></span> {if empty($website)}{ts}Add website{/ts}{else}{ts}Add or edit website{/ts}{/if}
       </div>
     {/if}
     {if empty($website)}
diff --git a/civicrm/templates/CRM/Contact/Page/View/CustomDataFieldView.tpl b/civicrm/templates/CRM/Contact/Page/View/CustomDataFieldView.tpl
index 50cf674c889870f585fdc936a8be458aa93b32b0..e9042f21574dd47b3cc024d664c8903742769796 100644
--- a/civicrm/templates/CRM/Contact/Page/View/CustomDataFieldView.tpl
+++ b/civicrm/templates/CRM/Contact/Page/View/CustomDataFieldView.tpl
@@ -11,7 +11,7 @@
   <div class="crm-clear crm-inline-block-content" {if $permission EQ 'edit'}title="{ts}Edit{/ts}"{/if}>
     {if $permission EQ 'edit'}
       <div class="crm-edit-help">
-        <span class="crm-i fa-pencil"></span> {ts}Edit{/ts}
+        <span class="crm-i fa-pencil" aria-hidden="true"></span> {ts}Edit{/ts}
       </div>
     {/if}
 
diff --git a/civicrm/templates/CRM/Contact/Page/View/Note.tpl b/civicrm/templates/CRM/Contact/Page/View/Note.tpl
index 5ff7c4c761fc22e1456229d96dc3aabf44a8a0dd..6e8c11ab7f8a34cdee08f591558f9134341cff6d 100644
--- a/civicrm/templates/CRM/Contact/Page/View/Note.tpl
+++ b/civicrm/templates/CRM/Contact/Page/View/Note.tpl
@@ -84,7 +84,7 @@
 
 {if ($permission EQ 'edit' OR $canAddNotes) AND ($action eq 16)}
    <div class="action-link">
-   <a accesskey="N" href="{crmURL p='civicrm/contact/view/note' q="cid=`$contactId`&action=add"}" class="button medium-popup"><span><i class="crm-i fa-comment"></i> {ts}Add Note{/ts}</span></a>
+   <a accesskey="N" href="{crmURL p='civicrm/contact/view/note' q="cid=`$contactId`&action=add"}" class="button medium-popup"><span><i class="crm-i fa-comment" aria-hidden="true"></i> {ts}Add Note{/ts}</span></a>
    </div>
    <div class="clear"></div>
 {/if}
@@ -204,13 +204,13 @@
             <td class="crm-note-comment">
                 {if $note.comment_count}
                     <span id="{$note.id}_show" style="display:block" class="icon_comments_show">
-                        <a href="#" onclick="showHideComments({$note.id}); return false;" title="{ts}Show comments for this note.{/ts}"><i class="crm-i fa-caret-right"></i></span></a>
+                        <a href="#" onclick="showHideComments({$note.id}); return false;" title="{ts}Show comments for this note.{/ts}"><i class="crm-i fa-caret-right" aria-hidden="true"></i></span></a>
                     </span>
                     <span id="{$note.id}_hide" style="display:none" class="icon_comments_hide">
-                        <a href="#" onclick="showHideComments({$note.id}); return false;" title="{ts}Hide comments for this note.{/ts}"><i class="crm-i fa-caret-down"></i></span></a>
+                        <a href="#" onclick="showHideComments({$note.id}); return false;" title="{ts}Hide comments for this note.{/ts}"><i class="crm-i fa-caret-down" aria-hidden="true"></i></span></a>
                     </span>
                 {else}
-                    <span class="crm-i fa-caret-right" id="{$note.id}_hide" style="display:none"></span>
+                    <span class="crm-i fa-caret-right" id="{$note.id}_hide" style="display:none" aria-hidden="true"></span>
                 {/if}
             </td>
             <td class="crm-note-note">
diff --git a/civicrm/templates/CRM/Contact/Page/View/RelationshipPerm.tpl b/civicrm/templates/CRM/Contact/Page/View/RelationshipPerm.tpl
index bf582d84c5cac82050e22b3e00b7563fe4b6d952..709b0265c84f7757e8083d24dc173c2477f70bba 100644
--- a/civicrm/templates/CRM/Contact/Page/View/RelationshipPerm.tpl
+++ b/civicrm/templates/CRM/Contact/Page/View/RelationshipPerm.tpl
@@ -24,9 +24,12 @@
 {/if}
 
 <span class="fa-stack" title="{$permText}">
-  <i class="crm-i fa-square fa-stack-2x {if $permType eq 1}crm-i-blue{else}crm-i-green{/if}"></i>
-  <i class="crm-i {if $permType eq 1}fa-pencil{else}fa-eye{/if} fa-inverse fa-stack-1x"></i>
+  <i class="crm-i fa-square fa-stack-2x {if $permType eq 1}crm-i-blue{else}crm-i-green{/if}" aria-hidden="true"></i>
+  <i class="crm-i {if $permType eq 1}fa-pencil{else}fa-eye{/if} fa-inverse fa-stack-1x" aria-hidden="true"></i>
 </span>
+{if !$displayText}
+<span class="sr-only">{$permText}</span>
+{/if}
 
 {* Used for viewing a relationship *}
 {if $displayText}
diff --git a/civicrm/templates/CRM/Contact/Page/View/Summary.tpl b/civicrm/templates/CRM/Contact/Page/View/Summary.tpl
index 2fd815c233d311b7fd59f787eeb49d6fe0d5b9a6..bad21b98b2204fae74bf6e9be0858e378e12a431 100644
--- a/civicrm/templates/CRM/Contact/Page/View/Summary.tpl
+++ b/civicrm/templates/CRM/Contact/Page/View/Summary.tpl
@@ -110,7 +110,7 @@
         {foreach from=$allTabs key=tabName item=tabValue}
           <li id="tab_{$tabValue.id}" class="crm-tab-button ui-corner-all crm-count-{$tabValue.count}{if isset($tabValue.class)} {$tabValue.class}{/if}">
             <a href="{$tabValue.url}" title="{$tabValue.title|escape}">
-              <i class="{if $tabValue.icon}{$tabValue.icon}{else}crm-i fa-puzzle-piece{/if}"></i>
+              <i class="{if $tabValue.icon}{$tabValue.icon}{else}crm-i fa-puzzle-piece{/if}" aria-hidden="true"></i>
               <span>{$tabValue.title}</span>
               {if empty($tabValue.hideCount)}<em>{$tabValue.count}</em>{/if}
             </a>
diff --git a/civicrm/templates/CRM/Contribute/Form/AdditionalPayment.tpl b/civicrm/templates/CRM/Contribute/Form/AdditionalPayment.tpl
index 3fd811d48c35958dfbce09af5c360030eed769be..d3403fae9bb93fe6002718d40a8cd4dad041f9af 100644
--- a/civicrm/templates/CRM/Contribute/Form/AdditionalPayment.tpl
+++ b/civicrm/templates/CRM/Contribute/Form/AdditionalPayment.tpl
@@ -29,7 +29,7 @@
     {/if}
     {if $paymentType eq 'owed'}
       <div class="action-link css_right crm-link-credit-card-mode">
-        <a class="open-inline-noreturn action-item crm-hover-button" href="{$ccModeLink}">&raquo; {ts}submit credit card payment{/ts}</a>
+        <a class="open-inline-noreturn action-item crm-hover-button" href="{$ccModeLink}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}submit credit card payment{/ts}</a>
       </div>
     {/if}
   {/if}
diff --git a/civicrm/templates/CRM/Contribute/Form/CancelSubscription.tpl b/civicrm/templates/CRM/Contribute/Form/CancelSubscription.tpl
index 74e5e2f186621f32b9a11c0bd64c901ccec90dea..c193513b621c196485a25791a05b835b89032e9b 100644
--- a/civicrm/templates/CRM/Contribute/Form/CancelSubscription.tpl
+++ b/civicrm/templates/CRM/Contribute/Form/CancelSubscription.tpl
@@ -12,7 +12,7 @@
 <div class="help">
   <div class="icon inform-icon"></div>&nbsp;
   {$cancelRecurDetailText}
-  {if !$cancelSupported}
+  {if $cancelRecurNotSupportedText}
     <div class="status-warning">{$cancelRecurNotSupportedText}</div>
   {/if}
 </div>
diff --git a/civicrm/templates/CRM/Contribute/Form/Contribution.tpl b/civicrm/templates/CRM/Contribute/Form/Contribution.tpl
index c4c8815b5f7b19635370008daf90cd79ec6db1b9..0df23f41bd89addd667a48563990654425edaf8e 100644
--- a/civicrm/templates/CRM/Contribute/Form/Contribution.tpl
+++ b/civicrm/templates/CRM/Contribute/Form/Contribution.tpl
@@ -50,7 +50,7 @@
       {else}
         {capture assign=ccModeLink}{crmURL p='civicrm/contact/view/contribution' q="reset=1&action=add&context=standalone&mode=live"}{/capture}
       {/if}
-     <a class="open-inline-noreturn action-item crm-hover-button" href="{$ccModeLink}">&raquo; {ts}submit credit card contribution{/ts}</a>
+     <a class="open-inline-noreturn action-item crm-hover-button" href="{$ccModeLink}"><i class="crm-i fa-credit-card" aria-hidden="true"></i> {ts}submit credit card contribution{/ts}</a>
     </div>
     {/if}
   <div class="crm-submit-buttons">
@@ -148,7 +148,7 @@
         <td>
         {if !$isUsePaymentBlock && $contactId && $contribID && $contributionMode EQ null && $contribution_status_id eq 2}
           {capture assign=payNowLink}{crmURL p='civicrm/contact/view/contribution' q="reset=1&action=update&id=`$contribID`&cid=`$contactId`&context=`$context`&mode=live"}{/capture}
-          <a class="open-inline action-item crm-hover-button" href="{$payNowLink}">&raquo; {ts}Pay with Credit Card{/ts}</a>
+          <a class="open-inline action-item crm-hover-button" href="{$payNowLink}"><i class="crm-i fa-credit-card" aria-hidden="true"></i> {ts}Pay with Credit Card{/ts}</a>
         {/if}
       </td>
       </tr>
@@ -549,14 +549,14 @@ function adjustPayment( ) {
 }
 
 {/literal}{if $processorSupportsFutureStartDate}{literal}
-cj ('input:radio[name="is_recur"]').click( function( ) {
+cj ('#is_recur').click( function( ) {
   showStartDate( );
 });
 
 showStartDate( );
 
 function showStartDate( ) {
-  if (cj( 'input:radio[name="is_recur"]:checked').val( ) == 0 ) {
+  if (!cj('#is_recur').is(':checked')) {
     cj('#start_date').hide( );
   }
   else {
diff --git a/civicrm/templates/CRM/Contribute/Form/Contribution/PreviewHeader.tpl b/civicrm/templates/CRM/Contribute/Form/Contribution/PreviewHeader.tpl
index 48fd5158f5e0509a9b0a70b5dfb50f546e404f16..fe160753f41829d311d77a9eef53d7edea0d9f15 100644
--- a/civicrm/templates/CRM/Contribute/Form/Contribution/PreviewHeader.tpl
+++ b/civicrm/templates/CRM/Contribute/Form/Contribution/PreviewHeader.tpl
@@ -9,7 +9,7 @@
 *}
 {* Displays Test-drive mode header for Contribution pages. *}
 <div class="messages status no-popup">
-    <i class="crm-i fa-cogs"></i>
+    <i class="crm-i fa-cogs" aria-hidden="true"></i>
     <strong>{ts}Test-drive Your Contribution Page{/ts}</strong>
     <p>{ts}This page is currently running in <strong>test-drive mode</strong>. Transactions will be sent to your payment processor's test server. <strong>No live financial transactions will be submitted. However, a contact record will be created or updated and a test contribution record will be saved to the database. Use obvious test contact names so you can review and delete these records as needed. Test contributions are not visible on the Contributions tab, but can be viewed by searching for 'Test Contributions' in the CiviContribute search form.</strong> Refer to your payment processor's documentation for information on values to use for test credit card number, security code, postal code, etc.{/ts}</p>
 </div>
diff --git a/civicrm/templates/CRM/Contribute/Form/Contribution/ThankYou.tpl b/civicrm/templates/CRM/Contribute/Form/Contribution/ThankYou.tpl
index 020ba6265d001133722ba7383a91dff68824dc04..bc238abd4966bc02223c0a3a4f0f1cac55106ff9 100644
--- a/civicrm/templates/CRM/Contribute/Form/Contribution/ThankYou.tpl
+++ b/civicrm/templates/CRM/Contribute/Form/Contribution/ThankYou.tpl
@@ -23,13 +23,13 @@
   {* Show link to Tell a Friend (CRM-2153) *}
   {if $friendText}
     <div id="tell-a-friend" class="crm-section friend_link-section">
-      <a href="{$friendURL}" title="{$friendText|escape:'html'}" class="button"><span>&raquo; {$friendText}</span></a>
+      <a href="{$friendURL}" title="{$friendText|escape:'html'}" class="button"><span><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {$friendText}</span></a>
     </div>{if !$linkText}<br /><br />{/if}
   {/if}
   {* Add button for donor to create their own Personal Campaign page *}
   {if $linkText}
     <div class="crm-section create_pcp_link-section">
-      <a href="{$linkTextUrl}" title="{$linkText|escape:'html'}" class="button"><span>&raquo; {$linkText}</span></a>
+      <a href="{$linkTextUrl}" title="{$linkText|escape:'html'}" class="button"><span><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {$linkText}</span></a>
     </div><br /><br />
   {/if}
 
diff --git a/civicrm/templates/CRM/Contribute/Form/ContributionPage/Tab.hlp b/civicrm/templates/CRM/Contribute/Form/ContributionPage/Tab.hlp
index 0da4ccdfbc4fe887001c28408c02f36d8644b604..74944271f70cb85aaacfebba1dbc6834f2e18205 100644
--- a/civicrm/templates/CRM/Contribute/Form/ContributionPage/Tab.hlp
+++ b/civicrm/templates/CRM/Contribute/Form/ContributionPage/Tab.hlp
@@ -13,49 +13,49 @@
 {htxt id="id-configure-contrib-pages"}
 <table>
 <tr>
-    <td><a href="{crmURL p='civicrm/admin/contribute/settings' q="reset=1&action=update&id=`$contributionPageID`"}" id="idContribPageSettings">&raquo; {ts}Title and Settings{/ts}</a></td>
+    <td><a href="{crmURL p='civicrm/admin/contribute/settings' q="reset=1&action=update&id=`$contributionPageID`"}" id="idContribPageSettings"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Title and Settings{/ts}</a></td>
     <td>{ts}Use this form to edit the page title, financial type (e.g. donation, campaign contribution, etc.), goal amount, introduction, and status (active/inactive) for this online contribution page.{/ts}</td>
 </tr>
 <tr>
-    <td><a href="{crmURL p='civicrm/admin/contribute/amount' q="reset=1&action=update&id=`$contributionPageID`"}" id="idAmount">&raquo; {ts}Amount{/ts}</a></td>
+    <td><a href="{crmURL p='civicrm/admin/contribute/amount' q="reset=1&action=update&id=`$contributionPageID`"}" id="idAmount"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Amount{/ts}</a></td>
     <td>{ts}Use this form to configure Contribution Amount options. You can give contributors the ability to enter their own contribution amounts and/or provide a fixed list of amounts. For fixed amounts, you can enter a label for each 'level' of contribution (e.g. Friend, Sustainer, etc.). If you allow people to enter their own dollar amounts, you can also set minimum and maximum values. Depending on your choice of Payment Processor, you may be able to offer a recurring contribution option.{/ts}</td>
 </tr>
 <tr>
-    <td><a href="{crmURL p='civicrm/admin/contribute/membership' q="reset=1&action=update&id=`$contributionPageID`"}" id="idMembership">&raquo; {ts}Memberships{/ts}</a></td>
+    <td><a href="{crmURL p='civicrm/admin/contribute/membership' q="reset=1&action=update&id=`$contributionPageID`"}" id="idMembership"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Memberships{/ts}</a></td>
      <td>{ts}Use this form to enable and configure a Membership Signup and Renewal section for this Online Contribution Page. If you're not using this page for membership signup, leave the Enabled box un-checked.{/ts}</td>
 </tr>
 <tr>
-    <td><a href="{crmURL p='civicrm/admin/contribute/thankYou' q="reset=1&action=update&id=`$contributionPageID`"}" id="idThanks">&raquo; {ts}Thank-you and Receipting{/ts}</a></td>
+    <td><a href="{crmURL p='civicrm/admin/contribute/thankYou' q="reset=1&action=update&id=`$contributionPageID`"}" id="idThanks"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Thank-you and Receipting{/ts}</a></td>
     <td>{ts}Use this form to configure the thank-you message and receipting options. Contributors will see a confirmation and thank-you page after whenever an online contribution is successfully processed. You provide the content and layout of the thank-you section below. You also control whether an electronic receipt is automatically emailed to each contributor, and you can add a custom message to that receipt.{/ts}</td>
 </tr>
 <tr>
-    <td><a href="{crmURL p='civicrm/admin/contribute/friend' q="reset=1&action=update&id=`$contributionPageID`"}" id="idFriend">&raquo; {ts}Tell a Friend{/ts}</a></td>
+    <td><a href="{crmURL p='civicrm/admin/contribute/friend' q="reset=1&action=update&id=`$contributionPageID`"}" id="idFriend"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Tell a Friend{/ts}</a></td>
     <td>{ts}Tell a Friend gives your contributors an easy way to spread the word about this fundraising campaign. The contribution thank-you page will include a link to a form where they can enter their friends' email addresses, along with a personalized message. CiviCRM will record these solicitation activities, and will add the friends to your database.{/ts}</td>
 </tr>
 <tr>
-   <td><a href="{crmURL p='civicrm/admin/contribute/custom' q="reset=1&action=update&id=`$contributionPageID`"}" id="idCustom">&raquo; {ts}Include Profiles{/ts}</a></td>
+   <td><a href="{crmURL p='civicrm/admin/contribute/custom' q="reset=1&action=update&id=`$contributionPageID`"}" id="idCustom"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Include Profiles{/ts}</a></td>
     <td>{ts}You may want to collect information from contributors beyond what is required to make a contribution. For example, you may want to inquire about volunteer availability and skills. Add any number of fields to your contribution form by selecting CiviCRM Profiles (collections of fields) to include at the beginning of the page, and/or at the bottom.{/ts}<br />
 {capture assign=adminGroupURL}{crmURL p="civicrm/admin/uf/group" q="reset=1&action=browse"}{/capture}
 {ts 1=$adminGroupURL}You can use existing CiviCRM Profiles on your page or create profile(s) specifically for use in Online Contribution pages. Go to <a href="%1"><strong>Administer CiviCRM Profiles</strong></a> if you need to review, modify or create profiles (you can come back at any time to select or update the Profile(s) used for this page).{/ts}</td>
 </tr>
 <tr>
-   <td><a href="{crmURL p='civicrm/admin/contribute/premium q="reset=1&action=update&id=`$contributionPageID`"}" id="idPremium">&raquo; {ts}Premiums{/ts}</a></td>
+   <td><a href="{crmURL p='civicrm/admin/contribute/premium q="reset=1&action=update&id=`$contributionPageID`"}" id="idPremium"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Premiums{/ts}</a></td>
     <td>{ts}Enable the Premiums section for this Online Contribution Page, and customize the title and introductory message (e.g ...in appreciation of your support, you will be able to select from a number of exciting thank-you gifts...). You can optionally provide a contact email address and/or phone number for inquiries. Then select and review the premiums that you want to offer on this contribution page.{/ts}</td>
 </tr>
 <tr>
-   <td><a href="{crmURL p='civicrm/admin/contribute/widget' q="reset=1&action=update&id=`$contributionPageID`"}" id="idWidget">&raquo; {ts}Widget{/ts}</a></td>
+   <td><a href="{crmURL p='civicrm/admin/contribute/widget' q="reset=1&action=update&id=`$contributionPageID`"}" id="idWidget"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Widget{/ts}</a></td>
    <td>{ts}Widgets allow you and your supporters to easily promote this fund-raising campaign. Widget code can be added to any web page.  They will provide a real-time display of current contribution results, and a direct link to this contribution page.{/ts}</td>
 </tr>
 <tr>
-  <td><a href="{crmURL p='civicrm/admin/contribute/pcp' q="reset=1&action=update&id=`$contributionPageID`"}" id="idPCP">&raquo; {ts}Personal Campaign Pages{/ts}</a></td>
+  <td><a href="{crmURL p='civicrm/admin/contribute/pcp' q="reset=1&action=update&id=`$contributionPageID`"}" id="idPCP"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Personal Campaign Pages{/ts}</a></td>
   <td>{ts}Allow constituents to create their own personal fundraising pages linked to this contribution page.{/ts}</td>
 </tr>
 <tr>
-  <td><a href="{crmURL p='civicrm/contribute/transact' q="reset=1&action=preview&id=`$contributionPageID`"}" id="idTest">&raquo; {ts}Online Contribution{/ts}</a><br /> ({ts}Test-drive{/ts})</td>
+  <td><a href="{crmURL p='civicrm/contribute/transact' q="reset=1&action=preview&id=`$contributionPageID`"}" id="idTest"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Online Contribution{/ts}</a><br /> ({ts}Test-drive{/ts})</td>
   <td>{ts}Test-drive the entire contribution process&mdash;including custom fields, confirmation, thank-you page, and receipting. Transactions will be directed to your payment processor's test server. No live financial transactions will be submitted. However, a contact record will be created or updated and a test contribution record will be saved to the database. Use obvious test contact names so you can review and delete these records as needed. Test contributions are not visible on the Contributions tab, but can be viewed by searching for 'Test Contributions' in the CiviContribute search form.{/ts}</td>
 </tr>
 <tr>
-  <td><a href="{crmURL p='civicrm/contribute/transact' q="reset=1&id=`$contributionPageID`"}" id="idLive">&raquo; {ts}Online Contribution{/ts}</a><br /> ({ts}Live{/ts})</td>
+  <td><a href="{crmURL p='civicrm/contribute/transact' q="reset=1&id=`$contributionPageID`"}" id="idLive"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Online Contribution{/ts}</a><br /> ({ts}Live{/ts})</td>
   <td>{ts}Review your customized <strong>LIVE</strong> online contribution page here. Use the following URL in links and buttons on any website to send visitors to this live page:{/ts}<br />
       <strong>{crmURL a=1 fe=1 p="civicrm/contribute/transact" q="reset=1&id=`$contributionPageID`"}</strong></td>
 </tr>
diff --git a/civicrm/templates/CRM/Contribute/Form/ContributionPage/Widget.tpl b/civicrm/templates/CRM/Contribute/Form/ContributionPage/Widget.tpl
index 0d47f6ee58e8b146eafb2e77ca2defe887873c9a..2f8eb100310c18730d1a6586fed2653094a59261 100644
--- a/civicrm/templates/CRM/Contribute/Form/ContributionPage/Widget.tpl
+++ b/civicrm/templates/CRM/Contribute/Form/ContributionPage/Widget.tpl
@@ -60,7 +60,7 @@
                     </div>
                     <textarea rows="8" cols="50" name="widget_code" id="widget_code">{include file="CRM/Contribute/Page/Widget.tpl" widgetId=$widget_id cpageId=$cpageId}</textarea>
                     <br />
-                    <strong><a href="#" onclick="Widget.widget_code.select(); return false;">&raquo; {ts}Select Code{/ts}</a></strong>
+                    <strong><a href="#" onclick="Widget.widget_code.select(); return false;"><i class="crm-i fa-code" aria-hidden="true"></i> {ts}Select Code{/ts}</a></strong>
                 {else}
                     <div class="description">
                         {ts}The code for adding this widget to web pages will be displayed here after you click <strong>Save and Preview</strong>.{/ts}
diff --git a/civicrm/templates/CRM/Contribute/Form/ContributionView.tpl b/civicrm/templates/CRM/Contribute/Form/ContributionView.tpl
index 439d97e6601cc54a805f0eaa807fbce723879a0a..c7e0c8fe9ce7e3d78803dda53aef8e06243bb12f 100644
--- a/civicrm/templates/CRM/Contribute/Form/ContributionView.tpl
+++ b/civicrm/templates/CRM/Contribute/Form/ContributionView.tpl
@@ -17,10 +17,10 @@
         {assign var='urlParams' value="reset=1&id=$id&cid=$contact_id&action=update&context=$context&key=$searchKey"}
       {/if}
       <a class="button" href="{crmURL p='civicrm/contact/view/contribution' q=$urlParams}" accesskey="e"><span>
-          <i class="crm-i fa-pencil"></i> {ts}Edit{/ts}</span>
+          <i class="crm-i fa-pencil" aria-hidden="true"></i> {ts}Edit{/ts}</span>
       </a>
       {if $paymentButtonName}
-        <a class="button" href='{crmURL p="civicrm/payment" q="action=add&reset=1&component=`$component`&id=`$id`&cid=`$contact_id`"}'><i class="crm-i fa-plus-circle"></i> {ts}{$paymentButtonName}{/ts}</a>
+        <a class="button" href='{crmURL p="civicrm/payment" q="action=add&reset=1&component=`$component`&id=`$id`&cid=`$contact_id`"}'><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}{$paymentButtonName}{/ts}</a>
       {/if}
     {/if}
     {if (call_user_func(array('CRM_Core_Permission','check'), 'delete in CiviContribute') && call_user_func(array('CRM_Core_Permission', 'check'), "delete contributions of type $financial_type") && $canDelete)     || (call_user_func(array('CRM_Core_Permission','check'), 'delete in CiviContribute') && $noACL)}
@@ -29,7 +29,7 @@
         {assign var='urlParams' value="reset=1&id=$id&cid=$contact_id&action=delete&context=$context&key=$searchKey"}
       {/if}
       <a class="button" href="{crmURL p='civicrm/contact/view/contribution' q=$urlParams}"><span>
-          <i class="crm-i fa-trash"></i> {ts}Delete{/ts}</span>
+          <i class="crm-i fa-trash" aria-hidden="true"></i> {ts}Delete{/ts}</span>
       </a>
     {/if}
     {include file="CRM/common/formButtons.tpl" location="top"}
@@ -38,14 +38,14 @@
     {if $invoicing}
       <div class="css_right">
         <a class="button no-popup" href="{crmURL p='civicrm/contribute/invoice' q=$pdfUrlParams}">
-          <i class="crm-i fa-print"></i>
+          <i class="crm-i fa-print" aria-hidden="true"></i>
         {if $contribution_status != 'Refunded' && $contribution_status != 'Cancelled' }
           {ts}Print Invoice{/ts}</a>
         {else}
           {ts}Print Invoice and Credit Note{/ts}</a>
         {/if}
         <a class="button" href="{crmURL p='civicrm/contribute/invoice/email' q=$emailUrlParams}">
-          <i class="crm-i fa-paper-plane"></i>
+          <i class="crm-i fa-paper-plane" aria-hidden="true"></i>
           {ts}Email Invoice{/ts}</a>
       </div>
     {/if}
@@ -341,9 +341,9 @@
     {if ( $context eq 'fulltext' || $context eq 'search' ) && $searchKey}
       {assign var='urlParams' value="reset=1&id=$id&cid=$contact_id&action=update&context=$context&key=$searchKey"}
     {/if}
-    <a class="button" href="{crmURL p='civicrm/contact/view/contribution' q=$urlParams}" accesskey="e"><span><i class="crm-i fa-pencil"></i> {ts}Edit{/ts}</span></a>
+    <a class="button" href="{crmURL p='civicrm/contact/view/contribution' q=$urlParams}" accesskey="e"><span><i class="crm-i fa-pencil" aria-hidden="true"></i> {ts}Edit{/ts}</span></a>
     {if $paymentButtonName}
-      <a class="button" href='{crmURL p="civicrm/payment" q="action=add&reset=1&component=`$component`&id=`$id`&cid=`$contact_id`"}'><i class="crm-i fa-plus-circle"></i> {ts}{$paymentButtonName}{/ts}</a>
+      <a class="button" href='{crmURL p="civicrm/payment" q="action=add&reset=1&component=`$component`&id=`$id`&cid=`$contact_id`"}'><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}{$paymentButtonName}{/ts}</a>
     {/if}
   {/if}
   {if (call_user_func(array('CRM_Core_Permission','check'), 'delete in CiviContribute') && call_user_func(array('CRM_Core_Permission', 'check'), "delete contributions of type $financial_type") && $canDelete)     || (call_user_func(array('CRM_Core_Permission','check'), 'delete in CiviContribute') && $noACL)}
@@ -351,7 +351,7 @@
     {if ( $context eq 'fulltext' || $context eq 'search' ) && $searchKey}
       {assign var='urlParams' value="reset=1&id=$id&cid=$contact_id&action=delete&context=$context&key=$searchKey"}
     {/if}
-    <a class="button" href="{crmURL p='civicrm/contact/view/contribution' q=$urlParams}"><span><i class="crm-i fa-trash"></i> {ts}Delete{/ts}</span></a>
+    <a class="button" href="{crmURL p='civicrm/contact/view/contribution' q=$urlParams}"><span><i class="crm-i fa-trash" aria-hidden="true"></i> {ts}Delete{/ts}</span></a>
   {/if}
   {include file="CRM/common/formButtons.tpl" location="bottom"}
 </div>
diff --git a/civicrm/templates/CRM/Contribute/Form/PCP/PCPAccount.tpl b/civicrm/templates/CRM/Contribute/Form/PCP/PCPAccount.tpl
index 310a064a56afdf173457e3f981e187413015ed28..9914571235267966db33a940d64d8deaeedae7a8 100644
--- a/civicrm/templates/CRM/Contribute/Form/PCP/PCPAccount.tpl
+++ b/civicrm/templates/CRM/Contribute/Form/PCP/PCPAccount.tpl
@@ -16,7 +16,7 @@
 
 {if $profileDisplay}
 <div class="messages status no-popup">
-  <i class="crm-i fa-exclamation-triangle"></i>
+  <i class="crm-i fa-exclamation-triangle" aria-hidden="true"></i>
   <strong>{ts}Profile is not configured with Email address.{/ts}</strong>
 </div>
 {else}
diff --git a/civicrm/templates/CRM/Contribute/Form/PaymentInfoBlock.tpl b/civicrm/templates/CRM/Contribute/Form/PaymentInfoBlock.tpl
index afcc8c3c608d73b099ddc953764a949fba0e0351..bcb23f9a56fd7fce8d1a16e44ab3f1dcef3afafc 100644
--- a/civicrm/templates/CRM/Contribute/Form/PaymentInfoBlock.tpl
+++ b/civicrm/templates/CRM/Contribute/Form/PaymentInfoBlock.tpl
@@ -41,7 +41,7 @@
 {/if}
 
   {foreach from=$paymentLinks item=paymentLink}
-    <a class="open-inline action-item crm-hover-button" href="{$paymentLink.url}">&raquo; {ts}{$paymentLink.title}{/ts}</a>
+    <a class="open-inline action-item crm-hover-button" href="{$paymentLink.url}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}{$paymentLink.title}{/ts}</a>
   {/foreach}
 
 {/crmRegion}
diff --git a/civicrm/templates/CRM/Contribute/Form/Search/Common.tpl b/civicrm/templates/CRM/Contribute/Form/Search/Common.tpl
index c5594dcabd76272b96a4239e9ff26f02bdfcf7ec..657fb77251ff6781a1414af5a2fa01055dd8bc88 100644
--- a/civicrm/templates/CRM/Contribute/Form/Search/Common.tpl
+++ b/civicrm/templates/CRM/Contribute/Form/Search/Common.tpl
@@ -157,6 +157,9 @@
     {$form.cancel_reason.html}
   </td>
 </tr>
+<tr>
+  <td><label>{$form.contribution_id.label}</label> {$form.contribution_id.html}</td>
+</tr>
 
 {* campaign in contribution search *}
 {include file="CRM/Campaign/Form/addCampaignToComponent.tpl" campaignContext="componentSearch"
diff --git a/civicrm/templates/CRM/Contribute/Form/SoftCredit.tpl b/civicrm/templates/CRM/Contribute/Form/SoftCredit.tpl
index 817ad26bc09d88ac71637c07d34767c1fc0d96c3..824ed7c0e54e6d49943782532b9b4e8527094ddb 100644
--- a/civicrm/templates/CRM/Contribute/Form/SoftCredit.tpl
+++ b/civicrm/templates/CRM/Contribute/Form/SoftCredit.tpl
@@ -28,7 +28,7 @@
   {/section}
   <tr>
     <td>
-      <a href="#" class="crm-hover-button" id="addMoreSoftCredit"><i class="crm-i fa-plus-circle"></i> {ts}another soft credit{/ts}</a>
+      <a href="#" class="crm-hover-button" id="addMoreSoftCredit"><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}another soft credit{/ts}</a>
     </td>
   </tr>
 </table>
diff --git a/civicrm/templates/CRM/Contribute/Form/Task/Batch.tpl b/civicrm/templates/CRM/Contribute/Form/Task/Batch.tpl
index 2a5c48cb2ca144620c9cce5dbc3ad3300fe7be20..23effd2f50d4b17903c39b5362faa71f8821a4e4 100644
--- a/civicrm/templates/CRM/Contribute/Form/Task/Batch.tpl
+++ b/civicrm/templates/CRM/Contribute/Form/Task/Batch.tpl
@@ -20,7 +20,7 @@
             {/foreach}
 
              {foreach from=$fields item=field key=fieldName}
-                <td><img  src="{$config->resourceBase}i/copy.png" alt="{ts 1=$field.title}Click to copy %1 from row one to all rows.{/ts}" fname="{$field.name}" class="action-icon" title="{ts}Click here to copy the value in row one to ALL rows.{/ts}" />{$field.title}</td>
+                <td>{copyIcon name=$field.name title=$field.title}{$field.title}</td>
              {/foreach}
             </tr>
           </thead>
diff --git a/civicrm/templates/CRM/Contribute/Page/ContributionPage.tpl b/civicrm/templates/CRM/Contribute/Page/ContributionPage.tpl
index 39ab184744dd868dd87576ac2382af3836ad0011..a4665f8026cb79b6f82f65c670ce145e7ea74d11 100644
--- a/civicrm/templates/CRM/Contribute/Page/ContributionPage.tpl
+++ b/civicrm/templates/CRM/Contribute/Page/ContributionPage.tpl
@@ -16,7 +16,7 @@
     {if NOT ($action eq 1 or $action eq 2) }
       <table class="form-layout-compressed">
       <tr>
-      <td><a href="{$newPageURL}" class="button"><span><i class="crm-i fa-plus-circle"></i> {ts}Add Contribution Page{/ts}</span></a></td>
+      <td><a href="{$newPageURL}" class="button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Add Contribution Page{/ts}</span></a></td>
             <td style="vertical-align: top"><a class="button" href="{crmURL p="civicrm/admin/pcp" q="reset=1"}"><span>{ts}Manage Personal Campaign Pages{/ts}</span></a> {help id="id-pcp-intro" file="CRM/PCP/Page/PCP.hlp"}</td>
       </tr>
       </table>
diff --git a/civicrm/templates/CRM/Contribute/Page/ContributionRecur.tpl b/civicrm/templates/CRM/Contribute/Page/ContributionRecur.tpl
index f57c33199ba38528e0fcb44f7de75f931fde306a..1c8b4dca28b9860472f6893b7c6ca9357e92e6f9 100644
--- a/civicrm/templates/CRM/Contribute/Page/ContributionRecur.tpl
+++ b/civicrm/templates/CRM/Contribute/Page/ContributionRecur.tpl
@@ -8,80 +8,73 @@
  +--------------------------------------------------------------------+
 *}
 
-{include file="CRM/common/enableDisableApi.tpl"}
+{if $recur}
+  {if $recur.is_test}
+    <div class="help">
+      <strong>{ts}This is a TEST transaction{/ts}</strong>
+    </div>
+  {/if}
+  <div class="crm-block crm-content-block crm-recurcontrib-view-block">
+    <table class="crm-info-panel">
+      <tr>
+        <td class="label">{ts}From{/ts}</td>
+        <td class="bold"><a href="{crmURL p='civicrm/contact/view' q="cid=`$recur.contact_id`"}">{$displayName}</a></td>
+      </tr>
+      <tr><td class="label">{ts}Amount{/ts}</td><td>{$recur.amount|crmMoney:$recur.currency}{if $is_test} ({ts}test{/ts}){/if}</td></tr>
+      <tr><td class="label">{ts}Frequency{/ts}</td><td>every {$recur.frequency_interval} {$recur.frequency_unit}</td></tr>
+      <tr><td class="label">{ts}Installments{/ts}</td><td>{$recur.installments}</td></tr>
+      <tr><td class="label">{ts}Status{/ts}</td><td>{$recur.contribution_status}</td></tr>
+      <tr><td class="label">{ts}Start Date{/ts}</td><td>{$recur.start_date|crmDate}</td></tr>
+      <tr><td class="label">{ts}Created Date{/ts}</td><td>{$recur.create_date|crmDate}</td></tr>
+      {if $recur.modified_date}<tr><td class="label">{ts}Modified Date{/ts}</td><td>{$recur.modified_date|crmDate}</td></tr>{/if}
+      {if $recur.cancel_date}<tr><td class="label">{ts}Cancelled Date{/ts}</td><td>{$recur.cancel_date|crmDate}</td></tr>{/if}
+      {if $recur.cancel_reason}<tr><td class="label">{ts}Cancel Reason{/ts}</td><td>{$recur.cancel_reason}</td></tr>{/if}
+      {if $recur.end_date}<tr><td class="label">{ts}End Date{/ts}</td><td>{$recur.end_date|crmDate}</td></tr>{/if}
+      {if $recur.processor_id}<tr><td class="label">{ts}Processor ID{/ts}</td><td>{$recur.processor_id}</td></tr>{/if}
+      <tr><td class="label">{ts}Transaction ID{/ts}</td><td>{$recur.trxn_id}</td></tr>
+      {if $recur.invoice_id}<tr><td class="label">{ts}Invoice ID{/ts}</td><td>{$recur.invoice_id}</td></tr>{/if}
+      <tr><td class="label">{ts}Cycle Day{/ts}</td><td>{$recur.cycle_day}</td></tr>
+      {if $recur.contribution_status_id neq 3}<tr><td class="label">{ts}Next Contribution{/ts}</td><td>{$recur.next_sched_contribution_date|crmDate}</td></tr>{/if}
+      <tr><td class="label">{ts}Failure Count{/ts}</td><td>{$recur.failure_count}</td></tr>
+      {if $recur.invoice_id}<tr><td class="label">{ts}Failure Retry Date{/ts}</td><td>{$recur.next_sched_contribution_date|crmDate}</td></tr>{/if}
+      <tr><td class="label">{ts}Auto Renew?{/ts}</td><td>{if $recur.auto_renew}{ts}Yes{/ts}{else}{ts}No{/ts}{/if}</td></tr>
+      <tr><td class="label">{ts}Send receipt for each contribution?{/ts}</td><td>{if $recur.is_email_receipt}{ts}Yes{/ts}{else}{ts}No{/ts}{/if}</td></tr>
+      {if $recur.payment_processor}<tr><td class="label">{ts}Payment Processor{/ts}</td><td>{$recur.payment_processor}</td></tr>{/if}
+      {if $recur.financial_type}<tr><td class="label">{ts}Financial Type{/ts}</td><td>{$recur.financial_type}</td></tr>{/if}
+      {if $recur.campaign}<tr><td class="label">{ts}Campaign{/ts}</td><td>{$recur.campaign}</td></tr>{/if}
+      {if $recur.membership_id}<tr>
+        <td class="label">{ts}Membership{/ts}</td>
+        <td><a class="crm-hover-button action-item" href='{crmURL p="civicrm/contact/view/membership" q="action=view&reset=1&cid=`$contactId`&id=`$recur.membership_id`&context=membership&selectedChild=member"}'>{$recur.membership_name}</a></td>
+        </tr>
+      {/if}
+      {include file="CRM/Custom/Page/CustomDataView.tpl"}
 
-{if $action eq 4} {* when action is view *}
-    {if $recur}
-        {if $recur.is_test}
-        <div class="help">
-          <strong>{ts}This is a TEST transaction{/ts}</strong>
-        </div>
-        {/if}
-        <div class="crm-block crm-content-block crm-recurcontrib-view-block">
-          <table class="crm-info-panel">
-            <tr>
-              <td class="label">{ts}From{/ts}</td>
-              <td class="bold"><a href="{crmURL p='civicrm/contact/view' q="cid=`$recur.contact_id`"}">{$displayName}</a></td>
-            </tr>
-            <tr><td class="label">{ts}Amount{/ts}</td><td>{$recur.amount|crmMoney:$recur.currency}{if $is_test} ({ts}test{/ts}){/if}</td></tr>
-            <tr><td class="label">{ts}Frequency{/ts}</td><td>every {$recur.frequency_interval} {$recur.frequency_unit}</td></tr>
-            <tr><td class="label">{ts}Installments{/ts}</td><td>{$recur.installments}</td></tr>
-            <tr><td class="label">{ts}Status{/ts}</td><td>{$recur.contribution_status}</td></tr>
-            <tr><td class="label">{ts}Start Date{/ts}</td><td>{$recur.start_date|crmDate}</td></tr>
-            <tr><td class="label">{ts}Created Date{/ts}</td><td>{$recur.create_date|crmDate}</td></tr>
-            {if $recur.modified_date}<tr><td class="label">{ts}Modified Date{/ts}</td><td>{$recur.modified_date|crmDate}</td></tr>{/if}
-            {if $recur.cancel_date}<tr><td class="label">{ts}Cancelled Date{/ts}</td><td>{$recur.cancel_date|crmDate}</td></tr>{/if}
-            {if $recur.cancel_reason}<tr><td class="label">{ts}Cancel Reason{/ts}</td><td>{$recur.cancel_reason}</td></tr>{/if}
-            {if $recur.end_date}<tr><td class="label">{ts}End Date{/ts}</td><td>{$recur.end_date|crmDate}</td></tr>{/if}
-            {if $recur.processor_id}<tr><td class="label">{ts}Processor ID{/ts}</td><td>{$recur.processor_id}</td></tr>{/if}
-            <tr><td class="label">{ts}Transaction ID{/ts}</td><td>{$recur.trxn_id}</td></tr>
-            {if $recur.invoice_id}<tr><td class="label">{ts}Invoice ID{/ts}</td><td>{$recur.invoice_id}</td></tr>{/if}
-            <tr><td class="label">{ts}Cycle Day{/ts}</td><td>{$recur.cycle_day}</td></tr>
-            {if $recur.contribution_status_id neq 3}<tr><td class="label">{ts}Next Contribution{/ts}</td><td>{$recur.next_sched_contribution_date|crmDate}</td></tr>{/if}
-            <tr><td class="label">{ts}Failure Count{/ts}</td><td>{$recur.failure_count}</td></tr>
-            {if $recur.invoice_id}<tr><td class="label">{ts}Failure Retry Date{/ts}</td><td>{$recur.next_sched_contribution_date|crmDate}</td></tr>{/if}
-            <tr><td class="label">{ts}Auto Renew?{/ts}</td><td>{if $recur.auto_renew}{ts}Yes{/ts}{else}{ts}No{/ts}{/if}</td></tr>
-            <tr><td class="label">{ts}Send receipt for each contribution?{/ts}</td><td>{if $recur.is_email_receipt}{ts}Yes{/ts}{else}{ts}No{/ts}{/if}</td></tr>
-            {if $recur.payment_processor}<tr><td class="label">{ts}Payment Processor{/ts}</td><td>{$recur.payment_processor}</td></tr>{/if}
-            {if $recur.financial_type}<tr><td class="label">{ts}Financial Type{/ts}</td><td>{$recur.financial_type}</td></tr>{/if}
-            {if $recur.campaign}<tr><td class="label">{ts}Campaign{/ts}</td><td>{$recur.campaign}</td></tr>{/if}
-            {if $recur.membership_id}<tr>
-              <td class="label">{ts}Membership{/ts}</td>
-              <td><a class="crm-hover-button action-item" href='{crmURL p="civicrm/contact/view/membership" q="action=view&reset=1&cid=`$contactId`&id=`$recur.membership_id`&context=membership&selectedChild=member"}'>{$recur.membership_name}</a></td>
-              </tr>
-            {/if}
-            {include file="CRM/Custom/Page/CustomDataView.tpl"}
-
-          </table>
-          <div class="crm-submit-buttons"><a class="button cancel crm-form-submit" href="{crmURL p='civicrm/contact/view' q='action=browse&selectedChild=contribute'}">{ts}Done{/ts}</a></div>
-        </div>
-    {/if}
-
-  <script type="text/javascript">
-    var recurContribID = {$recur.id};
-    var contactID = {$contactId};
-    {literal}
-    CRM.$(function($) {
-      CRM.loadPage(
-        CRM.url(
-          'civicrm/contribute/contributionrecur-payments',
-          {
-            reset: 1,
-            id: recurContribID,
-            cid: contactID
-          },
-          'back'
-        ),
-        {
-          target : '#recurring-contribution-payments',
-          dialog : false
-        }
-      );
-    });
-    {/literal}
-  </script>
-  <div id="recurring-contribution-payments"></div>
-{/if}
-{if $recurRows}
-  {include file="CRM/Contribute/Page/ContributionRecurSelector.tpl"}
+    </table>
+    <div class="crm-submit-buttons"><a class="button cancel crm-form-submit" href="{crmURL p='civicrm/contact/view' q='action=browse&selectedChild=contribute'}">{ts}Done{/ts}</a></div>
+  </div>
 {/if}
+
+<script type="text/javascript">
+  var recurContribID = {$recur.id};
+  var contactID = {$contactId};
+  {literal}
+  CRM.$(function($) {
+    CRM.loadPage(
+            CRM.url(
+                    'civicrm/contribute/contributionrecur-payments',
+                    {
+                      reset: 1,
+                      id: recurContribID,
+                      cid: contactID
+                    },
+                    'back'
+            ),
+            {
+              target : '#recurring-contribution-payments',
+              dialog : false
+            }
+    );
+  });
+  {/literal}
+</script>
+<div id="recurring-contribution-payments"></div>
diff --git a/civicrm/templates/CRM/Contribute/Page/DashBoard.tpl b/civicrm/templates/CRM/Contribute/Page/DashBoard.tpl
index aed10c2c5554868dcd12a688c94dd1b3726d76f5..5dfc47df28424403b45860002225461021c2a4a4 100644
--- a/civicrm/templates/CRM/Contribute/Page/DashBoard.tpl
+++ b/civicrm/templates/CRM/Contribute/Page/DashBoard.tpl
@@ -55,7 +55,7 @@
       <a href="{$configPagesURL}" class="button no-popup"><span>{ts}Manage Contribution Pages{/ts}</span></a>
     </td>
     <td>
-      <a href="{$newPageURL}" class="button no-popup"><span><i class="crm-i fa-plus-circle"></i> {ts}Add Contribution Page{/ts}</span></a>
+      <a href="{$newPageURL}" class="button no-popup"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Add Contribution Page{/ts}</span></a>
     </td>
   </tr>
 </table>
diff --git a/civicrm/templates/CRM/Contribute/Page/Premium.tpl b/civicrm/templates/CRM/Contribute/Page/Premium.tpl
index a226f68833863b5cd7fd1e0a5df97641cef81ee4..3737acf5ae1650792317e466867776d82f7b51c2 100644
--- a/civicrm/templates/CRM/Contribute/Page/Premium.tpl
+++ b/civicrm/templates/CRM/Contribute/Page/Premium.tpl
@@ -43,7 +43,7 @@
     </div>
     {if $products}
       <div class="action-link">
-        <a href="{crmURL p='civicrm/admin/contribute/addProductToPage' q="reset=1&action=update&id=$id"}">&raquo; {ts}Offer Another Premium on this Contribution Page{/ts}</a>
+        <a href="{crmURL p='civicrm/admin/contribute/addProductToPage' q="reset=1&action=update&id=$id"}"><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Offer Another Premium on this Contribution Page{/ts}</a>
       </div>
     {/if}
 </div>
diff --git a/civicrm/templates/CRM/Contribute/Page/Tab.tpl b/civicrm/templates/CRM/Contribute/Page/Tab.tpl
index f154d5d1bff9cb639d189cf445c4338ec8503c5a..8e17896d6080b2fa6fa17adb6be6d99a82d94414 100644
--- a/civicrm/templates/CRM/Contribute/Page/Tab.tpl
+++ b/civicrm/templates/CRM/Contribute/Page/Tab.tpl
@@ -48,9 +48,9 @@
 
           {if $action eq 16 and $permission EQ 'edit'}
             <div class="action-link">
-              <a accesskey="N" href="{$newContribURL}" class="button"><span><i class="crm-i fa-plus-circle"></i> {ts}Record Contribution (Check, Cash, EFT ...){/ts}</span></a>
+              <a accesskey="N" href="{$newContribURL}" class="button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Record Contribution (Check, Cash, EFT ...){/ts}</span></a>
               {if $newCredit}
-                <a accesskey="N" href="{$newCreditURL}" class="button"><span><i class="crm-i fa-credit-card"></i> {ts}Submit Credit Card Contribution{/ts}</span></a>
+                <a accesskey="N" href="{$newCreditURL}" class="button"><span><i class="crm-i fa-credit-card" aria-hidden="true"></i> {ts}Submit Credit Card Contribution{/ts}</span></a>
               {/if}
               <br /><br />
             </div>
@@ -79,11 +79,11 @@
           {if $recur}
             <div class="crm-block crm-contact-contribute-recur crm-contact-contribute-recur-active">
               <h3>{ts}Active Recurring Contributions{/ts}</h3>
-              {include file="CRM/Contribute/Page/ContributionRecur.tpl" recurRows=$activeRecurRows}
+              {include file="CRM/Contribute/Page/ContributionRecurSelector.tpl" recurRows=$activeRecurRows}
             </div>
             <div class="crm-block crm-contact-contribute-recur crm-contact-contribute-recur-inactive">
               <h3>{ts}Inactive Recurring Contributions{/ts}</h3>
-              {include file="CRM/Contribute/Page/ContributionRecur.tpl" recurRows=$inactiveRecurRows}
+              {include file="CRM/Contribute/Page/ContributionRecurSelector.tpl" recurRows=$inactiveRecurRows}
             </div>
           {/if}
         </div>
diff --git a/civicrm/templates/CRM/Contribute/Page/UserDashboard.tpl b/civicrm/templates/CRM/Contribute/Page/UserDashboard.tpl
index eb95300e2b86671a472737640b75d93127d9396c..29ae828479a8da79ab473e73a0af67d16471e652 100644
--- a/civicrm/templates/CRM/Contribute/Page/UserDashboard.tpl
+++ b/civicrm/templates/CRM/Contribute/Page/UserDashboard.tpl
@@ -49,7 +49,7 @@
                             {if call_user_func(array('CRM_Core_Permission','check'), 'view my invoices') OR call_user_func(array('CRM_Core_Permission','check'), 'access CiviContribute')}
                                 <a class="button no-popup nowrap"
                                    href="{crmURL p='civicrm/contribute/invoice' q=$urlParams}">
-                                    <i class="crm-i fa-print"></i>
+                                    <i class="crm-i fa-print" aria-hidden="true"></i>
                                     {if $row.contribution_status_name != 'Refunded' && $row.contribution_status_name != 'Cancelled' }
                                         <span>{ts}Print Invoice{/ts}</span>
                                     {else}
diff --git a/civicrm/templates/CRM/Core/Form/Task/Batch.tpl b/civicrm/templates/CRM/Core/Form/Task/Batch.tpl
index 5be7cbb1abef3635b9307587a525b57a710079c4..ed23171394abaa8f700dfbfc23091edc9f13d1a6 100644
--- a/civicrm/templates/CRM/Core/Form/Task/Batch.tpl
+++ b/civicrm/templates/CRM/Core/Form/Task/Batch.tpl
@@ -22,7 +22,7 @@
            {/foreach}
 
               {foreach from=$fields item=field key=fieldName}
-                <td><img  src="{$config->resourceBase}i/copy.png" alt="{ts 1=$field.title}Click to copy %1 from row one to all rows.{/ts}" fname="{$field.name}" class="action-icon" title="{ts}Click here to copy the value in row one to ALL rows.{/ts}" />{$field.title}</td>
+                <td>{copyIcon name=$field.name title=$field.title}{$field.title}</td>
              {/foreach}
             </tr>
           </thead>
diff --git a/civicrm/templates/CRM/Core/I18n/Dialog.tpl b/civicrm/templates/CRM/Core/I18n/Dialog.tpl
index 429952a6646369cd9d34bfcf493e4efb76099309..a63991513d6d4919eb99816a9a927967be100b35 100644
--- a/civicrm/templates/CRM/Core/I18n/Dialog.tpl
+++ b/civicrm/templates/CRM/Core/I18n/Dialog.tpl
@@ -9,6 +9,6 @@
 *}
 {if $config->languageLimit && $config->languageLimit|@count >= 2 and $translatePermission }
   <a href="{crmURL p='civicrm/i18n' q="reset=1&table=$table&field=$field&id=$id"}" data-field="{$field}" class="crm-hover-button crm-multilingual-edit-button" title="{ts}Languages{/ts}">
-    <i class="crm-i fa-language fa-lg"></i>
+    <i class="crm-i fa-language fa-lg" aria-hidden="true"></i>
   </a>
 {/if}
diff --git a/civicrm/templates/CRM/Core/Page/RecurringEntityPreview.tpl b/civicrm/templates/CRM/Core/Page/RecurringEntityPreview.tpl
index ec36313d52a1897b628935e2ac7b0ce7c64b9a89..9b83796ba94fcde4c809d1f063a22027be8d6415 100644
--- a/civicrm/templates/CRM/Core/Page/RecurringEntityPreview.tpl
+++ b/civicrm/templates/CRM/Core/Page/RecurringEntityPreview.tpl
@@ -9,7 +9,7 @@
 *}
 {if !empty($participantData)}
   <div class="messages status no-popup">
-    <i class="crm-i fa-exclamation-triangle"></i>
+    <i class="crm-i fa-exclamation-triangle" aria-hidden="true"></i>
     {ts}There are participants registered for repeating events being removed from the set. Those with participants will be converted to standalone events, and those without registration will be deleted.{/ts}
   </div>
   <table class="display">
diff --git a/civicrm/templates/CRM/Custom/Form/Edit/CustomData.tpl b/civicrm/templates/CRM/Custom/Form/Edit/CustomData.tpl
index 4af50207837ce50525ec20f5bbae82599e7797ed..8228791b3cfef327dd804e1c0be0ef950f5d1f88 100644
--- a/civicrm/templates/CRM/Custom/Form/Edit/CustomData.tpl
+++ b/civicrm/templates/CRM/Custom/Form/Edit/CustomData.tpl
@@ -34,7 +34,7 @@
   {else}
     <div id="add-more-link-{$cgCount}" class="add-more-link-{$group_id} add-more-link-{$group_id}-{$cgCount}">
       <a href="#" class="crm-hover-button" onclick="CRM.buildCustomData('{$cd_edit.extends}',{if $cd_edit.subtype}'{$cd_edit.subtype}'{else}'{$cd_edit.extends_entity_column_id}'{/if}, '', {$cgCount}, {$group_id}, true ); return false;">
-        <i class="crm-i fa-plus-circle"></i>
+        <i class="crm-i fa-plus-circle" aria-hidden="true"></i>
         {ts 1=$cd_edit.title}Another %1 record{/ts}
       </a>
     </div>
diff --git a/civicrm/templates/CRM/Custom/Form/Field.tpl b/civicrm/templates/CRM/Custom/Form/Field.tpl
index 040dc348cbb97927f1e32e5dcb92d2e9007d95f9..f6be9fb9c330d7b0ee6847192dad9fc197d54a81 100644
--- a/civicrm/templates/CRM/Custom/Form/Field.tpl
+++ b/civicrm/templates/CRM/Custom/Form/Field.tpl
@@ -27,13 +27,17 @@
         {if $action eq 2 and $changeFieldType}
           <br />
           <a class="action-item crm-hover-button" href='{crmURL p="civicrm/admin/custom/group/field/changetype" q="reset=1&id=`$id`"}'>
-            <i class="crm-i fa-wrench"></i>
+            <i class="crm-i fa-wrench" aria-hidden="true"></i>
             {ts}Change Input Field Type{/ts}
           </a>
           <div class='clear'></div>
         {/if}
       </td>
     </tr>
+    <tr class="crm-custom-field-form-block-serialize">
+      <td class="label">{$form.serialize.label}</td>
+      <td class="html-adjust">{$form.serialize.html}</td>
+    </tr>
     {if $form.in_selector}
       <tr class='crm-custom-field-form-block-in_selector'>
         <td class='label'>{$form.in_selector.label}</td>
@@ -59,7 +63,7 @@
         {$form.group_id.html}
         &nbsp;&nbsp;<span><a class="crm-hover-button toggle-contact-ref-mode" href="#Advance">{ts}Advanced Filter{/ts}</a></span>
         {capture assign=searchPreferences}{crmURL p="civicrm/admin/setting/search" q="reset=1"}{/capture}
-        <div class="messages status no-popup"><i class="crm-i fa-exclamation-triangle"></i> {ts 1=$searchPreferences}If you are planning on using this field in front-end profile, event registration or contribution forms, you should 'Limit List to Group' or configure an 'Advanced Filter'  (so that you do not unintentionally expose your entire set of contacts). Users must have either 'access contact reference fields' OR 'access CiviCRM' permission in order to use contact reference autocomplete fields. You can assign 'access contact reference fields' to the anonymous role if you want un-authenticated visitors to use this field. Use <a href='%1'>Search Preferences - Contact Reference Options</a> to control the fields included in the search results.{/ts}
+        <div class="messages status no-popup"><i class="crm-i fa-exclamation-triangle" aria-hidden="true"></i> {ts 1=$searchPreferences}If you are planning on using this field in front-end profile, event registration or contribution forms, you should 'Limit List to Group' or configure an 'Advanced Filter'  (so that you do not unintentionally expose your entire set of contacts). Users must have either 'access contact reference fields' OR 'access CiviCRM' permission in order to use contact reference autocomplete fields. You can assign 'access contact reference fields' to the anonymous role if you want un-authenticated visitors to use this field. Use <a href='%1'>Search Preferences - Contact Reference Options</a> to control the fields included in the search results.{/ts}
       </td>
     </tr>
     <tr id='field_advance_filter'>
@@ -262,6 +266,8 @@
       $("#textLength", $form).toggle(dataType === 'String');
 
       $("#noteColumns, #noteRows, #noteLength", $form).toggle(dataType === 'Memo');
+
+      $(".crm-custom-field-form-block-serialize", $form).toggle(htmlType === 'Select' || htmlType === 'Country' || htmlType === 'StateProvince');
     }
 
     $('[name^="data_type"]', $form).change(customOptionHtmlType);
diff --git a/civicrm/templates/CRM/Custom/Form/Optionfields.tpl b/civicrm/templates/CRM/Custom/Form/Optionfields.tpl
index c4cf3c8b31ae839f8e5657173f025a270e7fe677..0b145459516ce73d0762c589f73bb45a07b2d7e5 100644
--- a/civicrm/templates/CRM/Custom/Form/Optionfields.tpl
+++ b/civicrm/templates/CRM/Custom/Form/Optionfields.tpl
@@ -42,7 +42,7 @@
   <tr id="optionField_{$index}" class="form-item {cycle values="odd-row,even-row"}">
         <td>
         {if $index GT 1}
-            <a onclick="showHideRow({$index}); return false;" name="optionField_{$index}" href="#" class="form-link"><i class="crm-i fa-trash" title="{ts}hide field or section{/ts}"></i></a>
+            <a onclick="showHideRow({$index}); return false;" name="optionField_{$index}" href="#" class="form-link"><i class="crm-i fa-trash" title="{ts}hide field or section{/ts}" aria-hidden="true"></i></a>
         {/if}
         </td>
       <td>
@@ -61,7 +61,7 @@
     {/section}
     </table>
   <div id="optionFieldLink" class="add-remove-link">
-        <a onclick="showHideRow(); return false;" name="optionFieldLink" href="#" class="form-link"><i class="crm-i fa-plus-circle"></i> {ts}add another choice{/ts}</a>
+        <a onclick="showHideRow(); return false;" name="optionFieldLink" href="#" class="form-link"><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}add another choice{/ts}</a>
     </div>
   <span id="additionalOption" class="description">
     {ts}If you need additional options - you can add them after you Save your current entries.{/ts}
diff --git a/civicrm/templates/CRM/Custom/Page/CustomDataView.tpl b/civicrm/templates/CRM/Custom/Page/CustomDataView.tpl
index 3cddab39b417d09ed569bd687dc436424bd656e5..fafb2fd82d1a87d01b458fdbbe81bcf56d33da98 100644
--- a/civicrm/templates/CRM/Custom/Page/CustomDataView.tpl
+++ b/civicrm/templates/CRM/Custom/Page/CustomDataView.tpl
@@ -24,7 +24,7 @@
           <td>
             <a
               href="{crmURL p="civicrm/contact/view/cd/edit" q="tableId=`$contactId`&cid=`$contactId`&groupID=`$groupId`&action=update&reset=1"}"
-              class="button" style="margin-left: 6px;"><span><i class="crm-i fa-pencil"></i> {ts 1=$cd_edit.title}Edit %1{/ts}</span></a><br/><br/>
+              class="button" style="margin-left: 6px;"><span><i class="crm-i fa-pencil" aria-hidden="true"></i> {ts 1=$cd_edit.title}Edit %1{/ts}</span></a><br/><br/>
           </td>
         </tr>
       {/if}
@@ -43,7 +43,7 @@
                   <a href="#" class="crm-hover-button crm-custom-value-del"
                      data-post='{ldelim}"valueID": "{$cvID}", "groupID": "{$customGroupId}", "contactId": "{$contactId}", "key": "{crmKey name='civicrm/ajax/customvalue'}"{rdelim}'
                      title="{ts 1=$cd_edit.title|cat:" `$rowCount`"}Delete %1{/ts}">
-                    <i class="crm-i fa-trash"></i> {ts}Delete{/ts}
+                    <i class="crm-i fa-trash" aria-hidden="true"></i> {ts}Delete{/ts}
                   </a>
                 </div>
               {/if}
diff --git a/civicrm/templates/CRM/Custom/Page/Option.tpl b/civicrm/templates/CRM/Custom/Page/Option.tpl
index f47cafc27e4ea8fe7e0244f57424b14936864883..41b49ac784904ad6e6fca788dcbcc08ee15e53bc 100644
--- a/civicrm/templates/CRM/Custom/Page/Option.tpl
+++ b/civicrm/templates/CRM/Custom/Page/Option.tpl
@@ -54,8 +54,8 @@
               "aoColumns"  : [
                               {sClass:'crm-custom_option-label'},
                               {sClass:'crm-custom_option-value'},
+                              {sClass:'crm-custom_option-description'},
                               {sClass:'crm-custom_option-default_value'},
-                              {sClass:'crm-custom_option-default_description'},
                               {sClass:'crm-custom_option-is_active'},
                               {sClass:'crm-custom_option-links'},
                               {sClass:'hiddenElement'}
@@ -87,7 +87,7 @@
                 $(nRow).addClass(cl).attr({id: 'OptionValue-' + id});
                 $('td:eq(0)', nRow).wrapInner('<span class="crm-editable crmf-label" />');
                 $('td:eq(0)', nRow).prepend('<span class="crm-i fa-arrows crm-grip" />');
-                $('td:eq(2)', nRow).addClass('crmf-default_value');
+                $('td:eq(3)', nRow).addClass('crmf-default_value').html(CRM.utils.formatIcon('fa-check', ts('Default'), nRow.cells[3].innerText));
                 return nRow;
               },
               "fnDrawCallback": function() {
diff --git a/civicrm/templates/CRM/Dashlet/Page/Blog.tpl b/civicrm/templates/CRM/Dashlet/Page/Blog.tpl
index df8e45a04d45d5b3fed600e527264af194cc6c1f..16d33ff2dea2497459703acd1769ee86d48df5d0 100644
--- a/civicrm/templates/CRM/Dashlet/Page/Blog.tpl
+++ b/civicrm/templates/CRM/Dashlet/Page/Blog.tpl
@@ -52,7 +52,7 @@
         </div>
         <div class="crm-accordion-body">
           <div>{$article.description}</div>
-          <p class="crm-news-feed-item-link"><a target="_blank" href="{$article.link}" title="{$article.title|escape}"><i class="crm-i fa-external-link"></i> {ts}read more{/ts}…</a></p>
+          <p class="crm-news-feed-item-link"><a target="_blank" href="{$article.link}" title="{$article.title|escape}"><i class="crm-i fa-external-link" aria-hidden="true"></i> {ts}read more{/ts}…</a></p>
         </div>
       </div>
     {/foreach}
diff --git a/civicrm/templates/CRM/Dashlet/Page/CaseDashboard.tpl b/civicrm/templates/CRM/Dashlet/Page/CaseDashboard.tpl
index 14a12500153031040d648be9232eb7c85bcc01ce..36811b2db903c4bb11344dbedfb0b19a0de95fef 100644
--- a/civicrm/templates/CRM/Dashlet/Page/CaseDashboard.tpl
+++ b/civicrm/templates/CRM/Dashlet/Page/CaseDashboard.tpl
@@ -18,7 +18,7 @@
     <tr>
       <td>
         <a href="{$newCaseURL}" class="button">
-          <span><i class="crm-i fa-plus-circle"></i> {ts}New Case{/ts}</span>
+          <span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}New Case{/ts}</span>
         </a>
       </td>
     </tr>
@@ -26,19 +26,19 @@
    {if $myCases}
     <tr>
       <td class="right">
-        <a href="{crmURL p="civicrm/case" q="reset=1&all=1"}"><span>&raquo; {ts}Show ALL Cases with Upcoming Activities{/ts}</span></a>
+        <a href="{crmURL p="civicrm/case" q="reset=1&all=1"}"><span><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Show ALL Cases with Upcoming Activities{/ts}</span></a>
       </td>
     </tr>
    {else}
     <tr>
       <td class="right">
-        <a href="{crmURL p="civicrm/case" q="reset=1&all=0"}"><span>&raquo; {ts}Show My Cases with Upcoming Activities{/ts}</span></a>
+        <a href="{crmURL p="civicrm/case" q="reset=1&all=0"}"><span><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Show My Cases with Upcoming Activities{/ts}</span></a>
       </td>
     </tr>
    {/if}
    <tr>
      <td class="right">
-       <a href="{crmURL p="civicrm/case/search" q="reset=1&case_owner=1&force=1"}"><span>&raquo; {ts}Show My Cases{/ts}</span></a>
+       <a href="{crmURL p="civicrm/case/search" q="reset=1&case_owner=1&force=1"}"><span><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Show My Cases{/ts}</span></a>
      </td>
    </tr>
   </table>
diff --git a/civicrm/templates/CRM/Event/Cart/Page/ViewCart.tpl b/civicrm/templates/CRM/Event/Cart/Page/ViewCart.tpl
index 2e84d4965e9b6ba7ccc6cf34f6c5593e865680cf..0cfd2c1db40c17dc376100bb88d8b6a93b8f73c9 100644
--- a/civicrm/templates/CRM/Event/Cart/Page/ViewCart.tpl
+++ b/civicrm/templates/CRM/Event/Cart/Page/ViewCart.tpl
@@ -30,7 +30,7 @@
       {/foreach}
       </tbody>
     </table>
-    <a href="{crmURL p='civicrm/event/cart_checkout'}" class="button crm-check-out-button"><i class="crm-i fa-credit-card"></i> {ts}Checkout{/ts}</a>
+    <a href="{crmURL p='civicrm/event/cart_checkout'}" class="button crm-check-out-button"><i class="crm-i fa-credit-card" aria-hidden="true"></i> {ts}Checkout{/ts}</a>
   </div>
 {/if}
-<a href="{crmURL p="civicrm/event/ical" q="reset=1&page=1&html=1"}">&laquo; Back to Event List</a>
+<a href="{crmURL p="civicrm/event/ical" q="reset=1&page=1&html=1"}"><i class="crm-i fa-chevron-left" aria-hidden="true"></i> Back to Event List</a>
diff --git a/civicrm/templates/CRM/Event/Form/EventFees.tpl b/civicrm/templates/CRM/Event/Form/EventFees.tpl
index a786cb231088eadbfd423b08afd19ce17a31dcc8..355e821caa77ce4bfa9a9f88c96fb46ba93901f1 100644
--- a/civicrm/templates/CRM/Event/Form/EventFees.tpl
+++ b/civicrm/templates/CRM/Event/Form/EventFees.tpl
@@ -32,7 +32,7 @@
           <tr>
             <td></td>
             <td>
-              <a class="action-item crm-hover-button" href='{crmURL p="civicrm/event/participant/feeselection" q="reset=1&id=`$participantId`&cid=`$contactId`&action=update"}'><i class="crm-i fa-pencil"></i> {ts}Change Selections{/ts}</a>
+              <a class="action-item crm-hover-button" href='{crmURL p="civicrm/event/participant/feeselection" q="reset=1&id=`$participantId`&cid=`$contactId`&action=update"}'><i class="crm-i fa-pencil" aria-hidden="true"></i> {ts}Change Selections{/ts}</a>
             </td>
          </tr>
       {else} {* New participant *}
diff --git a/civicrm/templates/CRM/Event/Form/ManageEvent/ConfirmRepeatMode.tpl b/civicrm/templates/CRM/Event/Form/ManageEvent/ConfirmRepeatMode.tpl
index 5038b97660e1523e9a47e4512e02f20aa55d27c4..8d48983cc5adc1fb22cbe022d67cb2fc067969ae 100644
--- a/civicrm/templates/CRM/Event/Form/ManageEvent/ConfirmRepeatMode.tpl
+++ b/civicrm/templates/CRM/Event/Form/ManageEvent/ConfirmRepeatMode.tpl
@@ -25,7 +25,7 @@
         <label for="recur-all-entity">{ts 1=$entity_type}Every %1{/ts}</label>
         <div class="description">{ts 1=$entity_type}Change applies to every %1 in the series.{/ts}</div>
       </div>
-      <div class="status help"><i class="crm-i fa-info-circle"></i> {ts}Changes to date or time will <em>not</em> be applied to others in the series.{/ts}</div>
+      <div class="status help"><i class="crm-i fa-info-circle" aria-hidden="true"></i> {ts}Changes to date or time will <em>not</em> be applied to others in the series.{/ts}</div>
     </div>
   </script>
 {literal}
diff --git a/civicrm/templates/CRM/Event/Form/ManageEvent/EventInfo.tpl b/civicrm/templates/CRM/Event/Form/ManageEvent/EventInfo.tpl
index ca6cfbc87bc5848ccf12df9428fd18ccfe5f9d5f..8e1b0645c162bff3baf0279d34b7be021e147155 100644
--- a/civicrm/templates/CRM/Event/Form/ManageEvent/EventInfo.tpl
+++ b/civicrm/templates/CRM/Event/Form/ManageEvent/EventInfo.tpl
@@ -74,7 +74,7 @@
       <td>
         {$form.max_participants.html|crmAddClass:four}
         {if call_user_func(array('CRM_Core_Permission','check'), 'administer CiviCRM') }
-          <a class="crm-popup crm-hover-button" target="_blank" title="{ts}Edit Participant Status Options{/ts}" href="{crmURL p='civicrm/admin/participant_status' q='reset=1'}"><i class="crm-i fa-wrench"></i></a>
+          <a class="crm-popup crm-hover-button" target="_blank" title="{ts}Edit Participant Status Options{/ts}" href="{crmURL p='civicrm/admin/participant_status' q='reset=1'}"><i class="crm-i fa-wrench" aria-hidden="true"></i></a>
         {/if}
       </td>
     </tr>
diff --git a/civicrm/templates/CRM/Event/Form/ManageEvent/Fee.tpl b/civicrm/templates/CRM/Event/Form/ManageEvent/Fee.tpl
index 2e93012d0192d505a6eb86d87022a71062258c5d..e397febb8cc1fdd9c6637c0af207eee75225ccff 100644
--- a/civicrm/templates/CRM/Event/Form/ManageEvent/Fee.tpl
+++ b/civicrm/templates/CRM/Event/Form/ManageEvent/Fee.tpl
@@ -174,7 +174,7 @@
   {section name=rowLoop start=1 loop=6}
      {assign var=index value=$smarty.section.rowLoop.index}
      <tr id="discount_{$index}" class=" crm-event-manage-fee-form-block-discount_{$index} {if $index GT 1 AND empty( $form.discount_name[$index].value) } hiddenElement {/if} form-item {cycle values="odd-row,even-row"}">
-           <td>{if $index GT 1} <a onclick="showHideDiscountRow('discount_{$index}', false, {$index}); return false;" name="discount_{$index}" href="#" class="form-link"><img src="{$config->resourceBase}i/TreeMinus.gif" class="action-icon" alt="{ts}hide field or section{/ts}"/></a>{/if}
+           <td>{if $index GT 1} <a onclick="showHideDiscountRow('discount_{$index}', false, {$index}); return false;" name="discount_{$index}" href="#" class="form-link">{icon icon="fa-trash"}{ts}remove discount set{/ts}{/icon}</span></a>{/if}
            </td>
            <td class="crm-event-manage-fee-form-block-discount_name"> {$form.discount_name.$index.html}</td>
            <td class="crm-event-manage-fee-form-block-discount_start_date"> {$form.discount_start_date.$index.html} </td>
@@ -183,7 +183,7 @@
     {/section}
     </table>
         <div id="discountLink" class="add-remove-link">
-           <a onclick="showHideDiscountRow( 'discount', true);return false;" id="discountLink" href="#" class="form-link"><img src="{$config->resourceBase}i/TreePlus.gif" class="action-icon" alt="{ts}show field or section{/ts}"/>{ts}another discount set{/ts}</a>
+           <a onclick="showHideDiscountRow( 'discount', true);return false;" id="discountLink" href="#" class="form-link"><i class="crm-i fa-plus action-icon" aria-hidden="true"></i> {ts}another discount set{/ts}</a>
         </div>
         {$form._qf_Fee_submit.html}
 
diff --git a/civicrm/templates/CRM/Event/Form/ManageEvent/Registration.tpl b/civicrm/templates/CRM/Event/Form/ManageEvent/Registration.tpl
index 7c60d0a5a0f071f6cb0895d6f305118bdc042301..4858e9803259849346fd504d596704290e30d18a 100644
--- a/civicrm/templates/CRM/Event/Form/ManageEvent/Registration.tpl
+++ b/civicrm/templates/CRM/Event/Form/ManageEvent/Registration.tpl
@@ -12,8 +12,8 @@
       width="20%">{if $addProfileBottomAdd }{$form.additional_custom_post_id_multiple[$profileBottomNumAdd].label}
     {else}{$form.custom_post_id_multiple[$profileBottomNum].label}{/if}</td>
   <td>{if $addProfileBottomAdd }{$form.additional_custom_post_id_multiple[$profileBottomNumAdd].html}{else}{$form.custom_post_id_multiple[$profileBottomNum].html}{/if}
-    <span class='profile_bottom_link_remove'><a href="#" class="crm-hover-button crm-button-rem-profile" data-addtlPartc="{$addProfileBottomAdd}"><i class="crm-i fa-trash"></i> {ts}remove profile{/ts}</a></span>
-    <span class='profile_bottom_link'>&nbsp;<a href="#" class="crm-hover-button crm-button-add-profile"><i class="crm-i fa-plus-circle"></i> {ts}add another profile (bottom of page){/ts}</a></span>
+    <span class='profile_bottom_link_remove'><a href="#" class="crm-hover-button crm-button-rem-profile" data-addtlPartc="{$addProfileBottomAdd}"><i class="crm-i fa-trash" aria-hidden="true"></i> {ts}remove profile{/ts}</a></span>
+    <span class='profile_bottom_link'>&nbsp;<a href="#" class="crm-hover-button crm-button-add-profile"><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}add another profile (bottom of page){/ts}</a></span>
     {if $addProfileBottomAdd }
       <div
         class="description">{ts}Change this if you want to use a different profile for additional participants.{/ts}</div>
@@ -140,7 +140,7 @@
         <td scope="row" class="label" width="20%">{$form.custom_post_id.label}</td>
         <td>{$form.custom_post_id.html}
           <div class="description">{ts}Include additional fields on this registration form by selecting and configuring a CiviCRM Profile to be included at the bottom of the page.{/ts}</div>
-          <span class='profile_bottom_link_main {if $profilePostMultiple}hiddenElement{/if}'><a href="#" class="crm-hover-button crm-button-add-profile"><i class="crm-i fa-plus-circle"></i> {ts}add another profile (bottom of page){/ts}</a></span>
+          <span class='profile_bottom_link_main {if $profilePostMultiple}hiddenElement{/if}'><a href="#" class="crm-hover-button crm-button-add-profile"><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}add another profile (bottom of page){/ts}</a></span>
           <br/>
         </td>
       </tr>
@@ -154,13 +154,13 @@
               &nbsp;
               <span class='profile_bottom_link_remove'>
                 <a href="#" class="crm-hover-button crm-button-rem-profile">
-                  <i class="crm-i fa-trash"></i> {ts}remove profile{/ts}
+                  <i class="crm-i fa-trash" aria-hidden="true"></i> {ts}remove profile{/ts}
                 </a>
               </span>
               &nbsp;&nbsp;
               <span class='profile_bottom_link' {if !$smarty.foreach.profilePostIdName.last} style="display: none"{/if}>
                 <a href="#" class="crm-hover-button crm-button-add-profile">
-                  <i class="crm-i fa-plus-circle"></i>
+                  <i class="crm-i fa-plus-circle" aria-hidden="true"></i>
                   {ts}add another profile (bottom of page){/ts}
                 </a>
               </span>
@@ -185,7 +185,7 @@
           <div
             class="description">{ts}Change this if you want to use a different profile for additional participants.{/ts}
           </div>
-          <span class='profile_bottom_add_link_main{if $profilePostMultipleAdd} hiddenElement{/if}'><a href="#" class="crm-hover-button crm-button-add-profile"><i class="crm-i fa-plus-circle"></i> {ts}add another profile (bottom of page){/ts}</a></span>
+          <span class='profile_bottom_add_link_main{if $profilePostMultipleAdd} hiddenElement{/if}'><a href="#" class="crm-hover-button crm-button-add-profile"><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}add another profile (bottom of page){/ts}</a></span>
           <br/><span class="profile-links"></span>
         </td>
       </tr>
@@ -199,12 +199,12 @@
               &nbsp;
               <span class='profile_bottom_add_link_remove'>
                 <a href="#" class="crm-hover-button crm-button-rem-profile">
-                  <i class="crm-i fa-trash"></i> {ts}remove profile{/ts}
+                  <i class="crm-i fa-trash" aria-hidden="true"></i> {ts}remove profile{/ts}
                 </a>
               </span>
               <span class='profile_bottom_add_link' {if !$smarty.foreach.profilePostIdAName.last} style="display: none"{/if}>
                 <a href="#" class="crm-hover-button crm-button-add-profile">
-                  <i class="crm-i fa-plus-circle"></i>
+                  <i class="crm-i fa-plus-circle" aria-hidden="true"></i>
                   {ts}add another profile (bottom of page){/ts}
                 </a>
               </span>
diff --git a/civicrm/templates/CRM/Event/Form/ManageEvent/Tab.hlp b/civicrm/templates/CRM/Event/Form/ManageEvent/Tab.hlp
index b19e1ea1abbf883bc55f0aacd4c9eaad3d8f20ee..026ceeeda1f7b83da709c3e7993851512dcd56c5 100644
--- a/civicrm/templates/CRM/Event/Form/ManageEvent/Tab.hlp
+++ b/civicrm/templates/CRM/Event/Form/ManageEvent/Tab.hlp
@@ -11,56 +11,56 @@
   {ts}Event Configuration{/ts}
 {/htxt}
 {htxt id="id-configure-events"}
-<table> 
+<table>
 <tr>
-    <td><a href="{crmURL p='civicrm/event/manage/settings' q="reset=1&action=update&id=`$params.eventId`"}" id="idEventInformationandSettings">&raquo; {ts}Info &amp; Settings{/ts}</a></td>
+    <td><a href="{crmURL p='civicrm/event/manage/settings' q="reset=1&action=update&id=`$params.eventId`"}" id="idEventInformationandSettings"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Info &amp; Settings{/ts}</a></td>
     <td>{ts}Set event title, type (conference, performance etc.), description, start and end dates, maximum number of participants, and activate the event. Enable the public participant listing feature.{/ts}</td>
 </tr>
 <tr>
-    <td><a href="{crmURL p='civicrm/event/manage/location' q="reset=1&action=update&id=`$params.eventId`"}" id="idLocation">&raquo; {ts}Location{/ts}</a></td>
+    <td><a href="{crmURL p='civicrm/event/manage/location' q="reset=1&action=update&id=`$params.eventId`"}" id="idLocation"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Location{/ts}</a></td>
     <td>{ts}Set event location and event contact information (email and phone).{/ts}</td>
 </tr>
 <tr>
-    <td><a href="{crmURL p='civicrm/event/manage/fee' q="reset=1&action=update&id=`$params.eventId`"}" id="idFee">&raquo; {ts}Fees{/ts}</a></td>
+    <td><a href="{crmURL p='civicrm/event/manage/fee' q="reset=1&action=update&id=`$params.eventId`"}" id="idFee"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Fees{/ts}</a></td>
      <td>{ts}Determine if the event is free or paid. For paid events, set the payment processor, fee level(s) and discounts. Give online registrants the option to 'pay later' (e.g. mail in a check, call in a credit card, etc.).{/ts}</td>
 </tr>
 <tr>
-    <td><a href="{crmURL p='civicrm/event/manage/registration' q="reset=1&action=update&id=`$params.eventId`"}" id="idRegistration">&raquo; {ts}Online Registration{/ts}</a></td>
+    <td><a href="{crmURL p='civicrm/event/manage/registration' q="reset=1&action=update&id=`$params.eventId`"}" id="idRegistration"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Online Registration{/ts}</a></td>
     <td>{ts}Determine whether an online registration page is available. If so, configure registration, confirmation and thank you page elements and confirmation email details.{/ts}</td>
 </tr>
 <tr>
-    <td><a href="{crmURL p='civicrm/event/manage/friend' q="reset=1&action=update&id=`$params.eventId`"}" id="idFriend">&raquo; {ts}Tell a Friend{/ts}</a></td>
+    <td><a href="{crmURL p='civicrm/event/manage/friend' q="reset=1&action=update&id=`$params.eventId`"}" id="idFriend"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Tell a Friend{/ts}</a></td>
     <td>{ts}Make it easy for participants to spread the word about this event to friends and colleagues.{/ts}</td>
 </tr>
 
 {if !$params.isTemplate}
     <tr>
     {if $params.participantListingURL}
-        <td><a href="{$params.participantListingURL}" id="idParticipantListing">&raquo; {ts}Participant Listing{/ts}</a></td>
+        <td><a href="{$params.participantListingURL}" id="idParticipantListing"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Participant Listing{/ts}</a></td>
         {if $config->userSystem->is_drupal}
           <td>{ts 1=$params.participantListingURL}The following URL will display a list of registered participants for this event to users whose role includes "view event participants" permission: <a href="%1">%1</a>{/ts}</td>
         {else}
           <td>{ts 1=$params.participantListingURL}The following URL will display a list of registered participants for this event: <a href="%1">%1</a>{/ts}</td>
         {/if}
     {else}
-        <td>&raquo; {ts}Participant Listing{/ts}</td>
+        <td><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Participant Listing{/ts}</td>
         <td>{ts}Participant Listing is not enabled for this event. You can enable it from{/ts} <a href="{crmURL p='civicrm/event/manage/settings' q="reset=1&action=update&id=`$params.eventId`"}">{ts}Event Information and Settings{/ts}</a>.
     {/if}
     </tr>
 
     <tr>
-        <td><a href="{crmURL p='civicrm/event/info' q="reset=1&id=`$params.eventId`" fe=1}" id="idDisplayEvent">&raquo; {ts}Event Info{/ts}</a></td>
+        <td><a href="{crmURL p='civicrm/event/info' q="reset=1&id=`$params.eventId`" fe=1}" id="idDisplayEvent"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Event Info{/ts}</a></td>
         <td>{ts}View the Event Information page as it will be displayed to site visitors.{/ts}</td>
     </tr>
 
     {if $params.isOnlineRegistration}
     <tr>
-        <td><a href="{crmURL p='civicrm/event/register' q="reset=1&action=preview&id=`$params.eventId`" a=1 fe=1}" id="idTest-drive">&raquo; {ts}Registration{/ts}</a><br />({ts}test-drive{/ts})</td>
+        <td><a href="{crmURL p='civicrm/event/register' q="reset=1&action=preview&id=`$params.eventId`" a=1 fe=1}" id="idTest-drive"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Registration{/ts}</a><br />({ts}test-drive{/ts})</td>
         <td>{ts}Test-drive the entire online registration process - including custom fields, confirmation, thank-you page, and receipting. Fee payment transactions will be directed to your payment processor's test server. <strong>No live financial transactions will be submitted. However, a contact record will be created or updated and participant and contribution records will be saved to the database. Use obvious test contact names so you can review and delete these records as needed.</strong>{/ts}</td>
     </tr>
 
     <tr>
-        <td><a href="{crmURL a=1 fe=1 p='civicrm/event/register' q="reset=1&id=`$params.eventId`"}" id="idLive">&raquo; {ts}Registration{/ts}</a><br />({ts}live{/ts})</td>
+        <td><a href="{crmURL a=1 fe=1 p='civicrm/event/register' q="reset=1&id=`$params.eventId`"}" id="idLive"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Registration{/ts}</a><br />({ts}live{/ts})</td>
         <td>{ts}Review your customized <strong>LIVE</strong> online event registration page here. Use the following URL in links and buttons on any website to send visitors to this live page{/ts}:<br />
             <strong>{crmURL a=1 fe=1 p='civicrm/event/register' q="reset=1&id=`$params.eventId`"}</strong>
         </td>
diff --git a/civicrm/templates/CRM/Event/Form/Participant.tpl b/civicrm/templates/CRM/Event/Form/Participant.tpl
index 9cded07e8e165e6873bd54fddfb01ca58c5f1629..069ad4e2872acd2eecae19c08de7bf340df9b210 100644
--- a/civicrm/templates/CRM/Event/Form/Participant.tpl
+++ b/civicrm/templates/CRM/Event/Form/Participant.tpl
@@ -106,7 +106,7 @@
           }
 
         if ( showError ) {
-          cj('#validate_pricefield').show().html('<i class="crm-i fa-exclamation-triangle crm-i-red"></i>{/literal} {ts escape='js'}This Option is already full for this event.{/ts}{literal}');
+          cj('#validate_pricefield').show().html('<i class="crm-i fa-exclamation-triangle crm-i-red" aria-hidden="true"></i>{/literal} {ts escape='js'}This Option is already full for this event.{/ts}{literal}');
         }
         else {
           cj('#validate_pricefield').hide( ).html('');
@@ -175,7 +175,7 @@
         {else}
           {capture assign=ccModeLink}{crmURL p='civicrm/contact/view/participant' q="reset=1&action=add&context=standalone&mode=live"}{/capture}
         {/if}
-        <a class="open-inline-noreturn action-item crm-hover-button" href="{$ccModeLink}">&raquo; {ts}submit credit card event registration{/ts}</a>
+        <a class="open-inline-noreturn action-item crm-hover-button" href="{$ccModeLink}"><i class="crm-i fa-credit-card" aria-hidden="true"></i> {ts}submit credit card event registration{/ts}</a>
       </div>
     {/if}
     <div class="view-content">
diff --git a/civicrm/templates/CRM/Event/Form/ParticipantFeeSelection.tpl b/civicrm/templates/CRM/Event/Form/ParticipantFeeSelection.tpl
index 3aed291cf012a5496b6cb7cf9c08aaf8570dd7db..02246d54cba6681f43f14049d85aa8d8b2d3a687 100644
--- a/civicrm/templates/CRM/Event/Form/ParticipantFeeSelection.tpl
+++ b/civicrm/templates/CRM/Event/Form/ParticipantFeeSelection.tpl
@@ -87,7 +87,7 @@ CRM.$(function($) {
   <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location="bottom"}</div>
   {if !$email}
   <div class="messages status no-popup">
-    <i class="crm-i fa-info-circle"></i>&nbsp;{ts}You will not be able to send an automatic email receipt for this payment because there is no email address recorded for this contact. If you want a receipt to be sent when this payment is recorded, click Cancel and then click Edit from the Summary tab to add an email address before recording the payment.{/ts}
+    <i class="crm-i fa-info-circle" aria-hidden="true"></i>&nbsp;{ts}You will not be able to send an automatic email receipt for this payment because there is no email address recorded for this contact. If you want a receipt to be sent when this payment is recorded, click Cancel and then click Edit from the Summary tab to add an email address before recording the payment.{/ts}
   </div>
   {/if}
   <table class="form-layout">
@@ -123,7 +123,7 @@ CRM.$(function($) {
          <div class='label'>{ts}Total Paid{/ts}</div>
          <div class='content'>
            {$paymentInfo.paid|crmMoney}<br/>
-           <a class="crm-hover-button action-item crm-popup medium-popup" href='{crmURL p="civicrm/payment" q="view=transaction&action=browse&cid=`$contactId`&id=`$paymentInfo.id`&component=`$paymentInfo.component`&context=transaction"}'><i class="crm-i fa-list-alt"></i> {ts}view payments{/ts}</a>
+           <a class="crm-hover-button action-item crm-popup medium-popup" href='{crmURL p="civicrm/payment" q="view=transaction&action=browse&cid=`$contactId`&id=`$paymentInfo.id`&component=`$paymentInfo.component`&context=transaction"}'><i class="crm-i fa-list-alt" aria-hidden="true"></i> {ts}view payments{/ts}</a>
          </div>
          <div class='label'><strong>{ts}Balance Owed{/ts}</strong></div><div class='content'><strong id='balance-fee'></strong></div>
           </div>
diff --git a/civicrm/templates/CRM/Event/Form/ParticipantView.tpl b/civicrm/templates/CRM/Event/Form/ParticipantView.tpl
index 64d9e84363e85f28097e6da7e9c41624fce50b3f..0a49832e64d3ce015c0c648573fc6db16f342d25 100644
--- a/civicrm/templates/CRM/Event/Form/ParticipantView.tpl
+++ b/civicrm/templates/CRM/Event/Form/ParticipantView.tpl
@@ -16,14 +16,14 @@
          {if ( $context eq 'fulltext' || $context eq 'search' ) && $searchKey}
          {assign var='urlParams' value="reset=1&id=$id&cid=$contact_id&action=update&context=$context&selectedChild=event&key=$searchKey"}
          {/if}
-               <a class="button" href="{crmURL p='civicrm/contact/view/participant' q=$urlParams}" accesskey="e"><span><i class="crm-i fa-pencil"></i> {ts}Edit{/ts}</span></a>
+               <a class="button" href="{crmURL p='civicrm/contact/view/participant' q=$urlParams}" accesskey="e"><span><i class="crm-i fa-pencil" aria-hidden="true"></i> {ts}Edit{/ts}</span></a>
             {/if}
             {if call_user_func(array('CRM_Core_Permission','check'), 'delete in CiviEvent')}
                 {assign var='urlParams' value="reset=1&id=$id&cid=$contact_id&action=delete&context=$context&selectedChild=event"}
           {if ( $context eq 'fulltext' || $context eq 'search' ) && $searchKey}
           {assign var='urlParams' value="reset=1&id=$id&cid=$contact_id&action=delete&context=$context&selectedChild=event&key=$searchKey"}
           {/if}
-                <a class="button" href="{crmURL p='civicrm/contact/view/participant' q=$urlParams}"><span><i class="crm-i fa-trash"></i> {ts}Delete{/ts}</span></a>
+                <a class="button" href="{crmURL p='civicrm/contact/view/participant' q=$urlParams}"><span><i class="crm-i fa-trash" aria-hidden="true"></i> {ts}Delete{/ts}</span></a>
             {/if}
             {include file="CRM/common/formButtons.tpl" location="top"}
         </div>
@@ -34,7 +34,7 @@
       <td>
         <strong><a href="{crmURL p='civicrm/contact/view' q="reset=1&cid=$contact_id"}" title="{ts}View contact record{/ts}">{$displayName}</a></strong>
         <div>
-            <a class="action-item crm-hover-button" href="{crmURL p='civicrm/event/badge' q="reset=1&context=view&id=$id&cid=$contact_id"}"><i class="crm-i fa-print"></i> {ts}Print Name Badge{/ts}</a>
+            <a class="action-item crm-hover-button" href="{crmURL p='civicrm/event/badge' q="reset=1&context=view&id=$id&cid=$contact_id"}"><i class="crm-i fa-print" aria-hidden="true"></i> {ts}Print Name Badge{/ts}</a>
         </div>
       </td>
   </tr>
@@ -99,10 +99,10 @@
                 <td>{include file="CRM/Price/Page/LineItem.tpl" context="Event"}
                 {if call_user_func(array('CRM_Core_Permission','check'), 'edit event participants')}
                     {if $hasPayment or $parentHasPayment}
-                        <a class="action-item crm-hover-button" href='{crmURL p="civicrm/event/participant/feeselection" q="reset=1&id=`$participantId`&cid=`$contactId`&action=update"}'><i class="crm-i fa-pencil"></i> {ts}Change Selections{/ts}</a>
+                        <a class="action-item crm-hover-button" href='{crmURL p="civicrm/event/participant/feeselection" q="reset=1&id=`$participantId`&cid=`$contactId`&action=update"}'><i class="crm-i fa-pencil" aria-hidden="true"></i> {ts}Change Selections{/ts}</a>
                     {/if}
                     {if $transferOrCancelLink}
-                      <a class="action-item crm-hover-button" href={$transferOrCancelLink}><i class="crm-i fa-times"></i> {ts}Transfer or Cancel{/ts}</a>
+                      <a class="action-item crm-hover-button" href={$transferOrCancelLink}><i class="crm-i fa-times" aria-hidden="true"></i> {ts}Transfer or Cancel{/ts}</a>
                     {/if}
                 {/if}
                 </td>
@@ -132,14 +132,14 @@
     {assign var='urlParams' value="reset=1&id=$id&cid=$contact_id&action=update&context=$context&selectedChild=event&key=$searchKey"}
     {/if}
 
-           <a class="button" href="{crmURL p='civicrm/contact/view/participant' q=$urlParams}" accesskey="e"><span><i class="crm-i fa-pencil"></i> {ts}Edit{/ts}</span></a>
+           <a class="button" href="{crmURL p='civicrm/contact/view/participant' q=$urlParams}" accesskey="e"><span><i class="crm-i fa-pencil" aria-hidden="true"></i> {ts}Edit{/ts}</span></a>
         {/if}
         {if call_user_func(array('CRM_Core_Permission','check'), 'delete in CiviEvent')}
     {assign var='urlParams' value="reset=1&id=$id&cid=$contact_id&action=delete&context=$context&selectedChild=event"}
     {if ( $context eq 'fulltext' || $context eq 'search' ) && $searchKey}
     {assign var='urlParams' value="reset=1&id=$id&cid=$contact_id&action=delete&context=$context&selectedChild=event&key=$searchKey"}
     {/if}
-            <a class="button" href="{crmURL p='civicrm/contact/view/participant' q=$urlParams}"><span><i class="crm-i fa-trash"></i> {ts}Delete{/ts}</span></a>
+            <a class="button" href="{crmURL p='civicrm/contact/view/participant' q=$urlParams}"><span><i class="crm-i fa-trash" aria-hidden="true"></i> {ts}Delete{/ts}</span></a>
         {/if}
         {include file="CRM/common/formButtons.tpl" location="bottom"}
     </div>
diff --git a/civicrm/templates/CRM/Event/Form/Registration/PreviewHeader.tpl b/civicrm/templates/CRM/Event/Form/Registration/PreviewHeader.tpl
index 8ede044020ca80cde46ac3567ba15b7efc2dc8e7..395974c33d6e79b9e0b1643d94c352c2089f3e2c 100644
--- a/civicrm/templates/CRM/Event/Form/Registration/PreviewHeader.tpl
+++ b/civicrm/templates/CRM/Event/Form/Registration/PreviewHeader.tpl
@@ -9,7 +9,7 @@
 *}
 {* Displays Test-drive mode header for Event Registration pages. *}
 <div class="messages status section test_drive-section">
-  <i class="crm-i fa-cogs"></i>
+  <i class="crm-i fa-cogs" aria-hidden="true"></i>
      <strong>{ts}Test-drive Your Event Registration Page{/ts}</strong>
          {ts}This page is currently running in <strong>test-drive mode</strong>. If this is a paid event, transactions will be sent to your payment processor's test server. <strong>No live financial transactions will be submitted. However, a contact record will be created or updated and test event registration and contribution records will be saved to the database. Use obvious test contact names so you can review and delete these records as needed. </strong> Refer to your payment processor's documentation for information on values to use for test credit card number, security code, postal code, etc.{/ts}
 </div>
diff --git a/civicrm/templates/CRM/Event/Form/Registration/ThankYou.tpl b/civicrm/templates/CRM/Event/Form/Registration/ThankYou.tpl
index 7d43c8ac190003758dc6ddd79820f9b9dfa6db27..0929bc97c9e3eddfc73f93badfd3f4f11227e746 100644
--- a/civicrm/templates/CRM/Event/Form/Registration/ThankYou.tpl
+++ b/civicrm/templates/CRM/Event/Form/Registration/ThankYou.tpl
@@ -26,14 +26,14 @@
     {* Show link to Tell a Friend (CRM-2153) *}
     {if $friendText}
         <div id="tell-a-friend" class="crm-section tell_friend_link-section">
-            <a href="{$friendURL}" title="{$friendText|escape:'html'}" class="button"><span>&raquo; {$friendText}</span></a>
+            <a href="{$friendURL}" title="{$friendText|escape:'html'}" class="button"><span><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {$friendText}</span></a>
        </div><br /><br />
     {/if}
 
     {* Add button for donor to create their own Personal Campaign page *}
     {if $pcpLink}
       <div class="crm-section create_pcp_link-section">
-            <a href="{$pcpLink}" title="{$pcpLinkText|escape:'html'}" class="button"><span>&raquo; {$pcpLinkText}</span></a>
+            <a href="{$pcpLink}" title="{$pcpLinkText|escape:'html'}" class="button"><span><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {$pcpLinkText}</span></a>
         </div><br /><br />
     {/if}
 
@@ -195,11 +195,13 @@
     {/if}
 
     <div class="action-link section event_info_link-section">
-        <a href="{crmURL p='civicrm/event/info' q="reset=1&id=`$event.id`"}">&raquo; {ts 1=$event.event_title}Back to "%1" event information{/ts}</a>
+        <a href="{crmURL p='civicrm/event/info' q="reset=1&id=`$event.id`"}"><i class="crm-i fa-chevron-left" aria-hidden="true"></i> {ts 1=$event.event_title}Back to "%1" event information{/ts}</a>
     </div>
 
     {if $event.is_public }
+      <div class="action-link section iCal_links-section">
         {include file="CRM/Event/Page/iCalLinks.tpl"}
+      </div>
     {/if}
     {if $event.is_share}
     {capture assign=eventUrl}{crmURL p='civicrm/event/info' q="id=`$event.id`&amp;reset=1" a=1 fe=1 h=1}{/capture}
diff --git a/civicrm/templates/CRM/Event/Form/Selector.tpl b/civicrm/templates/CRM/Event/Form/Selector.tpl
index 919fddb1554640c57e3f5099f6e789a8925947f1..e0cdefac6e1364e9656089667d7910079dd63430 100644
--- a/civicrm/templates/CRM/Event/Form/Selector.tpl
+++ b/civicrm/templates/CRM/Event/Form/Selector.tpl
@@ -73,11 +73,11 @@
 {if $limit and $pager->_totalItems GT $limit }
   {if $context EQ 'event_dashboard' }
     <tr class="even-row">
-    <td colspan="10"><a href="{crmURL p='civicrm/event/search' q='reset=1'}">&raquo; {ts}Find more event participants{/ts}...</a></td></tr>
+    <td colspan="10"><a href="{crmURL p='civicrm/event/search' q='reset=1'}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Find more event participants{/ts}...</a></td></tr>
     </tr>
   {elseif $context eq 'participant' }
     <tr class="even-row">
-    <td colspan="7"><a href="{crmURL p='civicrm/contact/view' q="reset=1&force=1&selectedChild=participant&cid=$contactId"}">&raquo; {ts}View all events for this contact{/ts}...</a></td></tr>
+    <td colspan="7"><a href="{crmURL p='civicrm/contact/view' q="reset=1&force=1&selectedChild=participant&cid=$contactId"}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}View all events for this contact{/ts}...</a></td></tr>
     </tr>
   {/if}
 {/if}
diff --git a/civicrm/templates/CRM/Event/Form/Task/Batch.tpl b/civicrm/templates/CRM/Event/Form/Task/Batch.tpl
index eef206692417675b64792d8cac5f0abcd925baa1..895ec862e66f28ee8ac2e85ccf4694eb12fd2c77 100644
--- a/civicrm/templates/CRM/Event/Form/Task/Batch.tpl
+++ b/civicrm/templates/CRM/Event/Form/Task/Batch.tpl
@@ -42,7 +42,7 @@
 
              <td>{ts}Event{/ts}</td>
              {foreach from=$fields item=field key=fieldName}
-                <td><img  src="{$config->resourceBase}i/copy.png" alt="{ts 1=$field.title}Click to copy %1 from row one to all rows.{/ts}" fname="{$field.name}" class="action-icon" title="{ts}Click here to copy the value in row one to ALL rows.{/ts}" />{$field.title}</td>
+                <td>{copyIcon name=$field.name title=$field.title}{$field.title}</td>
              {/foreach}
 
          </tr>
diff --git a/civicrm/templates/CRM/Event/Page/DashBoard.tpl b/civicrm/templates/CRM/Event/Page/DashBoard.tpl
index 74fb0ecd03cf7029c73ae022cab1016285fd4e5a..b5db9cad5ec587cfcd92b90f003643aa2e409f4b 100644
--- a/civicrm/templates/CRM/Event/Page/DashBoard.tpl
+++ b/civicrm/templates/CRM/Event/Page/DashBoard.tpl
@@ -10,21 +10,15 @@
 {* CiviEvent DashBoard (launch page) *}
 {capture assign=newEventURL}{crmURL p="civicrm/event/add" q="action=add&reset=1"}{/capture}
 {capture assign=configPagesURL}{crmURL p="civicrm/event/manage" q="reset=1"}{/capture}
-{capture assign=icalFile}{crmURL p='civicrm/event/ical' q="reset=1" fe=1 a=1}{/capture}
-{capture assign=icalFeed}{crmURL p='civicrm/event/ical' q="reset=1&list=1" fe=1 a=1}{/capture}
-{capture assign=rssFeed}{crmURL p='civicrm/event/ical' q="reset=1&list=1&rss=1" fe=1 a=1}{/capture}
-{capture assign=htmlFeed}{crmURL p='civicrm/event/ical' q="reset=1&list=1&html=1" fe=1 a=1}{/capture}
 
 {if $eventSummary.total_events}
-    <a href="{$configPagesURL}" class="button no-popup"><span><i class="crm-i fa-th-list"></i> {ts}Manage Events{/ts}</span></a>
-    <a href="{$newEventURL}" class="button"><span><i class="crm-i fa-calendar-plus-o"></i> {ts}New Event{/ts}</span></a>
+    <a href="{$configPagesURL}" class="button no-popup"><span><i class="crm-i fa-th-list" aria-hidden="true"></i> {ts}Manage Events{/ts}</span></a>
+    <a href="{$newEventURL}" class="button"><span><i class="crm-i fa-calendar-plus-o" aria-hidden="true"></i> {ts}New Event{/ts}</span></a>
     <div class="clear">&nbsp;</div>
     <h3 id="crm-event-dashboard-heading">{ts}Event Summary{/ts}
       {help id="id-event-intro"}
-      <a href="{$htmlFeed}"  target="_blank" title="{ts}HTML listing of current and future public events.{/ts}" class="crm-event-feed-link"><i class="crm-i fa-lg fa-calendar"></i></a>
-      <a href="{$rssFeed}"  target="_blank" title="{ts}Get RSS 2.0 feed for current and future public events.{/ts}" class="crm-event-feed-link"><i class="crm-i fa-lg fa-rss"></i></a>
-      <a href="{$icalFile}" title="{ts}Download iCalendar file for current and future public events.{/ts}" class="crm-event-feed-link"><i class="crm-i fa-lg fa-download"></i></a>
-      <a href="{$icalFeed}"  target="_blank" title="{ts}Get iCalendar feed for current and future public events.{/ts}" class="crm-event-feed-link"><i class="crm-i fa-lg fa-calendar-o"></i></a></h3>
+      {include file="CRM/Event/Page/iCalLinks.tpl"}
+    </h3>
     {include file="CRM/common/jsortable.tpl"}
     <table id="options" class="display">
     <thead>
@@ -93,7 +87,7 @@
       {if $actionColumn}
         <td class="crm-event-isMap">
           {if $values.isMap}
-            <a href="{$values.isMap}" title="{ts}Map event location{/ts}">&raquo;&nbsp;{ts}Map{/ts}</a>
+            <a href="{$values.isMap}" title="{ts}Map event location{/ts}"><i class="crm-i fa-map-marker" aria-hidden="true"></i>&nbsp;{ts}Map{/ts}</a>
             &nbsp;|&nbsp;
           {/if}
           {if $values.configure}
@@ -124,7 +118,7 @@
     </tbody>
     </table>
     {if $eventSummary.total_events GT 10}
-     <div><a href="{crmURL p='civicrm/admin/event' q='reset=1'}">&raquo; {ts}Browse more events{/ts}...</a></div>
+     <div><a href="{crmURL p='civicrm/admin/event' q='reset=1'}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Browse more events{/ts}...</a></div>
     {/if}
 {else}
     <br />
diff --git a/civicrm/templates/CRM/Event/Page/EventInfo.tpl b/civicrm/templates/CRM/Event/Page/EventInfo.tpl
index 530476eb8de2c94456a4aabac52788dcbb93bd4e..6bb4cc6d244d8b7e1cb80806514b327d51554e44 100644
--- a/civicrm/templates/CRM/Event/Page/EventInfo.tpl
+++ b/civicrm/templates/CRM/Event/Page/EventInfo.tpl
@@ -12,7 +12,7 @@
 {if $registerClosed }
 <div class="spacer"></div>
 <div class="messages status no-popup">
-  <i class="crm-i fa-info-circle"></i>
+  <i class="crm-i fa-info-circle" aria-hidden="true"></i>
      &nbsp;{ts}Registration is closed for this event{/ts}
   </div>
 {/if}
@@ -23,7 +23,7 @@
   <li>
     <div id="crm-event-links-wrapper">
       <span id="crm-event-configure-link" class="crm-hover-button">
-        <span title="{ts}Configure this event.{/ts}" class="crm-i fa-wrench"></span>
+        <span title="{ts}Configure this event.{/ts}" class="crm-i fa-wrench" aria-hidden="true"></span>
       </span>
       <div class="ac_results" id="crm-event-links-list" style="margin-left: -25px;">
         <div class="crm-event-links-list-inner">
@@ -47,7 +47,7 @@
   <li>
     <div id="crm-participant-wrapper">
       <span id="crm-participant-links" class="crm-hover-button">
-        <span title="{ts}Participant listing links.{/ts}" class="crm-i fa-search"></span>
+        <span title="{ts}Participant listing links.{/ts}" class="crm-i fa-search" aria-hidden="true"></span>
       </span>
       <div class="ac_results" id="crm-participant-list" style="margin-left: -25px;">
         <div class="crm-participant-list-inner">
@@ -217,7 +217,10 @@
       {/crmRegion}
     </div>
     {if $event.is_public }
-        <br />{include file="CRM/Event/Page/iCalLinks.tpl"}
+        <br />
+        <div class="action-link section iCal_links-section">
+          {include file="CRM/Event/Page/iCalLinks.tpl"}
+        </div>
     {/if}
 
     {if $event.is_share }
diff --git a/civicrm/templates/CRM/Event/Page/List.tpl b/civicrm/templates/CRM/Event/Page/List.tpl
index 94a2b1291362a8eb2b89b6b154ec1f5501b28775..e5f5fa182f19c98b3c0ea81d370162e0ef5bc0aa 100644
--- a/civicrm/templates/CRM/Event/Page/List.tpl
+++ b/civicrm/templates/CRM/Event/Page/List.tpl
@@ -11,8 +11,8 @@
 {include file="CRM/common/jsortable.tpl"}
 <div class="crm-section crm-event-list">
   {if $eventCartEnabled}
-    <a href="{crmURL p='civicrm/event/view_cart' }" class="button crm-shoppingcart-button"><i class="crm-i fa-shopping-cart"></i> {ts}View Cart{/ts}</a>
-    <a href="{crmURL p='civicrm/event/cart_checkout'}" class="button crm-check-out-button"><i class="crm-i fa-credit-card"></i> {ts}Checkout{/ts}</a>
+    <a href="{crmURL p='civicrm/event/view_cart' }" class="button crm-shoppingcart-button"><i class="crm-i fa-shopping-cart" aria-hidden="true"></i> {ts}View Cart{/ts}</a>
+    <a href="{crmURL p='civicrm/event/cart_checkout'}" class="button crm-check-out-button"><i class="crm-i fa-credit-card" aria-hidden="true"></i> {ts}Checkout{/ts}</a>
   {/if}
 
   <table id="options" class="display">
diff --git a/civicrm/templates/CRM/Event/Page/ManageEvent.tpl b/civicrm/templates/CRM/Event/Page/ManageEvent.tpl
index 0f8812f83e29a5161cac33fe4c79a4782c548cf9..0f4a98d4aa2c8c71928c862c8b78f55b03208dac 100644
--- a/civicrm/templates/CRM/Event/Page/ManageEvent.tpl
+++ b/civicrm/templates/CRM/Event/Page/ManageEvent.tpl
@@ -8,23 +8,15 @@
  +--------------------------------------------------------------------+
 *}
 {capture assign=newEventURL}{crmURL p='civicrm/event/add' q="action=add&reset=1"}{/capture}
-{capture assign=icalFile}{crmURL p='civicrm/event/ical' q="reset=1" fe=1}{/capture}
-{capture assign=icalFeed}{crmURL p='civicrm/event/ical' q="reset=1&list=1" fe=1}{/capture}
-{capture assign=rssFeed}{crmURL p='civicrm/event/ical' q="reset=1&list=1&rss=1" fe=1}{/capture}
-{capture assign=htmlFeed}{crmURL p='civicrm/event/ical' q="reset=1&list=1&html=1" fe=1}{/capture}
 
 <div class="crm-block crm-content-block">
 <div class="float-right">
-  <a href="{$htmlFeed}"  target="_blank" title="{ts}HTML listing of current and future public events.{/ts}" class="crm-event-feed-link"><i class="crm-i fa-lg fa-calendar"></i></a>
-  <a href="{$rssFeed}"  target="_blank" title="{ts}Get RSS 2.0 feed for current and future public events.{/ts}" class="crm-event-feed-link"><i class="crm-i fa-lg fa-rss"></i></a>
-  <a href="{$icalFile}" title="{ts}Download iCalendar file for current and future public events.{/ts}" class="crm-event-feed-link"><i class="crm-i fa-lg fa-download"></i></a>
-  <a href="{$icalFeed}"  target="_blank" title="{ts}Get iCalendar feed for current and future public events.{/ts}" class="crm-event-feed-link"><i class="crm-i fa-lg fa-calendar-o"></i></a>
-  {help id='icalendar'}
+  {include file="CRM/Event/Page/iCalLinks.tpl"}
 </div>
 
 <div class="action-link">
   <a accesskey="N" href="{$newEventURL}" id="newManageEvent" class="button crm-popup">
-    <span><i class="crm-i fa-calendar-plus-o"></i> {ts}Add Event{/ts}</span>
+    <span><i class="crm-i fa-calendar-plus-o" aria-hidden="true"></i> {ts}Add Event{/ts}</span>
   </a>
   <div class="clear"></div>
 </div>
diff --git a/civicrm/templates/CRM/Event/Page/Tab.tpl b/civicrm/templates/CRM/Event/Page/Tab.tpl
index d1807ecb375d21674d920c0ed489f23792ea6833..a85621d558f0e5a091d1558faeccb3f11e2c58e4 100644
--- a/civicrm/templates/CRM/Event/Page/Tab.tpl
+++ b/civicrm/templates/CRM/Event/Page/Tab.tpl
@@ -28,9 +28,9 @@
     </div>
     {if $action eq 16 and $permission EQ 'edit'}
        <div class="action-link">
-           <a accesskey="N" href="{$newEventURL}" class="button"><span><i class="crm-i fa-plus-circle"></i> {ts}Add Event Registration{/ts}</span></a>
+           <a accesskey="N" href="{$newEventURL}" class="button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Add Event Registration{/ts}</span></a>
             {if $accessContribution and $newCredit}
-                <a accesskey="N" href="{$newCreditURL}" class="button"><span><i class="crm-i fa-credit-card"></i> {ts}Submit Credit Card Event Registration{/ts}</a></span>
+                <a accesskey="N" href="{$newCreditURL}" class="button"><span><i class="crm-i fa-credit-card" aria-hidden="true"></i> {ts}Submit Credit Card Event Registration{/ts}</a></span>
             {/if}
             <br/ ><br/ >
        </div>
diff --git a/civicrm/templates/CRM/Event/Page/iCalLinks.tpl b/civicrm/templates/CRM/Event/Page/iCalLinks.tpl
index 610f98a91d70a8ca90919bacda729db26855b094..b122b063783dbaab5084fafaa7a1e98e04cda97e 100644
--- a/civicrm/templates/CRM/Event/Page/iCalLinks.tpl
+++ b/civicrm/templates/CRM/Event/Page/iCalLinks.tpl
@@ -7,9 +7,10 @@
  | and copyright information, see https://civicrm.org/licensing       |
  +--------------------------------------------------------------------+
 *}
-{* Display icons / links for ical download and feed for EventInfo.tpl and ThankYou.tpl *}
-{capture assign=icalFile}{crmURL p='civicrm/event/ical' q="reset=1&id=`$event.id`" fe=1 a=1}{/capture}
-{capture assign=icalFeed}{crmURL p='civicrm/event/ical' q="reset=1&list=1&id=`$event.id`" fe=1 a=1}{/capture}
-<div class="action-link section iCal_links-section">
-    <a href="{$icalFile}" title="{ts}Download iCalendar entry for this event.{/ts}"><img src="{$config->resourceBase}i/office-calendar.png" alt="{ts}Download iCalendar entry for this event.{/ts}"></a>&nbsp;&nbsp;<a href="{$icalFeed}" title="{ts}iCalendar feed for this event.{/ts}"><img src="{$config->resourceBase}i/ical_feed.gif" alt="{ts}iCalendar feed for this event.{/ts}" style="margin-bottom: 4px;" /></a>
-</div>
+{* Display icons / links for ical download and feed for EventInfo.tpl, ThankYou.tpl, DashBoard.tpl, and ManageEvent.tpl *}
+  {foreach from=$iCal item="iCalItem"}
+  <a href="{$iCalItem.url}" title="{$iCalItem.text}"{if !$event} class="crm-event-feed-link"{/if}>
+    <span class="fa-stack" aria-hidden="true"><i class="crm-i fa-calendar-o fa-stack-2x"></i><i style="top: 15%;" class="crm-i {$iCalItem.icon} fa-stack-1x"></i></span>
+    <span class="sr-only">{$iCalItem.text}</span>
+  </a>
+  {/foreach}
diff --git a/civicrm/templates/CRM/Financial/Form/Search.tpl b/civicrm/templates/CRM/Financial/Form/Search.tpl
index 6839b0d0f19318dd3147d4ad59f4d84d52e697b5..3cc7b90473e29a9a712d6d77670bf018148f573e 100644
--- a/civicrm/templates/CRM/Financial/Form/Search.tpl
+++ b/civicrm/templates/CRM/Financial/Form/Search.tpl
@@ -11,7 +11,7 @@
 {* Financial search component. *}
 <div id="enableDisableStatusMsg" class="crm-container" style="display:none"></div>
 <div class="action-link">
-  <a accesskey="N" href="{crmURL p='civicrm/financial/batch' q="reset=1&action=add&context=$batchStatus"}" id="newBatch" class="button"><span><i class="crm-i fa-plus-circle"></i> {ts}New Accounting Batch{/ts}</span></a>
+  <a accesskey="N" href="{crmURL p='civicrm/financial/batch' q="reset=1&action=add&context=$batchStatus"}" id="newBatch" class="button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}New Accounting Batch{/ts}</span></a>
 </div>
 <div class="crm-form-block crm-search-form-block">
   <div class="crm-accordion-wrapper crm-activity_search-accordion">
diff --git a/civicrm/templates/CRM/Financial/Page/FinancialAccount.tpl b/civicrm/templates/CRM/Financial/Page/FinancialAccount.tpl
index 264ab9ff45b0139980ca38ecaa952176f7294641..711003614e412ec271a9f225603afbaaf6b01d59 100644
--- a/civicrm/templates/CRM/Financial/Page/FinancialAccount.tpl
+++ b/civicrm/templates/CRM/Financial/Page/FinancialAccount.tpl
@@ -20,7 +20,7 @@
 <div class="crm-content-block crm-block">
   {if $action ne 1 and $action ne 2}
     <div class="action-link">
-      <a href="{crmURL q="action=add&reset=1"}" id="newFinancialAccount-top" class="button"><span><i class="crm-i fa-plus-circle"></i> {ts}Add Financial Account{/ts}</span></a>
+      <a href="{crmURL q="action=add&reset=1"}" id="newFinancialAccount-top" class="button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Add Financial Account{/ts}</span></a>
     </div>
   {/if}
 
@@ -52,7 +52,7 @@
           <td>{$row.financial_account_type_id}{if $row.account_type_code} ({$row.account_type_code}){/if}</td>
           <td>{if $row.is_deductible eq 1} {ts}Yes{/ts} {else} {ts}No{/ts} {/if}</td>
           <td>{if $row.is_reserved eq 1} {ts}Yes{/ts} {else} {ts}No{/ts} {/if}</td>
-          <td>{if $row.is_default eq 1}<img src="{$config->resourceBase}i/check.gif" alt="{ts}Default{/ts}" /> {/if}</td>
+          <td>{icon condition=$row.is_default}{ts}Default{/ts}{/icon}</td>
           <td id="row_{$row.id}_status">{if $row.is_active eq 1} {ts}Yes{/ts} {else} {ts}No{/ts} {/if}</td>
           <td>{$row.action|replace:'xx':$row.id}</td>
         </tr>
@@ -62,7 +62,7 @@
 
       {if $action ne 1 and $action ne 2}
         <div class="action-link">
-          <a href="{crmURL q="action=add&reset=1"}" id="newFinancialAccount-bottom" class="button"><span><i class="crm-i fa-plus-circle"></i> {ts}Add Financial Account{/ts}</span></a>
+          <a href="{crmURL q="action=add&reset=1"}" id="newFinancialAccount-bottom" class="button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Add Financial Account{/ts}</span></a>
         </div>
       {/if}
       </div>
diff --git a/civicrm/templates/CRM/Form/attachment.tpl b/civicrm/templates/CRM/Form/attachment.tpl
index bbe6bf8a23f4e120e8fa660138d522c023de0bc4..687751331dbc34df5567909118efab6c6e3ad390 100644
--- a/civicrm/templates/CRM/Form/attachment.tpl
+++ b/civicrm/templates/CRM/Form/attachment.tpl
@@ -15,7 +15,7 @@
           {foreach from=$currentAttachmentInfo key=attKey item=attVal}
                 <div id="attachStatusMesg" class="status hiddenElement"></div>
                 <div id="attachFileRecord_{$attVal.fileID}">
-                  <strong><a href="{$attVal.url}"><i class="crm-i {$attVal.icon}"></i> {$attVal.cleanName}</a></strong>
+                  <strong><a href="{$attVal.url}"><i class="crm-i {$attVal.icon}" aria-hidden="true"></i> {$attVal.cleanName}</a></strong>
                   {if $attVal.description}&nbsp;-&nbsp;{$attVal.description}{/if}
                   {if !empty($attVal.tag)}
                     <br />
@@ -47,7 +47,7 @@
         {/if}
         <tr>
           <td class="label">{$form.attachFile_1.label}</td>
-          <td>{$form.attachFile_1.html}&nbsp;{$form.attachDesc_1.html}<a href="#" class="crm-hover-button crm-clear-attachment" style="visibility: hidden;" title="{ts}Clear{/ts}"><i class="crm-i fa-times"></i></a>
+          <td>{$form.attachFile_1.html}&nbsp;{$form.attachDesc_1.html}<a href="#" class="crm-hover-button crm-clear-attachment" style="visibility: hidden;" title="{ts}Clear{/ts}"><i class="crm-i fa-times" aria-hidden="true"></i></a>
             <div class="description">{ts}Browse to the <strong>file</strong> you want to upload.{/ts}{if $maxAttachments GT 1} {ts 1=$maxAttachments}You can have a maximum of %1 attachment(s).{/ts}{/if} {ts 1=$config->maxFileSize}Each file must be less than %1M in size. You can also add a short description.{/ts}</div>
           </td>
         </tr>
@@ -68,7 +68,7 @@
             <tr class="attachment-fieldset solid-border-top"><td colspan="2"></td></tr>
             <tr>
                 <td class="label">{$form.attachFile_1.label}</td>
-                <td>{$form.$attachName.html}&nbsp;{$form.$attachDesc.html}<a href="#" class="crm-hover-button crm-clear-attachment" style="visibility: hidden;" title="{ts}Clear{/ts}"><i class="crm-i fa-times"></i></a></td>
+                <td>{$form.$attachName.html}&nbsp;{$form.$attachDesc.html}<a href="#" class="crm-hover-button crm-clear-attachment" style="visibility: hidden;" title="{ts}Clear{/ts}"><i class="crm-i fa-times" aria-hidden="true"></i></a></td>
             </tr>
             <tr>
               <td class="label">{$form.$tagElement.label}</td>
diff --git a/civicrm/templates/CRM/Form/body.tpl b/civicrm/templates/CRM/Form/body.tpl
index 07193943f8e6e3851146e478b43c64d2303daf02..40162fb0096265f64ba8e184f2130574524cc675 100644
--- a/civicrm/templates/CRM/Form/body.tpl
+++ b/civicrm/templates/CRM/Form/body.tpl
@@ -17,7 +17,7 @@
 
 {if ($snippet !== 'json') and !$suppressForm and count($form.errors) gt 0}
    <div class="messages crm-error">
-       <i class="crm-i fa-exclamation-triangle crm-i-red"></i>
+       <i class="crm-i fa-exclamation-triangle crm-i-red" aria-hidden="true"></i>
      {ts}Please correct the following errors in the form fields below:{/ts}
      <ul id="errorList">
      {foreach from=$form.errors key=errorName item=error}
diff --git a/civicrm/templates/CRM/Friend/Form.tpl b/civicrm/templates/CRM/Friend/Form.tpl
index 5781f1b06cbe8d94fb879f56dca9aea619e03f01..3adbc782609a02ae27c56a5979e969c0c109ee13 100644
--- a/civicrm/templates/CRM/Friend/Form.tpl
+++ b/civicrm/templates/CRM/Friend/Form.tpl
@@ -16,7 +16,7 @@
     {* Add button for donor to create their own Personal Campaign page *}
     {if $linkText}
    <div class="crm-section create_pcp_link-section">
-        <a href="{$linkTextUrl}" title="{$linkText|escape}" class="button"><span>&raquo; {$linkText}</span></a>
+        <a href="{$linkTextUrl}" title="{$linkText|escape}" class="button"><span><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {$linkText}</span></a>
     </div><br /><br />
     {/if}
 
diff --git a/civicrm/templates/CRM/Grant/Form/GrantView.tpl b/civicrm/templates/CRM/Grant/Form/GrantView.tpl
index 97d1b9a64fdbfdce9eba8f7b8187b200e0453bcb..c30e777d4c2ec1fccc1cdbdb204f481b4ccdf8f7 100644
--- a/civicrm/templates/CRM/Grant/Form/GrantView.tpl
+++ b/civicrm/templates/CRM/Grant/Form/GrantView.tpl
@@ -15,14 +15,14 @@
             {if ( $context eq 'fulltext' || $context eq 'search' ) && $searchKey}
                 {assign var='urlParams' value="reset=1&id=$id&cid=$contactId&action=update&context=$context&key=$searchKey"}
             {/if}
-            <a class="button" href="{crmURL p='civicrm/contact/view/grant' q=$urlParams}" accesskey="e"><span><i class="crm-i fa-pencil"></i> {ts}Edit{/ts}</span></a>
+            <a class="button" href="{crmURL p='civicrm/contact/view/grant' q=$urlParams}" accesskey="e"><span><i class="crm-i fa-pencil" aria-hidden="true"></i> {ts}Edit{/ts}</span></a>
         {/if}
         {if call_user_func(array('CRM_Core_Permission','check'), 'delete in CiviGrant')}
           {assign var='urlParams' value="reset=1&id=$id&cid=$contactId&action=delete&context=$context"}
             {if ( $context eq 'fulltext' || $context eq 'search' ) && $searchKey}
                 {assign var='urlParams' value="reset=1&id=$id&cid=$contactId&action=delete&context=$context&key=$searchKey"}
             {/if}
-            <a class="button" href="{crmURL p='civicrm/contact/view/grant' q=$urlParams}"><span><i class="crm-i fa-trash"></i> {ts}Delete{/ts}</span></a>
+            <a class="button" href="{crmURL p='civicrm/contact/view/grant' q=$urlParams}"><span><i class="crm-i fa-trash" aria-hidden="true"></i> {ts}Delete{/ts}</span></a>
         {/if}
         {include file="CRM/common/formButtons.tpl" location="top"}
     </div>
@@ -52,14 +52,14 @@
           {if ( $context eq 'fulltext' || $context eq 'search' ) && $searchKey}
               {assign var='urlParams' value="reset=1&id=$id&cid=$contactId&action=update&context=$context&key=$searchKey"}
           {/if}
-            <a class="button" href="{crmURL p='civicrm/contact/view/grant' q=$urlParams}" accesskey="e"><span><i class="crm-i fa-pencil"></i> {ts}Edit{/ts}</span></a>
+            <a class="button" href="{crmURL p='civicrm/contact/view/grant' q=$urlParams}" accesskey="e"><span><i class="crm-i fa-pencil" aria-hidden="true"></i> {ts}Edit{/ts}</span></a>
         {/if}
         {if call_user_func(array('CRM_Core_Permission','check'), 'delete in CiviGrant')}
         {assign var='urlParams' value="reset=1&id=$id&cid=$contactId&action=delete&context=$context"}
           {if ( $context eq 'fulltext' || $context eq 'search' ) && $searchKey}
               {assign var='urlParams' value="reset=1&id=$id&cid=$contactId&action=delete&context=$context&key=$searchKey"}
           {/if}
-            <a class="button" href="{crmURL p='civicrm/contact/view/grant' q=$urlParams}"><span><i class="crm-i fa-trash"></i> {ts}Delete{/ts}</span></a>
+            <a class="button" href="{crmURL p='civicrm/contact/view/grant' q=$urlParams}"><span><i class="crm-i fa-trash" aria-hidden="true"></i> {ts}Delete{/ts}</span></a>
         {/if}
         {include file="CRM/common/formButtons.tpl" location="bottom"}
     </div>
diff --git a/civicrm/templates/CRM/Grant/Form/Selector.tpl b/civicrm/templates/CRM/Grant/Form/Selector.tpl
index 4a163ccd13f3244991ea1bc498c8c1c441d1bf02..d3a1311fbe098ae8fcc2d9068fc671c211baf8ff 100644
--- a/civicrm/templates/CRM/Grant/Form/Selector.tpl
+++ b/civicrm/templates/CRM/Grant/Form/Selector.tpl
@@ -56,7 +56,7 @@
 
 {if ($context EQ 'dashboard') AND $pager->_totalItems GT $limit}
   <tr class="even-row">
-    <td colspan="9"><a href="{crmURL p='civicrm/grant/search' q='reset=1&force=1'}">&raquo; {ts}List more Grants{/ts}...</a></td></tr>
+    <td colspan="9"><a href="{crmURL p='civicrm/grant/search' q='reset=1&force=1'}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}List more Grants{/ts}...</a></td></tr>
   </tr>
 {/if}
 </table>
diff --git a/civicrm/templates/CRM/Grant/Page/Tab.tpl b/civicrm/templates/CRM/Grant/Page/Tab.tpl
index 1e517ba1f56244cc577400a3cb0bc89a6bd6a8d6..8ad006617459c11d839729310a1004e34fe7f8bb 100644
--- a/civicrm/templates/CRM/Grant/Page/Tab.tpl
+++ b/civicrm/templates/CRM/Grant/Page/Tab.tpl
@@ -27,7 +27,7 @@
     </div>
 {if $action eq 16 and $permission EQ 'edit'}
             <div class="action-link">
-            <a href="{$newGrantURL}" class="button"><span><i class="crm-i fa-plus-circle"></i> {ts}Add Grant{/ts}</span></a><br/><br/>
+            <a href="{$newGrantURL}" class="button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Add Grant{/ts}</span></a><br/><br/>
             </div>
         {/if}
     {if $rows}
diff --git a/civicrm/templates/CRM/Group/Form/Edit.tpl b/civicrm/templates/CRM/Group/Form/Edit.tpl
index 2598bf54fc567b07b055f257e1f5919bb8c63542..42b82ac93e797d17e181af92cd5b82aee271d61f 100644
--- a/civicrm/templates/CRM/Group/Form/Edit.tpl
+++ b/civicrm/templates/CRM/Group/Form/Edit.tpl
@@ -82,15 +82,15 @@
   <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location="bottom"}</div>
   {if $action neq 1}
     <div class="action-link">
-      <a {$crmURL}>&raquo; {ts}Contacts in this Group{/ts}</a>
+      <a {$crmURL}><i class="crm-i fa-users" aria-hidden="true"></i> {ts}Contacts in this Group{/ts}</a>
       {if $group.saved_search_id}
         <br />
         {if $group.mapping_id}
-          <a class="no-popup" href="{crmURL p="civicrm/contact/search/builder" q="reset=1&ssID=`$group.saved_search_id`"}">&raquo; {ts}Edit Smart Group Criteria{/ts}</a>
+          <a class="no-popup" href="{crmURL p="civicrm/contact/search/builder" q="reset=1&ssID=`$group.saved_search_id`"}"><i class="crm-i fa-pencil" aria-hidden="true"></i> {ts}Edit Smart Group Criteria{/ts}</a>
         {elseif $group.search_custom_id}
-          <a class="no-popup" href="{crmURL p="civicrm/contact/search/custom" q="reset=1&ssID=`$group.saved_search_id`"}">&raquo; {ts}Edit Smart Group Criteria{/ts}</a>
+          <a class="no-popup" href="{crmURL p="civicrm/contact/search/custom" q="reset=1&ssID=`$group.saved_search_id`"}"><i class="crm-i fa-pencil" aria-hidden="true"></i> {ts}Edit Smart Group Criteria{/ts}</a>
         {else}
-          <a class="no-popup" href="{crmURL p="civicrm/contact/search/advanced" q="reset=1&ssID=`$group.saved_search_id`"}">&raquo; {ts}Edit Smart Group Criteria{/ts}</a>
+          <a class="no-popup" href="{crmURL p="civicrm/contact/search/advanced" q="reset=1&ssID=`$group.saved_search_id`"}"><i class="crm-i fa-pencil" aria-hidden="true"></i> {ts}Edit Smart Group Criteria{/ts}</a>
         {/if}
 
       {/if}
diff --git a/civicrm/templates/CRM/Group/Page/Group.tpl b/civicrm/templates/CRM/Group/Page/Group.tpl
index c5eaf2861b9e0d2cc85a0346b5e0c6b51071fe9a..f36bfdc74312e840766b65f88a3f60ff510fb817 100644
--- a/civicrm/templates/CRM/Group/Page/Group.tpl
+++ b/civicrm/templates/CRM/Group/Page/Group.tpl
@@ -10,7 +10,7 @@
 {* Actions: 1=add, 2=edit, browse=16, delete=8 *}
 {if $action ne 1 and $action ne 2 and $action ne 8 and $groupPermission eq 1}
 <div class="crm-submit-buttons">
-    <a accesskey="N" href="{crmURL p='civicrm/group/add' q='reset=1'}" class="newGroup button"><span><i class="crm-i fa-plus-circle"></i> {ts}Add Group{/ts}</span></a><br/>
+    <a accesskey="N" href="{crmURL p='civicrm/group/add' q='reset=1'}" class="newGroup button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Add Group{/ts}</span></a><br/>
 </div>
 {/if} {* action ne add or edit *}
 <div class="crm-block crm-content-block">
@@ -31,7 +31,7 @@
 
 {if $action ne 1 and $action ne 2 and $action ne 8 and $groupPermission eq 1}
 <div class="crm-submit-buttons">
-        <a accesskey="N" href="{crmURL p='civicrm/group/add' q='reset=1'}" class="newGroup button"><span><i class="crm-i fa-plus-circle"></i> {ts}Add Group{/ts}</span></a><br/>
+        <a accesskey="N" href="{crmURL p='civicrm/group/add' q='reset=1'}" class="newGroup button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Add Group{/ts}</span></a><br/>
 </div>
 {/if} {* action ne add or edit *}
 </div>
diff --git a/civicrm/templates/CRM/Logging/ReportDetail.tpl b/civicrm/templates/CRM/Logging/ReportDetail.tpl
index dbe64961a743bcff425e82e022c5137123230368..63b3e7c1a0dd58e3c2eb66d8947f49ed3c045e49 100644
--- a/civicrm/templates/CRM/Logging/ReportDetail.tpl
+++ b/civicrm/templates/CRM/Logging/ReportDetail.tpl
@@ -32,8 +32,8 @@
   {/if}
   {if $layout neq 'overlay'}
   <div class="action-link">
-      <a href="{$backURL}"   class="button"><span><i class="crm-i fa-chevron-left"></i> {ts}Back to Logging Summary{/ts}</span></a>
-      <a href="{$revertURL}" class="button" onclick="return confirm('{$revertConfirm}');"><span><i class="crm-i fa-undo"></i> {ts}Revert These Changes{/ts}</span></a>
+      <a href="{$backURL}"   class="button"><span><i class="crm-i fa-chevron-left" aria-hidden="true"></i> {ts}Back to Logging Summary{/ts}</span></a>
+      <a href="{$revertURL}" class="button" onclick="return confirm('{$revertConfirm}');"><span><i class="crm-i fa-undo" aria-hidden="true"></i> {ts}Revert These Changes{/ts}</span></a>
   </div>
   {/if}
 </div>
diff --git a/civicrm/templates/CRM/Mailing/Form/ForwardMailing.tpl b/civicrm/templates/CRM/Mailing/Form/ForwardMailing.tpl
index a8991164281ccdf0afe320113f8a4529ca429aef..e8e6205d8edec38b29c99ff526b69b66ebe6a9a3 100644
--- a/civicrm/templates/CRM/Mailing/Form/ForwardMailing.tpl
+++ b/civicrm/templates/CRM/Mailing/Form/ForwardMailing.tpl
@@ -21,11 +21,11 @@
 
 </table>
 <div id="comment_show">
-    <a href="#" class="button" onclick="cj('#comment_show').hide(); cj('#comment').show(); document.getElementById('forward_comment').focus(); return false;"><span>&raquo; {ts}Add Comment{/ts}</span></a>
+    <a href="#" class="button" onclick="cj('#comment_show').hide(); cj('#comment').show(); document.getElementById('forward_comment').focus(); return false;"><span><i class="crm-i fa-plus" aria-hidden="true"></i> {ts}Add Comment{/ts}</span></a>
 </div><div class="spacer"></div>
 <div id="comment" style="display:none">
             <table class="form-layout">
-            <tr class="crm-mailing-forward-form-block-forward_comment"><td><a href="#" onclick="cj('#comment').hide(); cj('#comment_show').show(); return false;"><img src="{$config->resourceBase}i/TreeMinus.gif" class="action-icon" alt="{ts}close section{/ts}"/></a>
+            <tr class="crm-mailing-forward-form-block-forward_comment"><td><a href="#" onclick="cj('#comment').hide(); cj('#comment_show').show(); return false;">{icon icon="fa-caret-down action-icon"}{ts}close section{/ts}{/icon}</a>
                 <label>{$form.forward_comment.label}</label></td>
                 <td>{$form.forward_comment.html}<br /><br />
               &nbsp;{$form.html_comment.html}<br /></td>
@@ -35,4 +35,3 @@
 <br />
 <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location="bottom"}</div>
 </div>
-
diff --git a/civicrm/templates/CRM/Mailing/Page/Component.tpl b/civicrm/templates/CRM/Mailing/Page/Component.tpl
index 25ee1557b7be469bffbf828a20d44dca060965dd..ee7b47880d792c864adba077fdcdfd57b3c4d5b9 100644
--- a/civicrm/templates/CRM/Mailing/Page/Component.tpl
+++ b/civicrm/templates/CRM/Mailing/Page/Component.tpl
@@ -35,7 +35,7 @@
            <td>{$row.subject}</td>
            <td>{$row.body_html|escape}</td>
            <td>{$row.body_text|escape}</td>
-           <td>{if $row.is_default eq 1}<img src="{$config->resourceBase}i/check.gif" alt="{ts}Default{/ts}" />{/if}&nbsp;</td>
+           <td>{icon condition=$row.is_default}{ts}Default{/ts}{/icon}&nbsp;</td>
      <td id="row_{$row.id}_status">{if $row.is_active eq 1} {ts}Yes{/ts} {else} {ts}No{/ts} {/if}</td>
            <td>{$row.action|replace:'xx':$row.id}</td>
         </tr>
diff --git a/civicrm/templates/CRM/Mailing/Page/Event.tpl b/civicrm/templates/CRM/Mailing/Page/Event.tpl
index 101689a37e06e1c3cb90804d89aa568cd2965616..7c66c8be0e2fe428a738c63a11c7e57de73db8c5 100644
--- a/civicrm/templates/CRM/Mailing/Page/Event.tpl
+++ b/civicrm/templates/CRM/Mailing/Page/Event.tpl
@@ -46,7 +46,7 @@
 {/if}
 
 <div class="action-link">
-  <a href="{$backUrl}">&raquo; {$backUrlTitle}</a>
+  <a href="{$backUrl}"><i class="crm-i fa-chevron-left" aria-hidden="true"></i> {$backUrlTitle}</a>
 </div>
 
 {include file="CRM/common/pager.tpl" location="bottom"}
diff --git a/civicrm/templates/CRM/Mailing/Page/Report.tpl b/civicrm/templates/CRM/Mailing/Page/Report.tpl
index 3a9b37e59b593d4ca80f1477ada6efbbf6332f7b..703fe253c1428c4b7e66f12c329c56879549a528 100644
--- a/civicrm/templates/CRM/Mailing/Page/Report.tpl
+++ b/civicrm/templates/CRM/Mailing/Page/Report.tpl
@@ -158,7 +158,7 @@
   <td>
     {$report.mailing.body_text|mb_truncate:30|escape|nl2br}
     <br />
-    <strong><a class="crm-popup" href='{$textViewURL}'>&raquo; {ts}View complete message{/ts}</a></strong>
+    <strong><a class="crm-popup" href='{$textViewURL}'><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}View complete message{/ts}</a></strong>
   </td>
 </tr>
 {/if}
@@ -167,7 +167,7 @@
 <tr>
   <td class="label nowrap">{ts}HTML Message{/ts}</td>
   <td>
-    <a class="crm-popup" href='{$htmlViewURL}'>&raquo; {ts}View complete message{/ts}</a>
+    <a class="crm-popup" href='{$htmlViewURL}'><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}View complete message{/ts}</a>
   </td>
 </tr>
 {/if}
@@ -213,10 +213,5 @@
 {/strip}
 </fieldset>
 <div class="action-link">
-    <a href="{$backUrl}" >&raquo; {$backUrlTitle}</a>
+    <a href="{$backUrl}" ><i class="crm-i fa-chevron-left" aria-hidden="true"></i> {$backUrlTitle}</a>
 </div>
-
-
-
-
-
diff --git a/civicrm/templates/CRM/Member/Form/Membership.tpl b/civicrm/templates/CRM/Member/Form/Membership.tpl
index ca9071bb70cedceef9c5ae2bbcdac889ac90e489..36e5d8678f75780173fe9f4a2622ccd4e8460475 100644
--- a/civicrm/templates/CRM/Member/Form/Membership.tpl
+++ b/civicrm/templates/CRM/Member/Form/Membership.tpl
@@ -59,7 +59,7 @@
       {else}
         {capture assign=ccModeLink}{crmURL p='civicrm/contact/view/membership' q="reset=1&action=add&context=standalone&mode=live"}{/capture}
       {/if}
-     <a class="open-inline-noreturn action-item crm-hover-button" href="{$ccModeLink}">&raquo; {ts}submit credit card membership{/ts}</a>
+     <a class="open-inline-noreturn action-item crm-hover-button" href="{$ccModeLink}"><i class="crm-i fa-credit-card" aria-hidden="true"></i> {ts}submit credit card membership{/ts}</a>
     </div>
     {/if}
     <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location="top"}</div>
@@ -532,7 +532,7 @@
 
           alert = CRM.alert(
             // Mixing client-side variables with a translated string in smarty is awkward!
-            ts({/literal}'{ts escape='js' 1='%1' 2='%2' 3='%3' 4='%4'}This contact has an existing %1 membership at %2 with %3 status%4.{/ts}'{literal}, {1:memberorgs[selectedorg].membership_type, 2: org, 3: memberorgs[selectedorg].membership_status, 4: andEndDate})
+            ts({/literal}'{ts escape='js'}This contact has an existing %1 membership at %2 with %3 status%4.{/ts}'{literal}, {1:memberorgs[selectedorg].membership_type, 2: org, 3: memberorgs[selectedorg].membership_status, 4: andEndDate})
               + '<ul><li><a href="' + memberorgs[selectedorg].renewUrl + '">'
               + {/literal}'{ts escape='js'}Renew the existing membership instead{/ts}'
               + '</a></li><li><a href="' + memberorgs[selectedorg].membershipTab + '">'
diff --git a/civicrm/templates/CRM/Member/Form/MembershipView.tpl b/civicrm/templates/CRM/Member/Form/MembershipView.tpl
index 546c295236f6540c4e13bded6da949e592495da8..f6b073d2ff353fe48408314d6244c73dc6c8cb8a 100644
--- a/civicrm/templates/CRM/Member/Form/MembershipView.tpl
+++ b/civicrm/templates/CRM/Member/Form/MembershipView.tpl
@@ -17,7 +17,7 @@
       {if ( $context eq 'fulltext' || $context eq 'search' ) && $searchKey}
       {assign var='urlParams' value="reset=1&id=$id&cid=$contact_id&action=update&context=$context&key=$searchKey"}
       {/if}
-            <a class="button" href="{crmURL p='civicrm/contact/view/membership' q=$urlParams}" accesskey="e" id="crm-membership-edit-button-top"><span><i class="crm-i fa-pencil"></i> {ts}Edit{/ts}</span></a>
+            <a class="button" href="{crmURL p='civicrm/contact/view/membership' q=$urlParams}" accesskey="e" id="crm-membership-edit-button-top"><span><i class="crm-i fa-pencil" aria-hidden="true"></i> {ts}Edit{/ts}</span></a>
         {/if}
         {if ! ($owner_contact_id AND call_user_func(array('CRM_Core_Permission','check'), 'delete in CiviMember'))
           && (call_user_func(array('CRM_Core_Permission', 'check'), "delete contributions of type $financialTypeId") || $noACL)}
@@ -25,7 +25,7 @@
       {if ( $context eq 'fulltext' || $context eq 'search' ) && $searchKey}
       {assign var='urlParams' value="reset=1&id=$id&cid=$contact_id&action=delete&context=$context&key=$searchKey"}
       {/if}
-            <a class="button" href="{crmURL p='civicrm/contact/view/membership' q=$urlParams}" id="crm-membership-delete-button-top"><span><i class="crm-i fa-trash"></i> {ts}Delete{/ts}</span></a>
+            <a class="button" href="{crmURL p='civicrm/contact/view/membership' q=$urlParams}" id="crm-membership-delete-button-top"><span><i class="crm-i fa-trash" aria-hidden="true"></i> {ts}Delete{/ts}</span></a>
         {/if}
         {include file="CRM/common/formButtons.tpl" location="bottom"}
     </div>
@@ -119,7 +119,7 @@
           {if ( $context eq 'fulltext' || $context eq 'search' ) && $searchKey}
             {assign var='urlParams' value="reset=1&id=$id&cid=$contact_id&action=update&context=$context&key=$searchKey"}
           {/if}
-          <a class="button" href="{crmURL p='civicrm/contact/view/membership' q=$urlParams}" accesskey="e" id="crm-membership-edit-button-bottom"><span><i class="crm-i fa-pencil"></i> {ts}Edit{/ts}</span></a>
+          <a class="button" href="{crmURL p='civicrm/contact/view/membership' q=$urlParams}" accesskey="e" id="crm-membership-edit-button-bottom"><span><i class="crm-i fa-pencil" aria-hidden="true"></i> {ts}Edit{/ts}</span></a>
         {/if}
         {if ! ($owner_contact_id AND call_user_func(array('CRM_Core_Permission','check'), 'delete in CiviMember'))
           && (call_user_func(array('CRM_Core_Permission', 'check'), "delete contributions of type $financialTypeId") || $noACL)}
@@ -127,7 +127,7 @@
           {if ( $context eq 'fulltext' || $context eq 'search' ) && $searchKey}
             {assign var='urlParams' value="reset=1&id=$id&cid=$contact_id&action=delete&context=$context&key=$searchKey"}
           {/if}
-          <a class="button" href="{crmURL p='civicrm/contact/view/membership' q=$urlParams}" id="crm-membership-delete-button-bottom"><span><i class="crm-i fa-trash"></i> {ts}Delete{/ts}</span></a>
+          <a class="button" href="{crmURL p='civicrm/contact/view/membership' q=$urlParams}" id="crm-membership-delete-button-bottom"><span><i class="crm-i fa-trash" aria-hidden="true"></i> {ts}Delete{/ts}</span></a>
         {/if}
         {include file="CRM/common/formButtons.tpl" location="bottom"}
     </div>
diff --git a/civicrm/templates/CRM/Member/Form/Selector.tpl b/civicrm/templates/CRM/Member/Form/Selector.tpl
index b9148fae70b9be5d9db6f62842a5662e466d619e..7ddcfc02a6f57ebfc55c0f3d9fd02bf7a7ba6dfb 100644
--- a/civicrm/templates/CRM/Member/Form/Selector.tpl
+++ b/civicrm/templates/CRM/Member/Form/Selector.tpl
@@ -69,12 +69,12 @@
 {* Link to "View all memberships" for Contact Summary selector display *}
 {if ($context EQ 'membership') AND $pager->_totalItems GT $limit}
   <tr class="even-row">
-    <td colspan="7"><a href="{crmURL p='civicrm/contact/view' q="reset=1&force=1&selectedChild=member&cid=$contactId"}">&raquo; {ts}View all memberships for this contact{/ts}...</a></td></tr>
+    <td colspan="7"><a href="{crmURL p='civicrm/contact/view' q="reset=1&force=1&selectedChild=member&cid=$contactId"}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}View all memberships for this contact{/ts}...</a></td></tr>
   </tr>
 {/if}
 {if ($context EQ 'dashboard') AND $pager->_totalItems GT $limit}
   <tr class="even-row">
-    <td colspan="10"><a href="{crmURL p='civicrm/member/search' q='reset=1'}">&raquo; {ts}Find more members{/ts}...</a></td></tr>
+    <td colspan="10"><a href="{crmURL p='civicrm/member/search' q='reset=1'}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Find more members{/ts}...</a></td></tr>
   </tr>
 {/if}
 </table>
diff --git a/civicrm/templates/CRM/Member/Form/Task/Batch.tpl b/civicrm/templates/CRM/Member/Form/Task/Batch.tpl
index b9ddfefe7eab2f69828c74a62e5f537650f386f1..28ed5aa2438a6f7a7533f840991037948e645331 100644
--- a/civicrm/templates/CRM/Member/Form/Task/Batch.tpl
+++ b/civicrm/templates/CRM/Member/Form/Task/Batch.tpl
@@ -22,7 +22,7 @@
            {/foreach}
 
               {foreach from=$fields item=field key=fieldName}
-                <td><img  src="{$config->resourceBase}i/copy.png" alt="{ts 1=$field.title}Click to copy %1 from row one to all rows.{/ts}" fname="{$field.name}" class="action-icon" title="{ts}Click here to copy the value in row one to ALL rows.{/ts}" />{$field.title}</td>
+                <td>{copyIcon name=$field.name title=$field.title}{$field.title}</td>
              {/foreach}
             </tr>
           </thead>
diff --git a/civicrm/templates/CRM/Member/Import/Form/Preview.tpl b/civicrm/templates/CRM/Member/Import/Form/Preview.tpl
index b86ad221aed2db954b6cdb065063716b66eb0b6f..e4465552d57b9c0a168403dfccb02cfcffd3a67c 100644
--- a/civicrm/templates/CRM/Member/Import/Form/Preview.tpl
+++ b/civicrm/templates/CRM/Member/Import/Form/Preview.tpl
@@ -48,7 +48,7 @@
         <td class="data">{$invalidRowCount}</td>
         <td class="explanation">{ts}Rows with invalid data in one or more fields. These rows will be skipped (not imported).{/ts}
             {if $invalidRowCount}
-                <div class="action-link"><a href="{$downloadErrorRecordsUrl}">&raquo; {ts}Download Errors{/ts}</a></div>
+                <div class="action-link"><a href="{$downloadErrorRecordsUrl}"><i class="crm-i fa-download" aria-hidden="true"></i> {ts}Download Errors{/ts}</a></div>
             {/if}
         </td>
     </tr>
@@ -59,7 +59,7 @@
         <td class="data">{$conflictRowCount}</td>
         <td class="explanation">{ts}Rows with conflicting transaction ids within this file. These rows will be skipped (not imported).{/ts}
             {if $conflictRowCount}
-                <div class="action-link"><a href="{$downloadConflictRecordsUrl}">&raquo; {ts}Download Conflicts{/ts}</a></div>
+                <div class="action-link"><a href="{$downloadConflictRecordsUrl}"><i class="crm-i fa-download" aria-hidden="true"></i> {ts}Download Conflicts{/ts}</a></div>
             {/if}
         </td>
     </tr>
diff --git a/civicrm/templates/CRM/Member/Import/Form/Summary.tpl b/civicrm/templates/CRM/Member/Import/Form/Summary.tpl
index f42c29ba8d91dc4046fc2ad49ad842a354d44635..70fd4ccdc171b05758c0c152287b19bc0f49373c 100644
--- a/civicrm/templates/CRM/Member/Import/Form/Summary.tpl
+++ b/civicrm/templates/CRM/Member/Import/Form/Summary.tpl
@@ -68,7 +68,7 @@
         <td class="data">{$invalidRowCount}</td>
         <td class="explanation">{ts}Rows with invalid data in one or more fields. These rows will be skipped (not imported).{/ts}
             {if $invalidRowCount}
-                <div class="action-link"><a href="{$downloadErrorRecordsUrl}">&raquo; {ts}Download Errors{/ts}</a></div>
+                <div class="action-link"><a href="{$downloadErrorRecordsUrl}"><i class="crm-i fa-download" aria-hidden="true"></i> {ts}Download Errors{/ts}</a></div>
             {/if}
         </td>
     </tr>
diff --git a/civicrm/templates/CRM/Member/Page/RecurringContributions.tpl b/civicrm/templates/CRM/Member/Page/RecurringContributions.tpl
index 4d11e7aa1766820d5786e7f9c5939a64a0fb8bc8..326eee90e3d5d600cf273c79b2a85bb4655e6696 100644
--- a/civicrm/templates/CRM/Member/Page/RecurringContributions.tpl
+++ b/civicrm/templates/CRM/Member/Page/RecurringContributions.tpl
@@ -2,5 +2,5 @@
   <div class="solid-border-top">
     <br /><label>{ts 1=$displayName}Recurring Contributions{/ts}</label>
   </div>
-  {include file="CRM/Contribute/Page/ContributionRecur.tpl" action=16}
+  {include file="CRM/Contribute/Page/ContributionRecurSelector.tpl" action=16}
 {/if}
diff --git a/civicrm/templates/CRM/Member/Page/Tab.tpl b/civicrm/templates/CRM/Member/Page/Tab.tpl
index b4dd598370968a32396cadb83f2221ede8b0cbb1..d4abafedf555116edd78e618c773bd88b2aca491 100644
--- a/civicrm/templates/CRM/Member/Page/Tab.tpl
+++ b/civicrm/templates/CRM/Member/Page/Tab.tpl
@@ -34,9 +34,9 @@
         </div>
 
         <div class="action-link">
-            <a accesskey="N" href="{$newURL}" class="button"><span><i class="crm-i fa-plus-circle"></i> {ts}Add Membership{/ts}</span></a>
+            <a accesskey="N" href="{$newURL}" class="button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Add Membership{/ts}</span></a>
             {if $accessContribution and $newCredit}
-                <a accesskey="N" href="{$newCreditURL}" class="button"><span><i class="crm-i fa-credit-card"></i> {ts}Submit Credit Card Membership{/ts}</span></a><br /><br />
+                <a accesskey="N" href="{$newCreditURL}" class="button"><span><i class="crm-i fa-credit-card" aria-hidden="true"></i> {ts}Submit Credit Card Membership{/ts}</span></a><br /><br />
             {else}
                 <br/ ><br/ >
             {/if}
@@ -80,9 +80,9 @@
                 <td class="crm-membership-source">{$activeMember.source}</td>
                 <td class="crm-membership-auto_renew">
                   {if $activeMember.auto_renew eq 1}
-                      <i class="crm-i fa-check" aria-hidden="true" title="{ts}Auto-renew active{/ts}"></i>
+                      {icon icon="fa-check"}{ts}Auto-renew active{/ts}{/icon}
                   {elseif $activeMember.auto_renew eq 2}
-                      <i class="crm-i fa-ban" aria-hidden="true" title="{ts}Auto-renew error{/ts}"></i>
+                      {icon icon="fa-exclamation-triangle"}{ts}Auto-renew error{/ts}{/icon}
                   {/if}
                 </td>
                 <td class="crm-membership-related_count">{$activeMember.related_count}</td>
@@ -130,9 +130,9 @@
                 <td class="crm-membership-source">{$inActiveMember.source}</td>
                 <td class="crm-membership-auto_renew">
                   {if $inActiveMember.auto_renew eq 1}
-                    <i class="crm-i fa-check" aria-hidden="true" title="{ts}Auto-renew active{/ts}"></i>
+                    {icon icon="fa-check"}{ts}Auto-renew active{/ts}{/icon}
                   {elseif $inActiveMember.auto_renew eq 2}
-                    <i class="crm-i fa-ban" aria-hidden="true" title="{ts}Auto-renew error{/ts}"></i>
+                    {icon icon="fa-exclamation-triangle"}{ts}Auto-renew error{/ts}{/icon}
                   {/if}
                 </td>
     <td>{$inActiveMember.action|replace:'xx':$inActiveMember.id}
diff --git a/civicrm/templates/CRM/PCP/Form/PCPAccount.tpl b/civicrm/templates/CRM/PCP/Form/PCPAccount.tpl
index 1ab7341e598b2b197856df8e42a478196db90e7b..fb205cad3fb640b9922df4d7e1665b7c125ec5ae 100644
--- a/civicrm/templates/CRM/PCP/Form/PCPAccount.tpl
+++ b/civicrm/templates/CRM/PCP/Form/PCPAccount.tpl
@@ -20,7 +20,7 @@
 
 {if $profileDisplay}
 <div class="messages status no-popup">
-  <i class="crm-i fa-exclamation-triangle"></i>
+  <i class="crm-i fa-exclamation-triangle" aria-hidden="true"></i>
   <strong>{ts}Profile is not configured with Email address.{/ts}</strong>
 </div>
 {else}
diff --git a/civicrm/templates/CRM/PCP/Page/PCPInfo.tpl b/civicrm/templates/CRM/PCP/Page/PCPInfo.tpl
index 6f006f4eaae9cca87e7c69a2c51db6dcee54b13b..450f685bf560e8f0b65bca4d8e0ad9af6f48ea8d 100644
--- a/civicrm/templates/CRM/PCP/Page/PCPInfo.tpl
+++ b/civicrm/templates/CRM/PCP/Page/PCPInfo.tpl
@@ -25,13 +25,13 @@
     {foreach from = $links key = k item = v}
           <tr>
             <td>
-                <a href="{crmURL p=$v.url q=$v.qs|replace:'%%pcpId%%':$replace.id|replace:'%%pageComponent%%':$replace.pageComponent|replace:'%%pcpBlock%%':$replace.block}" title="{$v.title|escape:'html'}" {if $v.extra}{$v.extra}{/if}><strong>&raquo; {$v.name}</strong></a>
+                <a href="{crmURL p=$v.url q=$v.qs|replace:'%%pcpId%%':$replace.id|replace:'%%pageComponent%%':$replace.pageComponent|replace:'%%pcpBlock%%':$replace.block}" title="{$v.title|escape:'html'}" {if $v.extra}{$v.extra}{/if}><strong><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {$v.name}</strong></a>
        </td>
          <td>&nbsp;<cite>{$hints.$k}</cite></td>
       </tr>
         {/foreach}
        </table>
-     <i class="crm-i fa-lightbulb-o"></i>
+     <i class="crm-i fa-lightbulb-o" aria-hidden="true"></i>
      <strong>{ts}Tip{/ts}</strong> - <span class="description">{ts}You must be logged in to your account to access the editing options above. (If you visit this page without logging in, you will be viewing the page in "live" mode - as your visitors and friends see it.){/ts}</span>
 </div>
 {/if}
diff --git a/civicrm/templates/CRM/Pledge/Form/Selector.tpl b/civicrm/templates/CRM/Pledge/Form/Selector.tpl
index dd6ebc91a9b61d7336ea2f14cfdecbb1a12a87ab..4df4985e81a1a6199bcf7defc8f2c8afa4816182 100644
--- a/civicrm/templates/CRM/Pledge/Form/Selector.tpl
+++ b/civicrm/templates/CRM/Pledge/Form/Selector.tpl
@@ -62,7 +62,7 @@
     {* Dashboard only lists 10 most recent pledges. *}
     {if $context EQ 'dashboard' and $limit and $pager->_totalItems GT $limit }
         <tr class="even-row">
-            <td colspan="10"><a href="{crmURL p='civicrm/pledge/search' q='reset=1'}">&raquo; {ts}Find more pledges{/ts}... </a></td>
+            <td colspan="10"><a href="{crmURL p='civicrm/pledge/search' q='reset=1'}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> {ts}Find more pledges{/ts}... </a></td>
         </tr>
     {/if}
 
diff --git a/civicrm/templates/CRM/Pledge/Page/Payment.tpl b/civicrm/templates/CRM/Pledge/Page/Payment.tpl
index 19350bcabf8fea6e6523768d1437af0ed661157f..0da77a15032f77cf00dfb311d5288f07cbca2dde 100644
--- a/civicrm/templates/CRM/Pledge/Page/Payment.tpl
+++ b/civicrm/templates/CRM/Pledge/Page/Payment.tpl
@@ -31,7 +31,7 @@
     <td>{$row.reminder_date|truncate:10:''|crmDate}</td>
     <td class="right">{if $row.reminder_count}{$row.reminder_count}{/if}</td>
     <td {if ! ($permission EQ 'edit' and ($row.status eq 'Pending' or $row.status eq 'Overdue' or $row.status eq 'Completed')) } colspan="2"{/if} >{$row.label}</td>
-{if $context neq user}
+{if $context neq 'user'}
     {if $permission EQ 'edit' and ($row.status eq 'Pending' or $row.status eq 'Overdue' or $row.status eq 'Completed') }
         <td class="nowrap">
         {if $row.status eq 'Completed'} {* Link to view contribution record for completed payment.*}
diff --git a/civicrm/templates/CRM/Pledge/Page/Tab.tpl b/civicrm/templates/CRM/Pledge/Page/Tab.tpl
index b143f37b67bf992de4cf7d1a5f6fffb81c9e688b..38740278c9f3e8d61a6b1cb6309c9a4e3313b0d3 100644
--- a/civicrm/templates/CRM/Pledge/Page/Tab.tpl
+++ b/civicrm/templates/CRM/Pledge/Page/Tab.tpl
@@ -24,7 +24,7 @@
 
 {if $action eq 16 and $permission EQ 'edit'}
     <div class="action-link">
-       <a accesskey="N" href="{$newContribURL}" class="button"><span><i class="crm-i fa-plus-circle"></i> {ts}Add Pledge{/ts}</a></span>
+       <a accesskey="N" href="{$newContribURL}" class="button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Add Pledge{/ts}</a></span>
        <br/><br/>
     </div>
 {/if}
diff --git a/civicrm/templates/CRM/Price/Form/OptionFields.tpl b/civicrm/templates/CRM/Price/Form/OptionFields.tpl
index 09a2336d54677b7467ea21aa722a83c2f53a6569..80deab4666f75964d0b4ff3a35c96146d8add4de 100644
--- a/civicrm/templates/CRM/Price/Form/OptionFields.tpl
+++ b/civicrm/templates/CRM/Price/Form/OptionFields.tpl
@@ -41,7 +41,7 @@
   <tr id="optionField_{$index}" class="form-item {cycle values="odd-row,even-row"}">
         <td>
         {if $index GT 1}
-            <a onclick="showHideRow({$index}); return false;" name="optionField_{$index}" href="#" class="form-link"><i class="crm-i fa-trash" title="{ts}hide field or section{/ts}"></i></a>
+            <a onclick="showHideRow({$index}); return false;" name="optionField_{$index}" href="#" class="form-link"><i class="crm-i fa-trash" title="{ts}hide field or section{/ts}" aria-hidden="true"></i></a>
         {/if}
         </td>
       <td>
@@ -71,7 +71,7 @@
     {/section}
     </table>
   <div id="optionFieldLink" class="add-remove-link">
-        <a onclick="showHideRow(); return false;" name="optionFieldLink" href="#" class="form-link"><i class="crm-i fa-plus-circle"></i> {ts}add another choice{/ts}</a>
+        <a onclick="showHideRow(); return false;" name="optionFieldLink" href="#" class="form-link"><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}add another choice{/ts}</a>
     </div>
   <div id="additionalOption" class="description">
     {ts}If you need additional options - you can add them after you Save your current entries.{/ts}
diff --git a/civicrm/templates/CRM/Price/Page/Option.tpl b/civicrm/templates/CRM/Price/Page/Option.tpl
index 0b3ddcbe3391aaf74f75634922ef76a143238722..22f82229a419147ca6c31249985a95f4ab93461c 100644
--- a/civicrm/templates/CRM/Price/Page/Option.tpl
+++ b/civicrm/templates/CRM/Price/Page/Option.tpl
@@ -68,7 +68,7 @@
                 <td class="crm-price-option-count">{$row.count}</td>
                 <td class="crm-price-option-max">{$row.max_value}</td>
               {/if}
-              <td class="crm-price-option-is_default">{if $row.is_default}<img src="{$config->resourceBase}i/check.gif" alt="{ts}Default{/ts}" />{/if}</td>
+              <td class="crm-price-option-is_default">{icon condition=$row.is_default}{ts}Default{/ts}{/icon}</td>
               <td class="nowrap crm-price-option-financial-type-id">{$row.financial_type_id}</td>
               <td class="nowrap crm-price-option-order">{$row.weight}</td>
               {if $getTaxDetails}
diff --git a/civicrm/templates/CRM/Profile/Form/Dynamic.tpl b/civicrm/templates/CRM/Profile/Form/Dynamic.tpl
index f97cbd59a1e662d1c5e8d5d1b99e776a4c4db022..206ec5444d68e2f5e1c1861eb0fb18950e6018a8 100644
--- a/civicrm/templates/CRM/Profile/Form/Dynamic.tpl
+++ b/civicrm/templates/CRM/Profile/Form/Dynamic.tpl
@@ -157,7 +157,9 @@
                 &nbsp;{$form.$phone_ext_field.html}
                 {/if}
               {else}
-                {$form.$n.html}
+                {if $field.html_type neq 'File' || ($field.html_type eq 'File' && !$field.is_view)}
+                   {$form.$n.html}
+                {/if}
                 {if $field.html_type eq 'Autocomplete-Select'}
                   {if $field.data_type eq 'ContactReference'}
                     {include file="CRM/Custom/Form/ContactReference.tpl" element_name = $n}
@@ -170,7 +172,9 @@
 
           {if $form.$n.type eq 'file'}
             <div class="crm-section file_displayURL-section file_displayURL{$n}-section"><div class="content">{$customFiles.$n.displayURL}</div></div>
-            <div class="crm-section file_deleteURL-section file_deleteURL{$n}-section"><div class="content">{$customFiles.$n.deleteURL}</div></div>
+            {if !$fields.$n.is_view}
+               <div class="crm-section file_deleteURL-section file_deleteURL{$n}-section"><div class="content">{$customFiles.$n.deleteURL}</div></div>
+            {/if}
           {/if}
         {/if}
 
@@ -207,7 +211,7 @@
         {if $includeCancelButton}
           <a class="button cancel" href="{$cancelURL}">
             <span>
-              <i class="crm-i fa-times"></i>
+              <i class="crm-i fa-times" aria-hidden="true"></i>
               {$cancelButtonText}
             </span>
           </a>
diff --git a/civicrm/templates/CRM/Profile/Page/Listings.tpl b/civicrm/templates/CRM/Profile/Page/Listings.tpl
index a6fc3a7c1e8a41fdaebf92a8535f8f77b78d4320..bd02d534e232b5615cedcc118df4e56e323da41c 100644
--- a/civicrm/templates/CRM/Profile/Page/Listings.tpl
+++ b/civicrm/templates/CRM/Profile/Page/Listings.tpl
@@ -28,7 +28,7 @@
      <div id="search-status">
         {ts}Displaying contacts where:{/ts}
         {include file="CRM/common/displaySearchCriteria.tpl"}
-        {if $mapURL}<a href="{$mapURL}">&raquo; {ts}Map these contacts{/ts}</a>{/if}
+        {if $mapURL}<a href="{$mapURL}"><i class="crm-i fa-map-marker" aria-hidden="true"></i> {ts}Map these contacts{/ts}</a>{/if}
     </div>
     </div>
     {/if}
diff --git a/civicrm/templates/CRM/Profile/Page/MultipleRecordFieldsListing.tpl b/civicrm/templates/CRM/Profile/Page/MultipleRecordFieldsListing.tpl
index 7853199db841af90d99e19a71c033fac124998b7..bd740966b6c2819479df69a42cb52e6437ad0e19 100644
--- a/civicrm/templates/CRM/Profile/Page/MultipleRecordFieldsListing.tpl
+++ b/civicrm/templates/CRM/Profile/Page/MultipleRecordFieldsListing.tpl
@@ -102,10 +102,10 @@
     <div class="action-link">
       {if $pageViewType eq 'customDataView'}
         <br/><a accesskey="N" title="{ts 1=$customGroupTitle}Add %1 Record{/ts}" href="{crmURL p='civicrm/contact/view/cd/edit' q="reset=1&type=$ctype&groupID=$customGroupId&entityID=$contactId&cgcount=$newCgCount&multiRecordDisplay=single&mode=add"}"
-         class="button action-item"><span><i class="crm-i fa-plus-circle"></i> {ts 1=$customGroupTitle}Add %1 Record{/ts}</span></a>
+         class="button action-item"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts 1=$customGroupTitle}Add %1 Record{/ts}</span></a>
       {else}
         <a accesskey="N" href="{crmURL p='civicrm/profile/edit' q="reset=1&id=`$contactId`&multiRecord=add&gid=`$gid`&context=multiProfileDialog&onPopupClose=`$onPopupClose`"}"
-         class="button action-item"><span><i class="crm-i fa-plus-circle"></i> {ts}Add New Record{/ts}</span></a>
+         class="button action-item"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Add New Record{/ts}</span></a>
       {/if}
     </div>
     <br />
diff --git a/civicrm/templates/CRM/Profile/Page/View.tpl b/civicrm/templates/CRM/Profile/Page/View.tpl
index 88cba5ef72ec78b06f08c736caedf26da73f24fc..052bffd02190411565b2dd6af4ea0e7eeb9e0abd 100644
--- a/civicrm/templates/CRM/Profile/Page/View.tpl
+++ b/civicrm/templates/CRM/Profile/Page/View.tpl
@@ -25,10 +25,10 @@
     {/foreach}
     <div class="action-link">
         {if $listingURL}
-            <a href="{$listingURL}">&raquo; {ts}Back to Listings{/ts}</a>&nbsp;&nbsp;&nbsp;&nbsp;
+            <a href="{$listingURL}"><i class="crm-i fa-chevron-left" aria-hidden="true"></i> {ts}Back to Listings{/ts}</a>&nbsp;&nbsp;&nbsp;&nbsp;
         {/if}
         {if $mapURL}
-            <a href="{$mapURL}">&raquo; {ts}Map Primary Address{/ts}</a>
+            <a href="{$mapURL}"><i class="crm-i fa-map-marker" aria-hidden="true"></i> {ts}Map Primary Address{/ts}</a>
         {/if}
     </div>
 {/if}
diff --git a/civicrm/templates/CRM/Report/Form/Actions.tpl b/civicrm/templates/CRM/Report/Form/Actions.tpl
index 7ba2c5f917c5b43e6a490ca732fbd9018e547149..9aee8f669585f0588cf03036c71760727fb3a19a 100644
--- a/civicrm/templates/CRM/Report/Form/Actions.tpl
+++ b/civicrm/templates/CRM/Report/Form/Actions.tpl
@@ -20,7 +20,7 @@
               <tr>
                 {include file="CRM/common/tasks.tpl" location="botton"}
                 {if $instanceUrl}
-                  <td>&nbsp;&nbsp;&raquo;&nbsp;<a href="{$instanceUrl}">{ts}Existing report(s) from this template{/ts}</a></td>
+                  <td>&nbsp;&nbsp;<i class="crm-i fa-chevron-right" aria-hidden="true"></i>&nbsp;<a href="{$instanceUrl}">{ts}Existing report(s) from this template{/ts}</a></td>
                 {/if}
               </tr>
             </table>
@@ -42,7 +42,7 @@
                       (function($) {
                         $('#groups').val('').change(function() {
                           CRM.confirm({
-                            message: ts({/literal}'{ts escape='js' 1='<em>%1</em>'}Add all contacts to %1 group?{/ts}'{literal}, {1: $('option:selected', '#groups').text()})
+                            message: ts({/literal}'{ts escape='js' 1='<em>%1</em>'}Add all contacts to %1 group?{/ts}'{literal}, {1: CRM._.escape($('option:selected', '#groups').text())})
                           })
                             .on({
                               'crmConfirm:yes': function() {
diff --git a/civicrm/templates/CRM/Report/Form/Tabs/OrderBy.tpl b/civicrm/templates/CRM/Report/Form/Tabs/OrderBy.tpl
index 413d66e14e446c6934035d0af99246e8fc265315..f881c58d2ae3d4f993fcea4042acc9bad02f5c31 100644
--- a/civicrm/templates/CRM/Report/Form/Tabs/OrderBy.tpl
+++ b/civicrm/templates/CRM/Report/Form/Tabs/OrderBy.tpl
@@ -24,7 +24,7 @@
         <tr id="optionField_{$index}" class="form-item {cycle values="odd-row,even-row"}">
           <td>
             {if $index GT 1}
-              <a onclick="hideRow({$index}); return false;" name="orderBy_{$index}" href="#" class="form-link"><img src="{$config->resourceBase}i/TreeMinus.gif" class="action-icon" alt="{ts}hide field or section{/ts}"/></a>
+              <a onclick="hideRow({$index}); return false;" name="orderBy_{$index}" href="#" class="form-link">{icon icon="fa-trash"}{ts}remove sort by column{/ts}{/icon}</a>
             {/if}
           </td>
           <td> {$form.order_bys.$index.column.html}</td>
@@ -35,7 +35,7 @@
       {/section}
     </table>
     <div id="optionFieldLink" class="add-remove-link">
-      <a onclick="showHideRow(); return false;" name="optionFieldLink" href="#" class="form-link"><img src="{$config->resourceBase}i/TreePlus.gif" class="action-icon" alt="{ts}show field or section{/ts}"/>{ts}another column{/ts}</a>
+      <a onclick="showHideRow(); return false;" name="optionFieldLink" href="#" class="form-link"><i class="crm-i fa-plus action-icon" aria-hidden="true"></i> {ts}another column{/ts}</a>
     </div>
     <script type="text/javascript">
       var showRows   = new Array({$showBlocks});
diff --git a/civicrm/templates/CRM/Report/Page/InstanceList.tpl b/civicrm/templates/CRM/Report/Page/InstanceList.tpl
index 2da9bdfe97c50f1e9488d3588e888fc6bdf0f606..354edd5444ff91c8cb26eddaa2445ff7425e290e 100644
--- a/civicrm/templates/CRM/Report/Page/InstanceList.tpl
+++ b/civicrm/templates/CRM/Report/Page/InstanceList.tpl
@@ -10,7 +10,7 @@
 {strip}
   <div class="action-link">
     {if $templateUrl}
-      <a href="{$templateUrl}" class="button"><span><i class="crm-i fa-plus-circle"></i> {$newButton}</span></a>
+      <a href="{$templateUrl}" class="button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {$newButton}</span></a>
     {/if}
     {if $reportUrl}
       <a href="{$reportUrl}" class="button"><span>{ts}View All Reports{/ts}</span></a>
@@ -29,7 +29,7 @@
               <table class="report-layout">
                 {foreach from=$rows item=row}
                   <tr id="row_{counter}" class="crm-report-instanceList">
-                    <td class="crm-report-instanceList-title" style="width:35%"><a href="{$row.url}" title="{ts}Run this report{/ts}">&raquo; <strong>{$row.title}</strong></a></td>
+                    <td class="crm-report-instanceList-title" style="width:35%"><a href="{$row.url}" title="{ts}Run this report{/ts}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> <strong>{$row.title}</strong></a></td>
                     <td class="crm-report-instanceList-description">{$row.description}</td>
                     <td>
                     <a href="{$row.viewUrl}" class="action-item crm-hover-button">{ts}View Results{/ts}</a>
@@ -54,7 +54,7 @@
 
     <div class="action-link">
       {if $templateUrl}
-        <a href="{$templateUrl}" class="button"><span><i class="crm-i fa-plus-circle"></i> {$newButton}</span></a>
+        <a href="{$templateUrl}" class="button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {$newButton}</span></a>
       {/if}
       {if $reportUrl}
         <a href="{$reportUrl}" class="button"><span>{ts}View All Reports{/ts}</span></a>
diff --git a/civicrm/templates/CRM/Report/Page/Options.tpl b/civicrm/templates/CRM/Report/Page/Options.tpl
index 42e6aed765e556d864e0048a014604421f1ba648..6cb81d5e180069c8c337d07e5bc33f690b3684dc 100644
--- a/civicrm/templates/CRM/Report/Page/Options.tpl
+++ b/civicrm/templates/CRM/Report/Page/Options.tpl
@@ -12,7 +12,7 @@
 </div>
 {if $action ne 1 and $action ne 2}
   <div class="action-link">
-    <a href="{$newReport}"  id="new"|cat:$GName class="button"><span><i class="crm-i fa-plus-circle"></i> {ts 1=$GName}Register New %1{/ts}</span></a>
+    <a href="{$newReport}"  id="new"|cat:$GName class="button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts 1=$GName}Register New %1{/ts}</span></a>
   </div>
   <div class="spacer"></div>
 {/if}
@@ -58,7 +58,7 @@
 
     {if $action ne 1 and $action ne 2}
       <div class="action-link">
-        <a href="{$newReport}"  id="new"|cat:$GName class="button"><span><i class="crm-i fa-plus-circle"></i> {ts 1=$GName}Register New %1{/ts}</span></a>
+        <a href="{$newReport}"  id="new"|cat:$GName class="button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts 1=$GName}Register New %1{/ts}</span></a>
       </div>
     {/if}
   </div>
diff --git a/civicrm/templates/CRM/Report/Page/TemplateList.tpl b/civicrm/templates/CRM/Report/Page/TemplateList.tpl
index 55eafeb22a6710e0fb9d5fbebb7479e65883db30..497157d07432908dc46cc7300a061f8642a198bb 100644
--- a/civicrm/templates/CRM/Report/Page/TemplateList.tpl
+++ b/civicrm/templates/CRM/Report/Page/TemplateList.tpl
@@ -27,7 +27,7 @@
                 {foreach from=$rows item=row}
                   <tr id="row_{counter}" class="crm-report-templateList">
                     <td class="crm-report-templateList-title" style="width:35%;">
-                      <a href="{$row.url}" title="{ts}Create report from this template{/ts}">&raquo; <strong>{$row.title}</strong></a>
+                      <a href="{$row.url}" title="{ts}Create report from this template{/ts}"><i class="crm-i fa-chevron-right" aria-hidden="true"></i> <strong>{$row.title}</strong></a>
                       {if $row.instanceUrl}
                         <div style="font-size:10px;text-align:right;margin-top:3px;">
                           <a href="{$row.instanceUrl}">{ts}Existing Report(s){/ts}</a>
diff --git a/civicrm/templates/CRM/Tag/Form/Tag.tpl b/civicrm/templates/CRM/Tag/Form/Tag.tpl
index 63066cedb1c752badb0709831c3bd661736f7e50..892ac1a8aa5c9ffb380f26eb1ac2d954fcc1c49d 100644
--- a/civicrm/templates/CRM/Tag/Form/Tag.tpl
+++ b/civicrm/templates/CRM/Tag/Form/Tag.tpl
@@ -87,7 +87,7 @@
       <tr>
         <td>
           <input class="crm-form-text big" name="filter_tag_tree" placeholder="{ts}Filter List{/ts}" allowclear="1"/>
-          <a class="crm-hover-button crm-clear-link" style="visibility:hidden;" title="{ts}Clear{/ts}"><i class="crm-i fa-times"></i></a>
+          <a class="crm-hover-button crm-clear-link" style="visibility:hidden;" title="{ts}Clear{/ts}"><i class="crm-i fa-times" aria-hidden="true"></i></a>
           <div id="tagtree">
             {include file="CRM/Tag/Form/Tagtree.tpl" level=1}
           </div>
diff --git a/civicrm/templates/CRM/Tag/Page/Tag.tpl b/civicrm/templates/CRM/Tag/Page/Tag.tpl
index 4d4702ea92487ee99ff6ad1640072d7e3e18010c..e59f24246a76710c5df727c464d88f4dab7a628a 100644
--- a/civicrm/templates/CRM/Tag/Page/Tag.tpl
+++ b/civicrm/templates/CRM/Tag/Page/Tag.tpl
@@ -20,7 +20,7 @@
   <div id="mainTabContainer">
     <ul>
       <li class="ui-corner-all crm-tab-button" title="{ts}Main Tag List{/ts}">
-        <a href="#tree"><i class="crm-i fa-tags"></i> {ts}Tag Tree{/ts}</a>
+        <a href="#tree"><i class="crm-i fa-tags" aria-hidden="true"></i> {ts}Tag Tree{/ts}</a>
       </li>
       {foreach from=$tagsets item=set}
         <li class="ui-corner-all crm-tab-button {if ($set.is_reserved)}is-reserved{/if}" title="{ts 1=', '|implode:$set.used_for_label}Tag Set for %1{/ts}">
@@ -29,7 +29,7 @@
       {/foreach}
       {if call_user_func(array('CRM_Core_Permission','check'), 'administer Tagsets')}
         <li class="ui-corner-all crm-tab-button" title="{ts}Add Tag Set{/ts}">
-          <a href="#new-tagset"><i class="crm-i fa-plus"></i></a>
+          <a href="#new-tagset"><i class="crm-i fa-plus" aria-hidden="true"></i></a>
         </li>
       {/if}
     </ul>
@@ -38,7 +38,7 @@
         {ts}Organize the tag hierarchy by clicking and dragging. Shift-click to select multiple tags to merge/move/delete.{/ts}
       </div>
       <input class="crm-form-text big" name="filter_tag_tree" placeholder="{ts}Filter List{/ts}" allowclear="1"/>
-      <a class="crm-hover-button crm-clear-link" style="visibility:hidden;" title="{ts}Clear{/ts}"><i class="crm-i fa-times"></i></a>
+      <a class="crm-hover-button crm-clear-link" style="visibility:hidden;" title="{ts}Clear{/ts}"><i class="crm-i fa-times" aria-hidden="true"></i></a>
     </div>
     {foreach from=$tagsets item=set}
       <div id="tagset-{$set.id}">
@@ -463,16 +463,16 @@
   <% {rdelim} %>
   <div class="crm-submit-buttons">
     <a href="{crmURL p="civicrm/tag/edit" q="action=add&parent_id="}<%= tagset || '' %>" class="button crm-popup">
-      <span><i class="crm-i fa-plus"></i>&nbsp; {ts}Add Tag{/ts}</span>
+      <span><i class="crm-i fa-plus" aria-hidden="true"></i>&nbsp; {ts}Add Tag{/ts}</span>
     </a>
     <% if(tagset && adminTagsets) {ldelim} %>
       <a href="{crmURL p="civicrm/tag/edit" q="action=update&id="}<%= tagset %>" class="button crm-popup tagset-action-update">
-        <span><i class="crm-i fa-pencil"></i>&nbsp; {ts}Edit Set{/ts}</span>
+        <span><i class="crm-i fa-pencil" aria-hidden="true"></i>&nbsp; {ts}Edit Set{/ts}</span>
       </a>
     <% {rdelim} %>
     <% if(tagset && !length && adminTagsets && (!is_reserved || adminReserved)) {ldelim} %>
       <a href="{crmURL p="civicrm/tag/edit" q="action=delete&id="}<%= tagset %>" class="button crm-popup small-popup tagset-action-delete">
-        <span><i class="crm-i fa-trash"></i>&nbsp; {ts}Delete Set{/ts}</span>
+        <span><i class="crm-i fa-trash" aria-hidden="true"></i>&nbsp; {ts}Delete Set{/ts}</span>
       </a>
     <% {rdelim} %>
   </div>
@@ -499,7 +499,7 @@
         <span class="tdl">{ts}Used For:{/ts}</span>
         {literal}
           <span class="<% if (!data.is_reserved || adminReserved) { %>crm-editable-enabled used-for-toggle<% } %>">
-            <% if (!data.used_for.length) { %><i class="crm-i fa-pencil crm-editable-placeholder"></i><% } %>
+            <% if (!data.used_for.length) { %><i class="crm-i fa-pencil crm-editable-placeholder" aria-hidden="true"></i><% } %>
             <% _.forEach(data.used_for, function(key, i) { %><%- (i ? ', ' : '') + usedFor[key] %><% }) %>
           </span>
           <span style="display: none">
@@ -514,26 +514,26 @@
       </div>
     <% {rdelim} %>
     <div><span class="tdl">{ts}Usage Count:{/ts}</span> <%= data.usages %></div>
-    <a class="clear-tag-selection" href="#" title="{ts}Clear selection{/ts}"><i class="crm-i fa-ban"></i></a>
+    <a class="clear-tag-selection" href="#" title="{ts}Clear selection{/ts}"><i class="crm-i fa-ban" aria-hidden="true"></i></a>
   </div>
   <div class="crm-submit-buttons">
     <% if(!tagset) {ldelim} %>
       <a href="{crmURL p="civicrm/tag/edit" q="action=add&parent_id="}<%= id %>" class="button crm-popup" title="{ts}Create new tag under this one{/ts}">
-        <span><i class="crm-i fa-plus"></i>&nbsp; {ts}Add Child{/ts}</span>
+        <span><i class="crm-i fa-plus" aria-hidden="true"></i>&nbsp; {ts}Add Child{/ts}</span>
       </a>
     <% {rdelim} %>
     <a href="{crmURL p="civicrm/tag/edit" q="action=add&clone_from="}<%= id %>" class="button crm-popup" title="{ts}Duplicate this tag{/ts}">
-      <span><i class="crm-i fa-copy"></i>&nbsp; {ts}Clone Tag{/ts}</span>
+      <span><i class="crm-i fa-copy" aria-hidden="true"></i>&nbsp; {ts}Clone Tag{/ts}</span>
     </a>
     <% if(!data.is_reserved || adminReserved) {ldelim} %>
       <% if(tagsetCount) {ldelim} %>
         <a href="#move" class="button move-tag-button" title="{ts}Move to a different tagset{/ts}">
-          <span><i class="crm-i fa-share-square-o"></i>&nbsp; {ts}Move Tag{/ts}</span>
+          <span><i class="crm-i fa-share-square-o" aria-hidden="true"></i>&nbsp; {ts}Move Tag{/ts}</span>
         </a>
       <% {rdelim} %>
       <% if(!hasChildren) {ldelim} %>
         <a href="{crmURL p="civicrm/tag/edit" q="action=delete&id="}<%= id %>" class="button crm-popup small-popup">
-          <span><i class="crm-i fa-trash"></i>&nbsp; {ts}Delete{/ts}</span>
+          <span><i class="crm-i fa-trash" aria-hidden="true"></i>&nbsp; {ts}Delete{/ts}</span>
         </a>
       <% {rdelim} %>
     <% {rdelim} %>
@@ -547,20 +547,20 @@
       <p>* {ts 1="<%= reserved %>"}%1 reserved.{/ts}</p>
     <% {rdelim} %>
   <p><span class="tdl">{ts}Total Usage:{/ts}</span> <%= usages %></p>
-  <a class="clear-tag-selection" href="#" title="{ts}Clear selection{/ts}"><i class="crm-i fa-ban"></i></a>
+  <a class="clear-tag-selection" href="#" title="{ts}Clear selection{/ts}"><i class="crm-i fa-ban" aria-hidden="true"></i></a>
   <div class="crm-submit-buttons">
     <% if(!reserved || adminReserved) {ldelim} %>
       <a href="{crmURL p="civicrm/tag/merge" q="id="}<%= items.join() %>" class="button crm-popup small-popup" title="{ts}Combine tags into one{/ts}">
-        <span><i class="crm-i fa-compress"></i>&nbsp; {ts}Merge Tags{/ts}</span>
+        <span><i class="crm-i fa-compress" aria-hidden="true"></i>&nbsp; {ts}Merge Tags{/ts}</span>
       </a>
       <% if(tagsetCount) {ldelim} %>
         <a href="#move" class="button move-tag-button" title="{ts}Move to a different tagset{/ts}">
-          <span><i class="crm-i fa-share-square-o"></i>&nbsp; {ts}Move Tags{/ts}</span>
+          <span><i class="crm-i fa-share-square-o" aria-hidden="true"></i>&nbsp; {ts}Move Tags{/ts}</span>
         </a>
       <% {rdelim} %>
       <% if(!hasChildren) {ldelim} %>
         <a href="{crmURL p="civicrm/tag/edit" q="action=delete&id="}<%= items.join() %>" class="button crm-popup small-popup">
-          <span><i class="crm-i fa-trash"></i>&nbsp; {ts}Delete All{/ts}</span>
+          <span><i class="crm-i fa-trash" aria-hidden="true"></i>&nbsp; {ts}Delete All{/ts}</span>
         </a>
       <% {rdelim} %>
     <% {rdelim} %>
@@ -576,6 +576,6 @@
       <% if(typeof description === 'string' && description.length) {ldelim} %><p><em><%- description %></em></p><% {rdelim} %>
     </div>
     <input class="crm-form-text big" name="filter_tag_tree" placeholder="{ts}Filter List{/ts}" allowclear="1"/>
-    <a class="crm-hover-button crm-clear-link" style="visibility:hidden;" title="{ts}Clear{/ts}"><i class="crm-i fa-times"></i></a>
+    <a class="crm-hover-button crm-clear-link" style="visibility:hidden;" title="{ts}Clear{/ts}"><i class="crm-i fa-times" aria-hidden="true"></i></a>
   </div>
 </script>
diff --git a/civicrm/templates/CRM/UF/Page/Group.tpl b/civicrm/templates/CRM/UF/Page/Group.tpl
index 62469563a99293b4b8e7c8ea97d0035e96c32c39..1eb5ee8ef0a9a609f65ad607f1a9578e417017de 100644
--- a/civicrm/templates/CRM/UF/Page/Group.tpl
+++ b/civicrm/templates/CRM/UF/Page/Group.tpl
@@ -26,7 +26,7 @@
         <a href="#" onclick="html_code.profile.select(); return false;" class="button"><span>Select HTML Code</span></a>
     </div>
     <div class="action-link">
-        &nbsp; <a href="{crmURL p='civicrm/admin/uf/group' q="reset=1"}">&raquo;  {ts}Back to Profile Listings{/ts}</a>
+        &nbsp; <a href="{crmURL p='civicrm/admin/uf/group' q="reset=1"}"><i class="crm-i fa-chevron-left" aria-hidden="true"></i>  {ts}Back to Profile Listings{/ts}</a>
     </div>
     </form>
 
@@ -38,7 +38,7 @@
 <div class="crm-content-block crm-block">
     {if NOT ($action eq 1 or $action eq 2)}
     <div class="crm-submit-buttons">
-        <a href="{crmURL p='civicrm/admin/uf/group/add' q="action=add&reset=1"}" id="newCiviCRMProfile-top" class="button"><span><i class="crm-i fa-plus-circle"></i> {ts}Add Profile{/ts}</span></a>
+        <a href="{crmURL p='civicrm/admin/uf/group/add' q="action=add&reset=1"}" id="newCiviCRMProfile-top" class="button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Add Profile{/ts}</span></a>
     </div>
     {/if}
     {if $rows}
@@ -90,7 +90,7 @@
 
             {if NOT ($action eq 1 or $action eq 2)}
             <div class="crm-submit-buttons">
-                <a href="{crmURL p='civicrm/admin/uf/group/add' q='action=add&reset=1'}" id="newCiviCRMProfile-bottom" class="button"><span><i class="crm-i fa-plus-circle"></i> {ts}Add Profile{/ts}</span></a>
+                <a href="{crmURL p='civicrm/admin/uf/group/add' q='action=add&reset=1'}" id="newCiviCRMProfile-bottom" class="button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Add Profile{/ts}</span></a>
             </div>
             {/if}
             </div>
@@ -135,7 +135,7 @@
 
             {if NOT ($action eq 1 or $action eq 2)}
             <div class="crm-submit-buttons">
-                <a href="{crmURL p='civicrm/admin/uf/group/add' q='action=add&reset=1'}" id="newCiviCRMProfile-bottom" class="button"><span><i class="crm-i fa-plus-circle"></i> {ts}Add Profile{/ts}</span></a>
+                <a href="{crmURL p='civicrm/admin/uf/group/add' q='action=add&reset=1'}" id="newCiviCRMProfile-bottom" class="button"><span><i class="crm-i fa-plus-circle" aria-hidden="true"></i> {ts}Add Profile{/ts}</span></a>
             </div>
             {/if}
             </div>
diff --git a/civicrm/templates/CRM/UF/Page/ProfileTemplates.tpl b/civicrm/templates/CRM/UF/Page/ProfileTemplates.tpl
index b5e8a1b4009caae1da902ddded894cc4d02c763a..c6fdeff238ce8a94ab6880ee056cb3c32926b392 100644
--- a/civicrm/templates/CRM/UF/Page/ProfileTemplates.tpl
+++ b/civicrm/templates/CRM/UF/Page/ProfileTemplates.tpl
@@ -36,7 +36,7 @@
       </div>
       <hr>
       <input type="text" class="crm-form-text" placeholder="{ts}Search Fields{/ts}" />
-      <a class="crm-designer-palette-clear-search crm-hover-button" href="#" style="visibility:hidden" title="{ts}Clear search{/ts}"><i class="crm-i fa-times"></i></a>
+      <a class="crm-designer-palette-clear-search crm-hover-button" href="#" style="visibility:hidden" title="{ts}Clear search{/ts}"><i class="crm-i fa-times" aria-hidden="true"></i></a>
       <div class="crm-designer-palette-controls">
         <a href="#" class="crm-designer-palette-toggle" rel="open_all">{ts}Open All{/ts}</a>&nbsp; |&nbsp;
         <a href="#" class="crm-designer-palette-toggle" rel="close_all">{ts}Close All{/ts}</a>
@@ -65,8 +65,8 @@
  *}
 <script type="text/template" id="field_summary_template">
   <span class="crm-designer-buttons">
-    <a class="crm-i fa-pencil crm-designer-action-settings" title="{ts}Settings{/ts}"></a>
-    <a class="crm-i fa-trash crm-designer-action-remove" title="{ts}Remove{/ts}"></a>
+    <a class="crm-i fa-pencil crm-designer-action-settings" title="{ts}Settings{/ts}" aria-hidden="true"></a>
+    <a class="crm-i fa-trash crm-designer-action-remove" title="{ts}Remove{/ts}" aria-hidden="true"></a>
   </span>
   <div class="description"><%= help_pre %></div>
   <div class="crm-designer-row-label">
@@ -93,7 +93,7 @@
 <script type="text/template" id="form_summary_template">
   <h3><%= title %></h3>
   <div class="crm-designer-buttons">
-    <a class="crm-designer-action-settings crm-i fa-pencil" title="{ts}Settings{/ts}"></a>
+    <a class="crm-designer-action-settings crm-i fa-pencil" title="{ts}Settings{/ts}" aria-hidden="true"></a>
   </div>
 </script>
 
@@ -114,7 +114,7 @@
 <script type="text/template" id="profile_selector_template">
     <div>
         <span class="crm-profile-selector-select"></span>
-        <button type="button" class="crm-profile-selector-preview" title="{ts}Preview{/ts}"><i class="crm-i fa-television"></i> {ts}Preview{/ts}</button>
+        <button type="button" class="crm-profile-selector-preview" title="{ts}Preview{/ts}"><i class="crm-i fa-television" aria-hidden="true"></i> {ts}Preview{/ts}</button>
         {if $perm}
           <button class="crm-profile-selector-edit">{ts}Edit{/ts}</button>
           <button class="crm-profile-selector-copy">{ts}Copy{/ts}</button>
diff --git a/civicrm/templates/CRM/common/CMSPrint.tpl b/civicrm/templates/CRM/common/CMSPrint.tpl
index e5a3823e7995002efa638e8e5579df11e13c71a0..9edb297abaff7a95f7b1414ea561dcf76abbed7b 100644
--- a/civicrm/templates/CRM/common/CMSPrint.tpl
+++ b/civicrm/templates/CRM/common/CMSPrint.tpl
@@ -17,7 +17,7 @@
   <div class="breadcrumb">
     {foreach from=$breadcrumb item=crumb key=key}
       {if $key != 0}
-        &raquo;
+        <i class="crm-i fa-angle-double-right" aria-hidden="true"></i>
       {/if}
       {$crumb}
     {/foreach}
diff --git a/civicrm/templates/CRM/common/civicrm.settings.php.template b/civicrm/templates/CRM/common/civicrm.settings.php.template
index a89b04864a4e807fac1c7da26fdf6de1bf4103e1..6bdab4ec760b77550da0ab950ddeed1c58e4d30e 100644
--- a/civicrm/templates/CRM/common/civicrm.settings.php.template
+++ b/civicrm/templates/CRM/common/civicrm.settings.php.template
@@ -483,6 +483,25 @@ if (CIVICRM_UF === 'UnitTests') {
   if (!defined('CIVICRM_MYSQL_STRICT')) define('CIVICRM_MYSQL_STRICT', true);
 }
 
+/**
+ * Whether to include the hash in config log filenames. Defaults to TRUE.
+ * Disable only if you have configured the logfiles to be outside the docroot
+ * using the civicrm.log path setting.
+ *
+ */
+// if (!defined('CIVICRM_LOG_HASH'))  {
+//   define('CIVICRM_LOG_HASH', FALSE );
+// }
+
+/**
+ * The maximum size a log file may be before it's rotated, in bytes.
+ * Set to 0 to disable rotation (only recommended if you have an
+ * external logrotate configuration).
+ */
+// if (!defined('CIVICRM_LOG_ROTATESIZE')) {
+//   define('CIVICRM_LOG_ROTATESIZE', 0 );
+// }
+
 /**
  *
  * Do not change anything below this line. Keep as is
diff --git a/civicrm/templates/CRM/common/contactFooter.tpl b/civicrm/templates/CRM/common/contactFooter.tpl
index bae700d6b3920a1194d4807b76f88583416b9022..76eaaae07ddb65ef06840053334e3ddc99c54737 100644
--- a/civicrm/templates/CRM/common/contactFooter.tpl
+++ b/civicrm/templates/CRM/common/contactFooter.tpl
@@ -17,7 +17,7 @@
   {if !empty($lastModified)}
     {ts}Last Change by{/ts}: <a href="{crmURL p='civicrm/contact/view' q="action=view&reset=1&cid=`$lastModified.id`"}">{$lastModified.name}</a> ({$lastModified.date|crmDate}) &nbsp;
     {if !empty($changeLog)}
-      <a href="{crmURL p='civicrm/contact/view' q="reset=1&action=browse&selectedChild=log&cid=`$contactId`"}" class="crm-log-view">&raquo; {ts}View Change Log{/ts}</a>
+      <a href="{crmURL p='civicrm/contact/view' q="reset=1&action=browse&selectedChild=log&cid=`$contactId`"}" class="crm-log-view"><i class="crm-i fa-history" aria-hidden="true"></i> {ts}View Change Log{/ts}</a>
     {/if}
   {/if}
   {if !empty($created_date)}<div class="contact-created-date">{ts}Created{/ts}: {$created_date|crmDate}</div>{/if}
diff --git a/civicrm/templates/CRM/common/fatal.tpl b/civicrm/templates/CRM/common/fatal.tpl
index 7a8e82130a8ac0437bf01b09edce13ec92a49f59..870180bb0a22c4b8cf399f5c4884d8963033f0c0 100644
--- a/civicrm/templates/CRM/common/fatal.tpl
+++ b/civicrm/templates/CRM/common/fatal.tpl
@@ -32,7 +32,7 @@
     @import url({$config->resourceBase}bower_components/font-awesome/css/font-awesome.min.css);
   </style>
 {/if}
-<div class="messages status no-popup">  <i class="crm-i fa-exclamation-triangle crm-i-red"></i>
+<div class="messages status no-popup">  <i class="crm-i fa-exclamation-triangle crm-i-red" aria-hidden="true"></i>
  <span class="status-fatal">{ts}Sorry, due to an error, we are unable to fulfill your request at the moment. You may want to contact your administrator or service provider with more details about what action you were performing when this occurred.{/ts}</span>
     <div class="crm-section crm-error-message">{$message|escape}</div>
     {if $error.message && $message != $error.message}
diff --git a/civicrm/templates/CRM/common/formButtons.tpl b/civicrm/templates/CRM/common/formButtons.tpl
index 2a1260011c02bd22c8b8cbbcc511ba3f8d38f77a..92b41f4dff0dae94968c7160b2faea3e8693db64 100644
--- a/civicrm/templates/CRM/common/formButtons.tpl
+++ b/civicrm/templates/CRM/common/formButtons.tpl
@@ -17,7 +17,7 @@
     {else}{assign var="accessKey" value=""}
     {/if}
     {if $linkButton.icon}
-      {capture assign=icon}<i class="crm-i {$linkButton.icon}"></i> {/capture}
+      {capture assign=icon}<i class="crm-i {$linkButton.icon}" aria-hidden="true"></i> {/capture}
     {else}{assign var="icon" value=""}
     {/if}
     {if $linkButton.ref}
@@ -42,7 +42,7 @@
     {capture assign=iconPrefix}{$icon|truncate:3:"":true}{/capture}
     {if $icon && $iconPrefix eq 'fa-'}
       {assign var='buttonClass' value=' crm-i-button'}
-      {capture assign=iconDisp}<i class="crm-i {$icon}"></i>{/capture}
+      {capture assign=iconDisp}<i class="crm-i {$icon}" aria-hidden="true"></i>{/capture}
     {elseif $icon}
       {assign var='buttonClass' value=' crm-icon-button'}
       {capture assign=iconDisp}<span class="crm-button-icon ui-icon-{$icon}"> </span>{/capture}
diff --git a/civicrm/templates/CRM/common/jcalendar.tpl b/civicrm/templates/CRM/common/jcalendar.tpl
index 1e978a51605831ee120c0fc5f89e37313fba7635..64153189bb293f0e3382b1e15474da68f30668ef 100644
--- a/civicrm/templates/CRM/common/jcalendar.tpl
+++ b/civicrm/templates/CRM/common/jcalendar.tpl
@@ -50,7 +50,7 @@
 {/if}
 
 {if $action neq 1028}
-    <a href="#" class="crm-hover-button crm-clear-link" title="{ts}Clear{/ts}"><i class="crm-i fa-times"></i></a>
+    <a href="#" class="crm-hover-button crm-clear-link" title="{ts}Clear{/ts}"><i class="crm-i fa-times" aria-hidden="true"></i></a>
 {/if}
 
 <script type="text/javascript">
diff --git a/civicrm/templates/CRM/common/joomla.tpl b/civicrm/templates/CRM/common/joomla.tpl
index e9d30f4adc74230ed27f69eb6b02c341842ee290..4d05e688f2f414d3fcef0b6f7dc2ac13ae8dbd99 100644
--- a/civicrm/templates/CRM/common/joomla.tpl
+++ b/civicrm/templates/CRM/common/joomla.tpl
@@ -26,7 +26,7 @@
     <div class="breadcrumb">
       {foreach from=$breadcrumb item=crumb key=key}
         {if $key != 0}
-           &raquo;
+           <i class="crm-i fa-angle-double-right" aria-hidden="true"></i>
         {/if}
         <a href="{$crumb.url}">{$crumb.title}</a>
       {/foreach}
diff --git a/civicrm/templates/CRM/common/navigation.js.tpl b/civicrm/templates/CRM/common/navigation.js.tpl
index 25d5b910bda5adb71766af25be9c459af6b095d1..866769185c51e2d61c862b3577e5ac1490019480 100644
--- a/civicrm/templates/CRM/common/navigation.js.tpl
+++ b/civicrm/templates/CRM/common/navigation.js.tpl
@@ -144,10 +144,6 @@ $('#civicrm-menu').ready(function() {
     var $selection = $('.crm-quickSearchField input:checked'),
       label = $selection.parent().text(),
       value = $selection.val();
-    // These fields are not supported by advanced search
-    if (!value || value === 'first_name' || value === 'last_name') {
-      value = 'sort_name';
-    }
     $('#sort_name_navigation').attr({name: value, placeholder: label});
   }
   $('.crm-quickSearchField').click(function() {
diff --git a/civicrm/templates/CRM/common/status.tpl b/civicrm/templates/CRM/common/status.tpl
index c2d81be5a2ec089901846cd5bba592a9032ec4cb..27f5fbc0faa5eb05f5b38d68e5ab7afb46d81bbc 100644
--- a/civicrm/templates/CRM/common/status.tpl
+++ b/civicrm/templates/CRM/common/status.tpl
@@ -13,7 +13,7 @@
   {assign var="status" value=$session->getStatus(true)}
   {foreach name=statLoop item=statItem from=$status}
     {if $urlIsPublic}
-      {assign var="infoType" value="no-popup"}
+      {assign var="infoType" value="no-popup `$statItem.type`"}
     {else}
       {assign var="infoType" value=$statItem.type}
     {/if}
diff --git a/civicrm/templates/CRM/common/success.tpl b/civicrm/templates/CRM/common/success.tpl
index 28fe6435e35589b834c016387994cd61f941ab1f..a1fbfb37023960e0d5969ddf6e34a43cdf150e42 100644
--- a/civicrm/templates/CRM/common/success.tpl
+++ b/civicrm/templates/CRM/common/success.tpl
@@ -30,11 +30,11 @@
             {ts 1=$docLink}This process may change your database structure and values. In case of emergency you may need to revert to a backup. For more detailed information, refer to the %1.{/ts}</p>
         <input type="hidden" name="action" value="begin" />
         <button type="submit" class="crm-button" name="upgrade" onclick="return confirm('{ts escape="js"}Are you sure you are ready to upgrade now?{/ts}');" >
-          <i class="crm-i fa-rocket"></i>
+          <i class="crm-i fa-rocket" aria-hidden="true"></i>
           {ts}Upgrade Now{/ts}
         </button>&nbsp;&nbsp;
         <a class="button cancel crm-form-submit" href="{$cancelURL}">
-          <i class="crm-i fa-times"></i>
+          <i class="crm-i fa-times" aria-hidden="true"></i>
           {ts}Cancel{/ts}
         </a>
     </form>
diff --git a/civicrm/vendor/autoload.php b/civicrm/vendor/autoload.php
index cc9a6cbbfd9de7c2b9a959edd991ba733fe60485..8eb61a144a6842ab39dc943c4e669f90512aaec7 100644
--- a/civicrm/vendor/autoload.php
+++ b/civicrm/vendor/autoload.php
@@ -4,4 +4,4 @@
 
 require_once __DIR__ . '/composer/autoload_real.php';
 
-return ComposerAutoloaderInitb2582f114b1248d5a20a7411671eea43::getLoader();
+return ComposerAutoloaderInitabd80a2472ebcf6a6e6e8989d45a577c::getLoader();
diff --git a/civicrm/vendor/composer/autoload_classmap.php b/civicrm/vendor/composer/autoload_classmap.php
index 885b3807f7f314ebc61b00146a4c97da102dc6d5..00e85ad0837c93deaf8a8225f4f6757ec999aa6d 100644
--- a/civicrm/vendor/composer/autoload_classmap.php
+++ b/civicrm/vendor/composer/autoload_classmap.php
@@ -23,6 +23,7 @@ return array(
     'HTML5_TreeBuilder' => $vendorDir . '/dompdf/dompdf/lib/html5lib/TreeBuilder.php',
     'ICallbackNamed' => $vendorDir . '/electrolinux/phpquery/phpQuery/phpQuery/Callback.php',
     'PDF417' => $vendorDir . '/tecnickcom/tcpdf/include/barcodes/pdf417.php',
+    'PEAR_Exception' => $vendorDir . '/pear/pear_exception/PEAR/Exception.php',
     'PclZip' => $vendorDir . '/pclzip/pclzip/pclzip.lib.php',
     'QRcode' => $vendorDir . '/tecnickcom/tcpdf/include/barcodes/qrcode.php',
     'TCPDF' => $vendorDir . '/tecnickcom/tcpdf/tcpdf.php',
diff --git a/civicrm/vendor/composer/autoload_namespaces.php b/civicrm/vendor/composer/autoload_namespaces.php
index 9d6930636ce21951aa3ab10adb6866a3d0d6cbf2..2fc96bee6aa518d97cc73862de3680930d55d9a8 100644
--- a/civicrm/vendor/composer/autoload_namespaces.php
+++ b/civicrm/vendor/composer/autoload_namespaces.php
@@ -11,7 +11,6 @@ return array(
     'System' => array($vendorDir . '/phpseclib/phpseclib/phpseclib'),
     'Sabberworm\\CSS' => array($vendorDir . '/sabberworm/php-css-parser/lib'),
     'PHPUnit_' => array($baseDir . '/packages'),
-    'PEAR' => array($vendorDir . '/pear/pear_exception'),
     'Net' => array($vendorDir . '/phpseclib/phpseclib/phpseclib', $vendorDir . '/pear/net_socket', $vendorDir . '/pear/net_smtp'),
     'Math' => array($vendorDir . '/phpseclib/phpseclib/phpseclib'),
     'Mail' => array($vendorDir . '/pear/mail'),
diff --git a/civicrm/vendor/composer/autoload_psr4.php b/civicrm/vendor/composer/autoload_psr4.php
index 04aff2ed58df9516afc0bf28558083b10fc06b49..d2f8e023fc7ef826d88df2fec4286892cfda6080 100644
--- a/civicrm/vendor/composer/autoload_psr4.php
+++ b/civicrm/vendor/composer/autoload_psr4.php
@@ -23,6 +23,7 @@ return array(
     'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
     'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
     'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
+    'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
     'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
     'PhpOffice\\PhpWord\\' => array($vendorDir . '/phpoffice/phpword/src/PhpWord'),
     'PhpOffice\\Common\\' => array($vendorDir . '/phpoffice/common/src/Common'),
diff --git a/civicrm/vendor/composer/autoload_real.php b/civicrm/vendor/composer/autoload_real.php
index c4f799abb533e9f44704776f6ebf47c5af495b5a..dcb5fefc09f9006bb7548619d2f495ea9ea8abcb 100644
--- a/civicrm/vendor/composer/autoload_real.php
+++ b/civicrm/vendor/composer/autoload_real.php
@@ -2,7 +2,7 @@
 
 // autoload_real.php @generated by Composer
 
-class ComposerAutoloaderInitb2582f114b1248d5a20a7411671eea43
+class ComposerAutoloaderInitabd80a2472ebcf6a6e6e8989d45a577c
 {
     private static $loader;
 
@@ -19,9 +19,9 @@ class ComposerAutoloaderInitb2582f114b1248d5a20a7411671eea43
             return self::$loader;
         }
 
-        spl_autoload_register(array('ComposerAutoloaderInitb2582f114b1248d5a20a7411671eea43', 'loadClassLoader'), true, true);
+        spl_autoload_register(array('ComposerAutoloaderInitabd80a2472ebcf6a6e6e8989d45a577c', 'loadClassLoader'), true, true);
         self::$loader = $loader = new \Composer\Autoload\ClassLoader();
-        spl_autoload_unregister(array('ComposerAutoloaderInitb2582f114b1248d5a20a7411671eea43', 'loadClassLoader'));
+        spl_autoload_unregister(array('ComposerAutoloaderInitabd80a2472ebcf6a6e6e8989d45a577c', 'loadClassLoader'));
 
         $includePaths = require __DIR__ . '/include_paths.php';
         $includePaths[] = get_include_path();
@@ -31,7 +31,7 @@ class ComposerAutoloaderInitb2582f114b1248d5a20a7411671eea43
         if ($useStaticLoader) {
             require_once __DIR__ . '/autoload_static.php';
 
-            call_user_func(\Composer\Autoload\ComposerStaticInitb2582f114b1248d5a20a7411671eea43::getInitializer($loader));
+            call_user_func(\Composer\Autoload\ComposerStaticInitabd80a2472ebcf6a6e6e8989d45a577c::getInitializer($loader));
         } else {
             $map = require __DIR__ . '/autoload_namespaces.php';
             foreach ($map as $namespace => $path) {
@@ -52,19 +52,19 @@ class ComposerAutoloaderInitb2582f114b1248d5a20a7411671eea43
         $loader->register(true);
 
         if ($useStaticLoader) {
-            $includeFiles = Composer\Autoload\ComposerStaticInitb2582f114b1248d5a20a7411671eea43::$files;
+            $includeFiles = Composer\Autoload\ComposerStaticInitabd80a2472ebcf6a6e6e8989d45a577c::$files;
         } else {
             $includeFiles = require __DIR__ . '/autoload_files.php';
         }
         foreach ($includeFiles as $fileIdentifier => $file) {
-            composerRequireb2582f114b1248d5a20a7411671eea43($fileIdentifier, $file);
+            composerRequireabd80a2472ebcf6a6e6e8989d45a577c($fileIdentifier, $file);
         }
 
         return $loader;
     }
 }
 
-function composerRequireb2582f114b1248d5a20a7411671eea43($fileIdentifier, $file)
+function composerRequireabd80a2472ebcf6a6e6e8989d45a577c($fileIdentifier, $file)
 {
     if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
         require $file;
diff --git a/civicrm/vendor/composer/autoload_static.php b/civicrm/vendor/composer/autoload_static.php
index ea477379058afbed58fc6e8993fa2edf5c7284fe..bad16bf4346834bd101e02bbb53fd6aecfba2c4b 100644
--- a/civicrm/vendor/composer/autoload_static.php
+++ b/civicrm/vendor/composer/autoload_static.php
@@ -4,7 +4,7 @@
 
 namespace Composer\Autoload;
 
-class ComposerStaticInitb2582f114b1248d5a20a7411671eea43
+class ComposerStaticInitabd80a2472ebcf6a6e6e8989d45a577c
 {
     public static $files = array (
         '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
@@ -56,6 +56,7 @@ class ComposerStaticInitb2582f114b1248d5a20a7411671eea43
             'Psr\\SimpleCache\\' => 16,
             'Psr\\Log\\' => 8,
             'Psr\\Http\\Message\\' => 17,
+            'Psr\\Container\\' => 14,
             'Psr\\Cache\\' => 10,
             'PhpOffice\\PhpWord\\' => 18,
             'PhpOffice\\Common\\' => 17,
@@ -163,6 +164,10 @@ class ComposerStaticInitb2582f114b1248d5a20a7411671eea43
         array (
             0 => __DIR__ . '/..' . '/psr/http-message/src',
         ),
+        'Psr\\Container\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/psr/container/src',
+        ),
         'Psr\\Cache\\' => 
         array (
             0 => __DIR__ . '/..' . '/psr/cache/src',
@@ -266,10 +271,6 @@ class ComposerStaticInitb2582f114b1248d5a20a7411671eea43
             array (
                 0 => __DIR__ . '/../..' . '/packages',
             ),
-            'PEAR' => 
-            array (
-                0 => __DIR__ . '/..' . '/pear/pear_exception',
-            ),
         ),
         'N' => 
         array (
@@ -366,6 +367,7 @@ class ComposerStaticInitb2582f114b1248d5a20a7411671eea43
         'HTML5_TreeBuilder' => __DIR__ . '/..' . '/dompdf/dompdf/lib/html5lib/TreeBuilder.php',
         'ICallbackNamed' => __DIR__ . '/..' . '/electrolinux/phpquery/phpQuery/phpQuery/Callback.php',
         'PDF417' => __DIR__ . '/..' . '/tecnickcom/tcpdf/include/barcodes/pdf417.php',
+        'PEAR_Exception' => __DIR__ . '/..' . '/pear/pear_exception/PEAR/Exception.php',
         'PclZip' => __DIR__ . '/..' . '/pclzip/pclzip/pclzip.lib.php',
         'QRcode' => __DIR__ . '/..' . '/tecnickcom/tcpdf/include/barcodes/qrcode.php',
         'TCPDF' => __DIR__ . '/..' . '/tecnickcom/tcpdf/tcpdf.php',
@@ -495,11 +497,11 @@ class ComposerStaticInitb2582f114b1248d5a20a7411671eea43
     public static function getInitializer(ClassLoader $loader)
     {
         return \Closure::bind(function () use ($loader) {
-            $loader->prefixLengthsPsr4 = ComposerStaticInitb2582f114b1248d5a20a7411671eea43::$prefixLengthsPsr4;
-            $loader->prefixDirsPsr4 = ComposerStaticInitb2582f114b1248d5a20a7411671eea43::$prefixDirsPsr4;
-            $loader->prefixesPsr0 = ComposerStaticInitb2582f114b1248d5a20a7411671eea43::$prefixesPsr0;
-            $loader->fallbackDirsPsr0 = ComposerStaticInitb2582f114b1248d5a20a7411671eea43::$fallbackDirsPsr0;
-            $loader->classMap = ComposerStaticInitb2582f114b1248d5a20a7411671eea43::$classMap;
+            $loader->prefixLengthsPsr4 = ComposerStaticInitabd80a2472ebcf6a6e6e8989d45a577c::$prefixLengthsPsr4;
+            $loader->prefixDirsPsr4 = ComposerStaticInitabd80a2472ebcf6a6e6e8989d45a577c::$prefixDirsPsr4;
+            $loader->prefixesPsr0 = ComposerStaticInitabd80a2472ebcf6a6e6e8989d45a577c::$prefixesPsr0;
+            $loader->fallbackDirsPsr0 = ComposerStaticInitabd80a2472ebcf6a6e6e8989d45a577c::$fallbackDirsPsr0;
+            $loader->classMap = ComposerStaticInitabd80a2472ebcf6a6e6e8989d45a577c::$classMap;
 
         }, null, ClassLoader::class);
     }
diff --git a/civicrm/vendor/composer/installed.json b/civicrm/vendor/composer/installed.json
index 3ff584c8a2f7569c0e08830512551b8de68ebb2b..f335899dddfa966b6f6247c3f258d232983d125e 100644
--- a/civicrm/vendor/composer/installed.json
+++ b/civicrm/vendor/composer/installed.json
@@ -917,21 +917,21 @@
     },
     {
         "name": "pear/log",
-        "version": "1.13.1",
-        "version_normalized": "1.13.1.0",
+        "version": "1.13.2",
+        "version_normalized": "1.13.2.0",
         "source": {
             "type": "git",
             "url": "https://github.com/pear/Log.git",
-            "reference": "c4be9ded2353c7c231d4c35cc3da75b209453803"
+            "reference": "d8cde3dba893a36ec561bf6188fdc39f4221c4d3"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/pear/Log/zipball/c4be9ded2353c7c231d4c35cc3da75b209453803",
-            "reference": "c4be9ded2353c7c231d4c35cc3da75b209453803",
+            "url": "https://api.github.com/repos/pear/Log/zipball/d8cde3dba893a36ec561bf6188fdc39f4221c4d3",
+            "reference": "d8cde3dba893a36ec561bf6188fdc39f4221c4d3",
             "shasum": ""
         },
         "require": {
-            "pear/pear_exception": "1.0.0",
+            "pear/pear_exception": "1.0.1",
             "php": ">5.2"
         },
         "require-dev": {
@@ -940,7 +940,7 @@
         "suggest": {
             "pear/db": "Install optionally via your project's composer.json"
         },
-        "time": "2016-04-16T00:49:33+00:00",
+        "time": "2020-06-02T00:04:03+00:00",
         "type": "library",
         "installation-source": "dist",
         "autoload": {
@@ -1205,17 +1205,17 @@
     },
     {
         "name": "pear/pear_exception",
-        "version": "v1.0.0",
-        "version_normalized": "1.0.0.0",
+        "version": "v1.0.1",
+        "version_normalized": "1.0.1.0",
         "source": {
             "type": "git",
             "url": "https://github.com/pear/PEAR_Exception.git",
-            "reference": "8c18719fdae000b690e3912be401c76e406dd13b"
+            "reference": "dbb42a5a0e45f3adcf99babfb2a1ba77b8ac36a7"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/pear/PEAR_Exception/zipball/8c18719fdae000b690e3912be401c76e406dd13b",
-            "reference": "8c18719fdae000b690e3912be401c76e406dd13b",
+            "url": "https://api.github.com/repos/pear/PEAR_Exception/zipball/dbb42a5a0e45f3adcf99babfb2a1ba77b8ac36a7",
+            "reference": "dbb42a5a0e45f3adcf99babfb2a1ba77b8ac36a7",
             "shasum": ""
         },
         "require": {
@@ -1224,7 +1224,7 @@
         "require-dev": {
             "phpunit/phpunit": "*"
         },
-        "time": "2015-02-10T20:07:52+00:00",
+        "time": "2019-12-10T10:24:42+00:00",
         "type": "class",
         "extra": {
             "branch-alias": {
@@ -1233,9 +1233,9 @@
         },
         "installation-source": "dist",
         "autoload": {
-            "psr-0": {
-                "PEAR": ""
-            }
+            "classmap": [
+                "PEAR/"
+            ]
         },
         "notification-url": "https://packagist.org/downloads/",
         "include-path": [
@@ -1712,6 +1712,57 @@
             "psr-6"
         ]
     },
+    {
+        "name": "psr/container",
+        "version": "1.0.0",
+        "version_normalized": "1.0.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/php-fig/container.git",
+            "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+            "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.0"
+        },
+        "time": "2017-02-14T16:28:37+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.0.x-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Psr\\Container\\": "src/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "PHP-FIG",
+                "homepage": "http://www.php-fig.org/"
+            }
+        ],
+        "description": "Common Container Interface (PHP FIG PSR-11)",
+        "homepage": "https://github.com/php-fig/container",
+        "keywords": [
+            "PSR-11",
+            "container",
+            "container-interface",
+            "container-interop",
+            "psr"
+        ]
+    },
     {
         "name": "psr/http-message",
         "version": "1.0.1",
@@ -1912,35 +1963,42 @@
     },
     {
         "name": "symfony/config",
-        "version": "v2.8.50",
-        "version_normalized": "2.8.50.0",
+        "version": "v3.4.40",
+        "version_normalized": "3.4.40.0",
         "source": {
             "type": "git",
             "url": "https://github.com/symfony/config.git",
-            "reference": "7dd5f5040dc04c118d057fb5886563963eb70011"
+            "reference": "3634991bea549e73c45a964c38f30ceeae6ed877"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/config/zipball/7dd5f5040dc04c118d057fb5886563963eb70011",
-            "reference": "7dd5f5040dc04c118d057fb5886563963eb70011",
+            "url": "https://api.github.com/repos/symfony/config/zipball/3634991bea549e73c45a964c38f30ceeae6ed877",
+            "reference": "3634991bea549e73c45a964c38f30ceeae6ed877",
             "shasum": ""
         },
         "require": {
-            "php": ">=5.3.9",
-            "symfony/filesystem": "~2.3|~3.0.0",
+            "php": "^5.5.9|>=7.0.8",
+            "symfony/filesystem": "~2.8|~3.0|~4.0",
             "symfony/polyfill-ctype": "~1.8"
         },
+        "conflict": {
+            "symfony/dependency-injection": "<3.3",
+            "symfony/finder": "<3.3"
+        },
         "require-dev": {
-            "symfony/yaml": "~2.7|~3.0.0"
+            "symfony/dependency-injection": "~3.3|~4.0",
+            "symfony/event-dispatcher": "~3.3|~4.0",
+            "symfony/finder": "~3.3|~4.0",
+            "symfony/yaml": "~3.0|~4.0"
         },
         "suggest": {
             "symfony/yaml": "To use the yaml reference dumper"
         },
-        "time": "2018-11-26T09:38:12+00:00",
+        "time": "2020-04-12T14:33:46+00:00",
         "type": "library",
         "extra": {
             "branch-alias": {
-                "dev-master": "2.8-dev"
+                "dev-master": "3.4-dev"
             }
         },
         "installation-source": "dist",
@@ -1971,41 +2029,49 @@
     },
     {
         "name": "symfony/dependency-injection",
-        "version": "v2.8.50",
-        "version_normalized": "2.8.50.0",
+        "version": "v3.4.40",
+        "version_normalized": "3.4.40.0",
         "source": {
             "type": "git",
             "url": "https://github.com/symfony/dependency-injection.git",
-            "reference": "c306198fee8f872a8f5f031e6e4f6f83086992d8"
+            "reference": "d10ff5503b0b27711087eef4ac7835a752fe42fd"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/c306198fee8f872a8f5f031e6e4f6f83086992d8",
-            "reference": "c306198fee8f872a8f5f031e6e4f6f83086992d8",
+            "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/d10ff5503b0b27711087eef4ac7835a752fe42fd",
+            "reference": "d10ff5503b0b27711087eef4ac7835a752fe42fd",
             "shasum": ""
         },
         "require": {
-            "php": ">=5.3.9"
+            "php": "^5.5.9|>=7.0.8",
+            "psr/container": "^1.0"
         },
         "conflict": {
-            "symfony/expression-language": "<2.6"
+            "symfony/config": "<3.3.7",
+            "symfony/finder": "<3.3",
+            "symfony/proxy-manager-bridge": "<3.4",
+            "symfony/yaml": "<3.4"
+        },
+        "provide": {
+            "psr/container-implementation": "1.0"
         },
         "require-dev": {
-            "symfony/config": "~2.2|~3.0.0",
-            "symfony/expression-language": "~2.6|~3.0.0",
-            "symfony/yaml": "~2.3.42|~2.7.14|~2.8.7|~3.0.7"
+            "symfony/config": "~3.3|~4.0",
+            "symfony/expression-language": "~2.8|~3.0|~4.0",
+            "symfony/yaml": "~3.4|~4.0"
         },
         "suggest": {
             "symfony/config": "",
             "symfony/expression-language": "For using expressions in service container configuration",
+            "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required",
             "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them",
             "symfony/yaml": ""
         },
-        "time": "2019-04-16T11:33:46+00:00",
+        "time": "2020-04-13T09:33:40+00:00",
         "type": "library",
         "extra": {
             "branch-alias": {
-                "dev-master": "2.8-dev"
+                "dev-master": "3.4-dev"
             }
         },
         "installation-source": "dist",
@@ -2036,38 +2102,41 @@
     },
     {
         "name": "symfony/event-dispatcher",
-        "version": "v2.8.50",
-        "version_normalized": "2.8.50.0",
+        "version": "v3.4.40",
+        "version_normalized": "3.4.40.0",
         "source": {
             "type": "git",
             "url": "https://github.com/symfony/event-dispatcher.git",
-            "reference": "a77e974a5fecb4398833b0709210e3d5e334ffb0"
+            "reference": "9d4e22943b73acc1ba50595b7de1a01fe9dbad48"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a77e974a5fecb4398833b0709210e3d5e334ffb0",
-            "reference": "a77e974a5fecb4398833b0709210e3d5e334ffb0",
+            "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9d4e22943b73acc1ba50595b7de1a01fe9dbad48",
+            "reference": "9d4e22943b73acc1ba50595b7de1a01fe9dbad48",
             "shasum": ""
         },
         "require": {
-            "php": ">=5.3.9"
+            "php": "^5.5.9|>=7.0.8"
+        },
+        "conflict": {
+            "symfony/dependency-injection": "<3.3"
         },
         "require-dev": {
             "psr/log": "~1.0",
-            "symfony/config": "^2.0.5|~3.0.0",
-            "symfony/dependency-injection": "~2.6|~3.0.0",
-            "symfony/expression-language": "~2.6|~3.0.0",
-            "symfony/stopwatch": "~2.3|~3.0.0"
+            "symfony/config": "~2.8|~3.0|~4.0",
+            "symfony/dependency-injection": "~3.3|~4.0",
+            "symfony/expression-language": "~2.8|~3.0|~4.0",
+            "symfony/stopwatch": "~2.8|~3.0|~4.0"
         },
         "suggest": {
             "symfony/dependency-injection": "",
             "symfony/http-kernel": ""
         },
-        "time": "2018-11-21T14:20:20+00:00",
+        "time": "2020-03-15T09:38:08+00:00",
         "type": "library",
         "extra": {
             "branch-alias": {
-                "dev-master": "2.8-dev"
+                "dev-master": "3.4-dev"
             }
         },
         "installation-source": "dist",
@@ -2098,28 +2167,28 @@
     },
     {
         "name": "symfony/filesystem",
-        "version": "v2.8.50",
-        "version_normalized": "2.8.50.0",
+        "version": "v3.4.40",
+        "version_normalized": "3.4.40.0",
         "source": {
             "type": "git",
             "url": "https://github.com/symfony/filesystem.git",
-            "reference": "7ae46872dad09dffb7fe1e93a0937097339d0080"
+            "reference": "78a93e5606a19d0fb490afc3c4a9b7ecd86e1515"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/filesystem/zipball/7ae46872dad09dffb7fe1e93a0937097339d0080",
-            "reference": "7ae46872dad09dffb7fe1e93a0937097339d0080",
+            "url": "https://api.github.com/repos/symfony/filesystem/zipball/78a93e5606a19d0fb490afc3c4a9b7ecd86e1515",
+            "reference": "78a93e5606a19d0fb490afc3c4a9b7ecd86e1515",
             "shasum": ""
         },
         "require": {
-            "php": ">=5.3.9",
+            "php": "^5.5.9|>=7.0.8",
             "symfony/polyfill-ctype": "~1.8"
         },
-        "time": "2018-11-11T11:18:13+00:00",
+        "time": "2020-04-12T16:54:01+00:00",
         "type": "library",
         "extra": {
             "branch-alias": {
-                "dev-master": "2.8-dev"
+                "dev-master": "3.4-dev"
             }
         },
         "installation-source": "dist",
@@ -2150,27 +2219,27 @@
     },
     {
         "name": "symfony/finder",
-        "version": "v2.8.50",
-        "version_normalized": "2.8.50.0",
+        "version": "v3.4.40",
+        "version_normalized": "3.4.40.0",
         "source": {
             "type": "git",
             "url": "https://github.com/symfony/finder.git",
-            "reference": "1444eac52273e345d9b95129bf914639305a9ba4"
+            "reference": "5ec813ccafa8164ef21757e8c725d3a57da59200"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/finder/zipball/1444eac52273e345d9b95129bf914639305a9ba4",
-            "reference": "1444eac52273e345d9b95129bf914639305a9ba4",
+            "url": "https://api.github.com/repos/symfony/finder/zipball/5ec813ccafa8164ef21757e8c725d3a57da59200",
+            "reference": "5ec813ccafa8164ef21757e8c725d3a57da59200",
             "shasum": ""
         },
         "require": {
-            "php": ">=5.3.9"
+            "php": "^5.5.9|>=7.0.8"
         },
-        "time": "2018-11-11T11:18:13+00:00",
+        "time": "2020-02-14T07:34:21+00:00",
         "type": "library",
         "extra": {
             "branch-alias": {
-                "dev-master": "2.8-dev"
+                "dev-master": "3.4-dev"
             }
         },
         "installation-source": "dist",
@@ -2201,17 +2270,17 @@
     },
     {
         "name": "symfony/polyfill-ctype",
-        "version": "v1.12.0",
-        "version_normalized": "1.12.0.0",
+        "version": "v1.17.0",
+        "version_normalized": "1.17.0.0",
         "source": {
             "type": "git",
             "url": "https://github.com/symfony/polyfill-ctype.git",
-            "reference": "550ebaac289296ce228a706d0867afc34687e3f4"
+            "reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4",
-            "reference": "550ebaac289296ce228a706d0867afc34687e3f4",
+            "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e94c8b1bbe2bc77507a1056cdb06451c75b427f9",
+            "reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9",
             "shasum": ""
         },
         "require": {
@@ -2220,11 +2289,11 @@
         "suggest": {
             "ext-ctype": "For best performance"
         },
-        "time": "2019-08-06T08:03:45+00:00",
+        "time": "2020-05-12T16:14:59+00:00",
         "type": "library",
         "extra": {
             "branch-alias": {
-                "dev-master": "1.12-dev"
+                "dev-master": "1.17-dev"
             }
         },
         "installation-source": "dist",
@@ -2261,17 +2330,17 @@
     },
     {
         "name": "symfony/polyfill-iconv",
-        "version": "v1.12.0",
-        "version_normalized": "1.12.0.0",
+        "version": "v1.17.0",
+        "version_normalized": "1.17.0.0",
         "source": {
             "type": "git",
             "url": "https://github.com/symfony/polyfill-iconv.git",
-            "reference": "685968b11e61a347c18bf25db32effa478be610f"
+            "reference": "c4de7601eefbf25f9d47190abe07f79fe0a27424"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/685968b11e61a347c18bf25db32effa478be610f",
-            "reference": "685968b11e61a347c18bf25db32effa478be610f",
+            "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/c4de7601eefbf25f9d47190abe07f79fe0a27424",
+            "reference": "c4de7601eefbf25f9d47190abe07f79fe0a27424",
             "shasum": ""
         },
         "require": {
@@ -2280,11 +2349,11 @@
         "suggest": {
             "ext-iconv": "For best performance"
         },
-        "time": "2019-08-06T08:03:45+00:00",
+        "time": "2020-05-12T16:47:27+00:00",
         "type": "library",
         "extra": {
             "branch-alias": {
-                "dev-master": "1.12-dev"
+                "dev-master": "1.17-dev"
             }
         },
         "installation-source": "dist",
@@ -2322,27 +2391,27 @@
     },
     {
         "name": "symfony/process",
-        "version": "v2.8.50",
-        "version_normalized": "2.8.50.0",
+        "version": "v3.4.40",
+        "version_normalized": "3.4.40.0",
         "source": {
             "type": "git",
             "url": "https://github.com/symfony/process.git",
-            "reference": "c3591a09c78639822b0b290d44edb69bf9f05dc8"
+            "reference": "f5104c9dcbc2cfad45d01d5150c1da9836967271"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/process/zipball/c3591a09c78639822b0b290d44edb69bf9f05dc8",
-            "reference": "c3591a09c78639822b0b290d44edb69bf9f05dc8",
+            "url": "https://api.github.com/repos/symfony/process/zipball/f5104c9dcbc2cfad45d01d5150c1da9836967271",
+            "reference": "f5104c9dcbc2cfad45d01d5150c1da9836967271",
             "shasum": ""
         },
         "require": {
-            "php": ">=5.3.9"
+            "php": "^5.5.9|>=7.0.8"
         },
-        "time": "2018-11-11T11:18:13+00:00",
+        "time": "2020-04-12T14:33:46+00:00",
         "type": "library",
         "extra": {
             "branch-alias": {
-                "dev-master": "2.8-dev"
+                "dev-master": "3.4-dev"
             }
         },
         "installation-source": "dist",
diff --git a/civicrm/vendor/pear/log/.travis.yml b/civicrm/vendor/pear/log/.travis.yml
index 92ccd8c1bb5d7b7471bad5ed35679f197a4adb31..1aa5a7028718fc1dfd79475b8ad7a18c234d357d 100644
--- a/civicrm/vendor/pear/log/.travis.yml
+++ b/civicrm/vendor/pear/log/.travis.yml
@@ -1,10 +1,20 @@
 language: php
-php:
-  - 5.4
-  - 5.5
-  - 5.6
-  - 7.0
-  - nightly
+matrix:
+  fast_finish: true
+  allow_failures:
+    - php: nightly
+  include:
+  - php: 5.4
+    dist: trusty
+  - php: 5.5
+    dist: trusty
+  - php: 5.6
+  - php: 7.0
+  - php: 7.1
+  - php: 7.2
+  - php: 7.3
+  - php: 7.4
+  - php: nightly
 install:
   - pear install package.xml
 script: pear run-tests tests/
diff --git a/civicrm/vendor/pear/log/composer.json b/civicrm/vendor/pear/log/composer.json
index 87378215a99a01a1cefcdfa35228fd948c43baf9..d7c0db084f22ad3e68abd9c2687a68737392fb64 100644
--- a/civicrm/vendor/pear/log/composer.json
+++ b/civicrm/vendor/pear/log/composer.json
@@ -9,7 +9,7 @@
     "license": "MIT",
     "require": {
         "php": ">5.2",
-        "pear/pear_exception": "1.0.0"
+        "pear/pear_exception": "1.0.1"
     },
     "autoload": {
         "psr-0": {
diff --git a/civicrm/vendor/pear/log/package.xml b/civicrm/vendor/pear/log/package.xml
index 55fca68dbfccabdb6edae02e3a802c87a2da0594..6a84b53bc08bf263dfb40e2011cbc0039e51cbe6 100644
--- a/civicrm/vendor/pear/log/package.xml
+++ b/civicrm/vendor/pear/log/package.xml
@@ -25,10 +25,10 @@
   <email>jan@horde.org</email>
   <active>yes</active>
  </lead>
- <date>2016-04-16</date>
+ <date>2020-06-02</date>
  <time>00:00:00</time>
  <version>
-  <release>1.13.1</release>
+  <release>1.13.2</release>
   <api>1.0.0</api>
  </version>
  <stability>
@@ -37,7 +37,7 @@
  </stability>
  <license uri="http://www.opensource.org/licenses/mit-license.php">MIT License</license>
  <notes>
-Use rand() to make instance identifiers more unique than microtime()'s resolution.
+Bump pear/pear_exception dependency to version to 1.0.1.
  </notes>
  <contents>
   <dir baseinstalldir="/" name="/">
@@ -139,6 +139,21 @@ Use rand() to make instance identifiers more unique than microtime()'s resolutio
  </dependencies>
  <phprelease />
  <changelog>
+  <release>
+   <version>
+    <release>1.13.1</release>
+    <api>1.0.0</api>
+   </version>
+   <stability>
+    <release>stable</release>
+    <api>stable</api>
+   </stability>
+   <date>2016-04-16</date>
+   <license uri="http://www.opensource.org/licenses/mit-license.php">MIT License</license>
+   <notes>
+Use rand() to make instance identifiers more unique than microtime()'s resolution.
+   </notes>
+  </release>
   <release>
    <version>
     <release>1.13.0</release>
diff --git a/civicrm/vendor/pear/log/phpdoc.sh b/civicrm/vendor/pear/log/phpdoc.sh
deleted file mode 100755
index f0d45a45777327ec0200594d47f7c8607a6b1b2e..0000000000000000000000000000000000000000
--- a/civicrm/vendor/pear/log/phpdoc.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-phpdoc -f Log.php -d Log -t docs/api -p -ti "Log Package API" -dn Log -dc Log -ed examples -i CVS/*
diff --git a/civicrm/vendor/pear/pear_exception/composer.json b/civicrm/vendor/pear/pear_exception/composer.json
index ce33ed1c805c22514ca13a5927f26cf32d4ecf44..b923cf7181bd02a987e685988a26185d0c5d4aff 100644
--- a/civicrm/vendor/pear/pear_exception/composer.json
+++ b/civicrm/vendor/pear/pear_exception/composer.json
@@ -21,9 +21,7 @@
         "php": ">=4.4.0"
     },
     "autoload": {
-        "psr-0": {
-            "PEAR": ""
-        }
+        "classmap": ["PEAR/"]
     },
     "extra": {
         "branch-alias": {
diff --git a/civicrm/vendor/psr/container/.gitignore b/civicrm/vendor/psr/container/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..b2395aa05541e1e90c84bdc3ce67115c8b84c385
--- /dev/null
+++ b/civicrm/vendor/psr/container/.gitignore
@@ -0,0 +1,3 @@
+composer.lock
+composer.phar
+/vendor/
diff --git a/civicrm/vendor/psr/container/LICENSE b/civicrm/vendor/psr/container/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..2877a4894ee539cf6ead668a1ea696823d506687
--- /dev/null
+++ b/civicrm/vendor/psr/container/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2013-2016 container-interop
+Copyright (c) 2016 PHP Framework Interoperability Group
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/civicrm/vendor/psr/container/README.md b/civicrm/vendor/psr/container/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..084f6df51b21868be82bfc59ef361a6d803d7d3d
--- /dev/null
+++ b/civicrm/vendor/psr/container/README.md
@@ -0,0 +1,5 @@
+# PSR Container
+
+This repository holds all interfaces/classes/traits related to [PSR-11](https://github.com/container-interop/fig-standards/blob/master/proposed/container.md).
+
+Note that this is not a container implementation of its own. See the specification for more details.
diff --git a/civicrm/vendor/psr/container/composer.json b/civicrm/vendor/psr/container/composer.json
new file mode 100644
index 0000000000000000000000000000000000000000..b8ee01265dee64bc8b911a6eece972353b7db2f3
--- /dev/null
+++ b/civicrm/vendor/psr/container/composer.json
@@ -0,0 +1,27 @@
+{
+    "name": "psr/container",
+    "type": "library",
+    "description": "Common Container Interface (PHP FIG PSR-11)",
+    "keywords": ["psr", "psr-11", "container", "container-interop", "container-interface"],
+    "homepage": "https://github.com/php-fig/container",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "PHP-FIG",
+            "homepage": "http://www.php-fig.org/"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "Psr\\Container\\": "src/"
+        }
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.0.x-dev"
+        }
+    }
+}
diff --git a/civicrm/vendor/psr/container/src/ContainerExceptionInterface.php b/civicrm/vendor/psr/container/src/ContainerExceptionInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..d35c6b4d864d9870f1517f47c2917b9e7e2b9a35
--- /dev/null
+++ b/civicrm/vendor/psr/container/src/ContainerExceptionInterface.php
@@ -0,0 +1,13 @@
+<?php
+/**
+ * @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace Psr\Container;
+
+/**
+ * Base interface representing a generic exception in a container.
+ */
+interface ContainerExceptionInterface
+{
+}
diff --git a/civicrm/vendor/psr/container/src/ContainerInterface.php b/civicrm/vendor/psr/container/src/ContainerInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..c3a7206fa00b0db45bbaa2cf47fa9698d578d363
--- /dev/null
+++ b/civicrm/vendor/psr/container/src/ContainerInterface.php
@@ -0,0 +1,37 @@
+<?php
+/**
+ * @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace Psr\Container;
+
+/**
+ * Describes the interface of a container that exposes methods to read its entries.
+ */
+interface ContainerInterface
+{
+    /**
+     * Finds an entry of the container by its identifier and returns it.
+     *
+     * @param string $id Identifier of the entry to look for.
+     *
+     * @throws NotFoundExceptionInterface  No entry was found for **this** identifier.
+     * @throws ContainerExceptionInterface Error while retrieving the entry.
+     *
+     * @return mixed Entry.
+     */
+    public function get($id);
+
+    /**
+     * Returns true if the container can return an entry for the given identifier.
+     * Returns false otherwise.
+     *
+     * `has($id)` returning true does not mean that `get($id)` will not throw an exception.
+     * It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`.
+     *
+     * @param string $id Identifier of the entry to look for.
+     *
+     * @return bool
+     */
+    public function has($id);
+}
diff --git a/civicrm/vendor/psr/container/src/NotFoundExceptionInterface.php b/civicrm/vendor/psr/container/src/NotFoundExceptionInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..6566704eda1d0f551641d38d7c944bffd7d530d9
--- /dev/null
+++ b/civicrm/vendor/psr/container/src/NotFoundExceptionInterface.php
@@ -0,0 +1,13 @@
+<?php
+/**
+ * @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace Psr\Container;
+
+/**
+ * No entry was found in the container.
+ */
+interface NotFoundExceptionInterface extends ContainerExceptionInterface
+{
+}
diff --git a/civicrm/vendor/symfony/config/CHANGELOG.md b/civicrm/vendor/symfony/config/CHANGELOG.md
index 65b602e71353cb3a6f911f3ccbd6f4b97c5b9051..6cb610c478694e978eb4e2d88ecf663e424835d1 100644
--- a/civicrm/vendor/symfony/config/CHANGELOG.md
+++ b/civicrm/vendor/symfony/config/CHANGELOG.md
@@ -1,6 +1,30 @@
 CHANGELOG
 =========
 
+3.4.0
+-----
+
+ * added `setDeprecated()` method to indicate a deprecated node
+ * added `XmlUtils::parse()` method to parse an XML string
+ * deprecated `ConfigCachePass`
+
+3.3.0
+-----
+
+ * added `ReflectionClassResource` class
+ * added second `$exists` constructor argument to `ClassExistenceResource`
+ * made `ClassExistenceResource` work with interfaces and traits
+ * added `ConfigCachePass` (originally in FrameworkBundle)
+ * added `castToArray()` helper to turn any config value into an array
+
+3.0.0
+-----
+
+ * removed `ReferenceDumper` class
+ * removed the `ResourceInterface::isFresh()` method
+ * removed `BCResourceInterfaceChecker` class
+ * removed `ResourceInterface::getResource()` method
+
 2.8.0
 -----
 
@@ -10,7 +34,7 @@ The edge case of defining just one value for nodes of type Enum is now allowed:
 $rootNode
     ->children()
         ->enumNode('variable')
-            ->values(array('value'))
+            ->values(['value'])
         ->end()
     ->end()
 ;
@@ -20,7 +44,7 @@ Before: `InvalidArgumentException` (variable must contain at least two
 distinct elements).
 After: the code will work as expected and it will restrict the values of the
 `variable` option to just `value`.
- 
+
  * deprecated the `ResourceInterface::isFresh()` method. If you implement custom resource types and they
    can be validated that way, make them implement the new `SelfCheckingResourceInterface`.
  * deprecated the getResource() method in ResourceInterface. You can still call this method
@@ -37,12 +61,12 @@ After: the code will work as expected and it will restrict the values of the
 2.2.0
 -----
 
- * added ArrayNodeDefinition::canBeEnabled() and ArrayNodeDefinition::canBeDisabled()
+ * added `ArrayNodeDefinition::canBeEnabled()` and `ArrayNodeDefinition::canBeDisabled()`
    to ease configuration when some sections are respectively disabled / enabled
    by default.
  * added a `normalizeKeys()` method for array nodes (to avoid key normalization)
  * added numerical type handling for config definitions
- * added convenience methods for optional configuration sections to ArrayNodeDefinition
+ * added convenience methods for optional configuration sections to `ArrayNodeDefinition`
  * added a utils class for XML manipulations
 
 2.1.0
@@ -50,5 +74,5 @@ After: the code will work as expected and it will restrict the values of the
 
  * added a way to add documentation on configuration
  * implemented `Serializable` on resources
- * LoaderResolverInterface is now used instead of LoaderResolver for type
+ * `LoaderResolverInterface` is now used instead of `LoaderResolver` for type
    hinting
diff --git a/civicrm/vendor/symfony/config/ConfigCache.php b/civicrm/vendor/symfony/config/ConfigCache.php
index 016e014b6c516a051d8fa8c0234e94757816c8b4..b2a39076f9d4a70352bc060c07abf516824aba56 100644
--- a/civicrm/vendor/symfony/config/ConfigCache.php
+++ b/civicrm/vendor/symfony/config/ConfigCache.php
@@ -11,7 +11,6 @@
 
 namespace Symfony\Component\Config;
 
-use Symfony\Component\Config\Resource\BCResourceInterfaceChecker;
 use Symfony\Component\Config\Resource\SelfCheckingResourceChecker;
 
 /**
@@ -21,11 +20,6 @@ use Symfony\Component\Config\Resource\SelfCheckingResourceChecker;
  * \Symfony\Component\Config\Resource\SelfCheckingResourceInterface will
  * be used to check cache freshness.
  *
- * During a transition period, also instances of
- * \Symfony\Component\Config\Resource\ResourceInterface will be checked
- * by means of the isFresh() method. This behaviour is deprecated since 2.8
- * and will be removed in 3.0.
- *
  * @author Fabien Potencier <fabien@symfony.com>
  * @author Matthias Pigulla <mp@webfactory.de>
  */
@@ -39,25 +33,14 @@ class ConfigCache extends ResourceCheckerConfigCache
      */
     public function __construct($file, $debug)
     {
-        parent::__construct($file, array(
-            new SelfCheckingResourceChecker(),
-            new BCResourceInterfaceChecker(),
-        ));
         $this->debug = (bool) $debug;
-    }
 
-    /**
-     * Gets the cache file path.
-     *
-     * @return string The cache file path
-     *
-     * @deprecated since 2.7, to be removed in 3.0. Use getPath() instead.
-     */
-    public function __toString()
-    {
-        @trigger_error('ConfigCache::__toString() is deprecated since Symfony 2.7 and will be removed in 3.0. Use the getPath() method instead.', E_USER_DEPRECATED);
+        $checkers = [];
+        if (true === $this->debug) {
+            $checkers = [new SelfCheckingResourceChecker()];
+        }
 
-        return $this->getPath();
+        parent::__construct($file, $checkers);
     }
 
     /**
diff --git a/civicrm/vendor/symfony/config/Definition/ArrayNode.php b/civicrm/vendor/symfony/config/Definition/ArrayNode.php
index 86eacae40b3d3a17ad147af97d8065c10d1d7ee8..83bab205ecfc2eab142cacf1e6ebdf79d1f323fe 100644
--- a/civicrm/vendor/symfony/config/Definition/ArrayNode.php
+++ b/civicrm/vendor/symfony/config/Definition/ArrayNode.php
@@ -22,8 +22,8 @@ use Symfony\Component\Config\Definition\Exception\UnsetKeyException;
  */
 class ArrayNode extends BaseNode implements PrototypeNodeInterface
 {
-    protected $xmlRemappings = array();
-    protected $children = array();
+    protected $xmlRemappings = [];
+    protected $children = [];
     protected $allowFalse = false;
     protected $allowNewKeys = true;
     protected $addIfNotSet = false;
@@ -38,17 +38,13 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
     }
 
     /**
-     * Normalizes keys between the different configuration formats.
+     * {@inheritdoc}
      *
      * Namely, you mostly have foo_bar in YAML while you have foo-bar in XML.
      * After running this method, all keys are normalized to foo_bar.
      *
      * If you have a mixed key like foo-bar_moo, it will not be altered.
      * The key will also not be altered if the target key already exists.
-     *
-     * @param mixed $value
-     *
-     * @return array The value with normalized keys
      */
     protected function preNormalize($value)
     {
@@ -56,10 +52,10 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
             return $value;
         }
 
-        $normalized = array();
+        $normalized = [];
 
         foreach ($value as $k => $v) {
-            if (false !== strpos($k, '-') && false === strpos($k, '_') && !array_key_exists($normalizedKey = str_replace('-', '_', $k), $value)) {
+            if (false !== strpos($k, '-') && false === strpos($k, '_') && !\array_key_exists($normalizedKey = str_replace('-', '_', $k), $value)) {
                 $normalized[$normalizedKey] = $v;
             } else {
                 $normalized[$k] = $v;
@@ -82,7 +78,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
     /**
      * Sets the xml remappings that should be performed.
      *
-     * @param array $remappings An array of the form array(array(string, string))
+     * @param array $remappings An array of the form [[string, string]]
      */
     public function setXmlRemappings(array $remappings)
     {
@@ -92,7 +88,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
     /**
      * Gets the xml remappings that should be performed.
      *
-     * @return array an array of the form array(array(string, string))
+     * @return array an array of the form [[string, string]]
      */
     public function getXmlRemappings()
     {
@@ -141,7 +137,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
     }
 
     /**
-     * Whether extra keys should just be ignore without an exception.
+     * Whether extra keys should just be ignored without an exception.
      *
      * @param bool $boolean To allow extra keys
      * @param bool $remove  To remove extra keys
@@ -177,7 +173,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
             throw new \RuntimeException(sprintf('The node at path "%s" has no default value.', $this->getPath()));
         }
 
-        $defaults = array();
+        $defaults = [];
         foreach ($this->children as $name => $child) {
             if ($child->hasDefaultValue()) {
                 $defaults[$name] = $child->getDefaultValue();
@@ -219,11 +215,11 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
     protected function finalizeValue($value)
     {
         if (false === $value) {
-            throw new UnsetKeyException(sprintf('Unsetting key for path "%s", value: %s', $this->getPath(), json_encode($value)));
+            throw new UnsetKeyException(sprintf('Unsetting key for path "%s", value: "%s".', $this->getPath(), json_encode($value)));
         }
 
         foreach ($this->children as $name => $child) {
-            if (!array_key_exists($name, $value)) {
+            if (!\array_key_exists($name, $value)) {
                 if ($child->isRequired()) {
                     $ex = new InvalidConfigurationException(sprintf('The child node "%s" at path "%s" must be configured.', $name, $this->getPath()));
                     $ex->setPath($this->getPath());
@@ -238,6 +234,10 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
                 continue;
             }
 
+            if ($child->isDeprecated()) {
+                @trigger_error($child->getDeprecationMessage($name, $this->getPath()), E_USER_DEPRECATED);
+            }
+
             try {
                 $value[$name] = $child->finalize($value[$name]);
             } catch (UnsetKeyException $e) {
@@ -285,7 +285,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
 
         $value = $this->remapXml($value);
 
-        $normalized = array();
+        $normalized = [];
         foreach ($value as $name => $val) {
             if (isset($this->children[$name])) {
                 try {
@@ -318,9 +318,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
      */
     protected function remapXml($value)
     {
-        foreach ($this->xmlRemappings as $transformation) {
-            list($singular, $plural) = $transformation;
-
+        foreach ($this->xmlRemappings as list($singular, $plural)) {
             if (!isset($value[$singular])) {
                 continue;
             }
@@ -357,7 +355,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
 
         foreach ($rightSide as $k => $v) {
             // no conflict
-            if (!array_key_exists($k, $leftSide)) {
+            if (!\array_key_exists($k, $leftSide)) {
                 if (!$this->allowNewKeys) {
                     $ex = new InvalidConfigurationException(sprintf('You are not allowed to define new elements for path "%s". Please define all elements for this path in one config file. If you are trying to overwrite an element, make sure you redefine it with the same name.', $this->getPath()));
                     $ex->setPath($this->getPath());
diff --git a/civicrm/vendor/symfony/config/Definition/BaseNode.php b/civicrm/vendor/symfony/config/Definition/BaseNode.php
index 7ca956e2113b0ae3f6ab5901e3615a1dc98e50e8..1f6ef7f834b8cbb3ce2be3687cfe7fb1d4a84b2f 100644
--- a/civicrm/vendor/symfony/config/Definition/BaseNode.php
+++ b/civicrm/vendor/symfony/config/Definition/BaseNode.php
@@ -25,12 +25,13 @@ abstract class BaseNode implements NodeInterface
 {
     protected $name;
     protected $parent;
-    protected $normalizationClosures = array();
-    protected $finalValidationClosures = array();
+    protected $normalizationClosures = [];
+    protected $finalValidationClosures = [];
     protected $allowOverwrite = true;
     protected $required = false;
-    protected $equivalentValues = array();
-    protected $attributes = array();
+    protected $deprecationMessage = null;
+    protected $equivalentValues = [];
+    protected $attributes = [];
 
     /**
      * @param string|null        $name   The name of the node
@@ -48,21 +49,37 @@ abstract class BaseNode implements NodeInterface
         $this->parent = $parent;
     }
 
+    /**
+     * @param string $key
+     */
     public function setAttribute($key, $value)
     {
         $this->attributes[$key] = $value;
     }
 
+    /**
+     * @param string $key
+     *
+     * @return mixed
+     */
     public function getAttribute($key, $default = null)
     {
         return isset($this->attributes[$key]) ? $this->attributes[$key] : $default;
     }
 
+    /**
+     * @param string $key
+     *
+     * @return bool
+     */
     public function hasAttribute($key)
     {
         return isset($this->attributes[$key]);
     }
 
+    /**
+     * @return array
+     */
     public function getAttributes()
     {
         return $this->attributes;
@@ -73,6 +90,9 @@ abstract class BaseNode implements NodeInterface
         $this->attributes = $attributes;
     }
 
+    /**
+     * @param string $key
+     */
     public function removeAttribute($key)
     {
         unset($this->attributes[$key]);
@@ -91,7 +111,7 @@ abstract class BaseNode implements NodeInterface
     /**
      * Returns info message.
      *
-     * @return string The info text
+     * @return string|null The info text
      */
     public function getInfo()
     {
@@ -111,7 +131,7 @@ abstract class BaseNode implements NodeInterface
     /**
      * Retrieves the example configuration for this node.
      *
-     * @return string|array The example
+     * @return string|array|null The example
      */
     public function getExample()
     {
@@ -126,7 +146,7 @@ abstract class BaseNode implements NodeInterface
      */
     public function addEquivalentValue($originalValue, $equivalentValue)
     {
-        $this->equivalentValues[] = array($originalValue, $equivalentValue);
+        $this->equivalentValues[] = [$originalValue, $equivalentValue];
     }
 
     /**
@@ -139,6 +159,19 @@ abstract class BaseNode implements NodeInterface
         $this->required = (bool) $boolean;
     }
 
+    /**
+     * Sets this node as deprecated.
+     *
+     * You can use %node% and %path% placeholders in your message to display,
+     * respectively, the node name and its complete path.
+     *
+     * @param string|null $message Deprecated message
+     */
+    public function setDeprecated($message)
+    {
+        $this->deprecationMessage = $message;
+    }
+
     /**
      * Sets if this node can be overridden.
      *
@@ -177,6 +210,29 @@ abstract class BaseNode implements NodeInterface
         return $this->required;
     }
 
+    /**
+     * Checks if this node is deprecated.
+     *
+     * @return bool
+     */
+    public function isDeprecated()
+    {
+        return null !== $this->deprecationMessage;
+    }
+
+    /**
+     * Returns the deprecated message.
+     *
+     * @param string $node the configuration node name
+     * @param string $path the path of the node
+     *
+     * @return string
+     */
+    public function getDeprecationMessage($node, $path)
+    {
+        return strtr($this->deprecationMessage, ['%node%' => $node, '%path%' => $path]);
+    }
+
     /**
      * {@inheritdoc}
      */
@@ -243,9 +299,9 @@ abstract class BaseNode implements NodeInterface
     /**
      * Normalizes the value before any other normalization is applied.
      *
-     * @param $value
+     * @param mixed $value
      *
-     * @return The normalized array value
+     * @return mixed The normalized array value
      */
     protected function preNormalize($value)
     {
@@ -279,7 +335,7 @@ abstract class BaseNode implements NodeInterface
             } catch (Exception $e) {
                 throw $e;
             } catch (\Exception $e) {
-                throw new InvalidConfigurationException(sprintf('Invalid configuration for path "%s": %s', $this->getPath(), $e->getMessage()), $e->getCode(), $e);
+                throw new InvalidConfigurationException(sprintf('Invalid configuration for path "%s": '.$e->getMessage(), $this->getPath()), $e->getCode(), $e);
             }
         }
 
diff --git a/civicrm/vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.php b/civicrm/vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.php
index 31e918af1ce47e54e92782a9a3c0b395736950bc..29d1e154fce8940053a189bf6fe69be39859fdbb 100644
--- a/civicrm/vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.php
+++ b/civicrm/vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.php
@@ -25,7 +25,7 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
     protected $performDeepMerging = true;
     protected $ignoreExtraKeys = false;
     protected $removeExtraKeys = true;
-    protected $children = array();
+    protected $children = [];
     protected $prototype;
     protected $atLeastOne = false;
     protected $allowNewKeys = true;
@@ -43,8 +43,8 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
     {
         parent::__construct($name, $parent);
 
-        $this->nullEquivalent = array();
-        $this->trueEquivalent = array();
+        $this->nullEquivalent = [];
+        $this->trueEquivalent = [];
     }
 
     /**
@@ -75,6 +75,62 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
         return $this->prototype = $this->getNodeBuilder()->node(null, $type)->setParent($this);
     }
 
+    /**
+     * @return VariableNodeDefinition
+     */
+    public function variablePrototype()
+    {
+        return $this->prototype('variable');
+    }
+
+    /**
+     * @return ScalarNodeDefinition
+     */
+    public function scalarPrototype()
+    {
+        return $this->prototype('scalar');
+    }
+
+    /**
+     * @return BooleanNodeDefinition
+     */
+    public function booleanPrototype()
+    {
+        return $this->prototype('boolean');
+    }
+
+    /**
+     * @return IntegerNodeDefinition
+     */
+    public function integerPrototype()
+    {
+        return $this->prototype('integer');
+    }
+
+    /**
+     * @return FloatNodeDefinition
+     */
+    public function floatPrototype()
+    {
+        return $this->prototype('float');
+    }
+
+    /**
+     * @return ArrayNodeDefinition
+     */
+    public function arrayPrototype()
+    {
+        return $this->prototype('array');
+    }
+
+    /**
+     * @return EnumNodeDefinition
+     */
+    public function enumPrototype()
+    {
+        return $this->prototype('enum');
+    }
+
     /**
      * Adds the default value if the node is not set in the configuration.
      *
@@ -158,15 +214,15 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
      * to be the key of the particular item. For example, if "id" is the
      * "key", then:
      *
-     *     array(
-     *         array('id' => 'my_name', 'foo' => 'bar'),
-     *     );
+     *     [
+     *         ['id' => 'my_name', 'foo' => 'bar'],
+     *     ];
      *
      *   becomes
      *
-     *     array(
-     *         'my_name' => array('foo' => 'bar'),
-     *     );
+     *     [
+     *         'my_name' => ['foo' => 'bar'],
+     *     ];
      *
      * If you'd like "'id' => 'my_name'" to still be present in the resulting
      * array, then you can set the second argument of this method to false.
@@ -219,9 +275,9 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
     {
         $this
             ->addDefaultsIfNotSet()
-            ->treatFalseLike(array('enabled' => false))
-            ->treatTrueLike(array('enabled' => true))
-            ->treatNullLike(array('enabled' => true))
+            ->treatFalseLike(['enabled' => false])
+            ->treatTrueLike(['enabled' => true])
+            ->treatNullLike(['enabled' => true])
             ->beforeNormalization()
                 ->ifArray()
                 ->then(function ($v) {
@@ -249,9 +305,9 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
     {
         $this
             ->addDefaultsIfNotSet()
-            ->treatFalseLike(array('enabled' => false))
-            ->treatTrueLike(array('enabled' => true))
-            ->treatNullLike(array('enabled' => true))
+            ->treatFalseLike(['enabled' => false])
+            ->treatTrueLike(['enabled' => true])
+            ->treatNullLike(['enabled' => true])
             ->children()
                 ->booleanNode('enabled')
                     ->defaultTrue()
@@ -276,10 +332,10 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
      * Allows extra config keys to be specified under an array without
      * throwing an exception.
      *
-     * Those config values are simply ignored and removed from the
-     * resulting array. This should be used only in special cases where
-     * you want to send an entire configuration array through a special
-     * tree that processes only part of the array.
+     * Those config values are ignored and removed from the resulting
+     * array. This should be used only in special cases where you want
+     * to send an entire configuration array through a special tree that
+     * processes only part of the array.
      *
      * @param bool $remove Whether to remove the extra keys
      *
@@ -356,6 +412,10 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
                 $node->setKeyAttribute($this->key, $this->removeKeyItem);
             }
 
+            if (false === $this->allowEmptyValue) {
+                @trigger_error(sprintf('Using %s::cannotBeEmpty() at path "%s" has no effect, consider requiresAtLeastOneElement() instead. In 4.0 both methods will behave the same.', __CLASS__, $node->getPath()), E_USER_DEPRECATED);
+            }
+
             if (true === $this->atLeastOne) {
                 $node->setMinNumberOfElements(1);
             }
@@ -381,6 +441,7 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
         $node->addEquivalentValue(false, $this->falseEquivalent);
         $node->setPerformDeepMerging($this->performDeepMerging);
         $node->setRequired($this->required);
+        $node->setDeprecated($this->deprecationMessage);
         $node->setIgnoreExtraKeys($this->ignoreExtraKeys, $this->removeExtraKeys);
         $node->setNormalizeKeys($this->normalizeKeys);
 
@@ -411,19 +472,23 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
         $path = $node->getPath();
 
         if (null !== $this->key) {
-            throw new InvalidDefinitionException(sprintf('->useAttributeAsKey() is not applicable to concrete nodes at path "%s"', $path));
+            throw new InvalidDefinitionException(sprintf('->useAttributeAsKey() is not applicable to concrete nodes at path "%s".', $path));
+        }
+
+        if (false === $this->allowEmptyValue) {
+            @trigger_error(sprintf('->cannotBeEmpty() is not applicable to concrete nodes at path "%s". In 4.0 it will throw an exception.', $path), E_USER_DEPRECATED);
         }
 
         if (true === $this->atLeastOne) {
-            throw new InvalidDefinitionException(sprintf('->requiresAtLeastOneElement() is not applicable to concrete nodes at path "%s"', $path));
+            throw new InvalidDefinitionException(sprintf('->requiresAtLeastOneElement() is not applicable to concrete nodes at path "%s".', $path));
         }
 
         if ($this->default) {
-            throw new InvalidDefinitionException(sprintf('->defaultValue() is not applicable to concrete nodes at path "%s"', $path));
+            throw new InvalidDefinitionException(sprintf('->defaultValue() is not applicable to concrete nodes at path "%s".', $path));
         }
 
         if (false !== $this->addDefaultChildren) {
-            throw new InvalidDefinitionException(sprintf('->addDefaultChildrenIfNoneSet() is not applicable to concrete nodes at path "%s"', $path));
+            throw new InvalidDefinitionException(sprintf('->addDefaultChildrenIfNoneSet() is not applicable to concrete nodes at path "%s".', $path));
         }
     }
 
@@ -437,20 +502,20 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
         $path = $node->getPath();
 
         if ($this->addDefaults) {
-            throw new InvalidDefinitionException(sprintf('->addDefaultsIfNotSet() is not applicable to prototype nodes at path "%s"', $path));
+            throw new InvalidDefinitionException(sprintf('->addDefaultsIfNotSet() is not applicable to prototype nodes at path "%s".', $path));
         }
 
         if (false !== $this->addDefaultChildren) {
             if ($this->default) {
-                throw new InvalidDefinitionException(sprintf('A default value and default children might not be used together at path "%s"', $path));
+                throw new InvalidDefinitionException(sprintf('A default value and default children might not be used together at path "%s".', $path));
             }
 
             if (null !== $this->key && (null === $this->addDefaultChildren || \is_int($this->addDefaultChildren) && $this->addDefaultChildren > 0)) {
-                throw new InvalidDefinitionException(sprintf('->addDefaultChildrenIfNoneSet() should set default children names as ->useAttributeAsKey() is used at path "%s"', $path));
+                throw new InvalidDefinitionException(sprintf('->addDefaultChildrenIfNoneSet() should set default children names as ->useAttributeAsKey() is used at path "%s".', $path));
             }
 
             if (null === $this->key && (\is_string($this->addDefaultChildren) || \is_array($this->addDefaultChildren))) {
-                throw new InvalidDefinitionException(sprintf('->addDefaultChildrenIfNoneSet() might not set default children names as ->useAttributeAsKey() is not used at path "%s"', $path));
+                throw new InvalidDefinitionException(sprintf('->addDefaultChildrenIfNoneSet() might not set default children names as ->useAttributeAsKey() is not used at path "%s".', $path));
             }
         }
     }
diff --git a/civicrm/vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.php b/civicrm/vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.php
index a6292f74d81200b0505205fa7a158d3e16f403b2..28e56579ada52427784017c49572f046dcc70d4d 100644
--- a/civicrm/vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.php
+++ b/civicrm/vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.php
@@ -12,6 +12,7 @@
 namespace Symfony\Component\Config\Definition\Builder;
 
 use Symfony\Component\Config\Definition\BooleanNode;
+use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException;
 
 /**
  * This class provides a fluent interface for defining a node.
@@ -31,24 +32,22 @@ class BooleanNodeDefinition extends ScalarNodeDefinition
     }
 
     /**
-     * {@inheritdoc}
+     * Instantiate a Node.
      *
-     * @deprecated Deprecated since version 2.8, to be removed in 3.0.
+     * @return BooleanNode The node
      */
-    public function cannotBeEmpty()
+    protected function instantiateNode()
     {
-        @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-
-        return parent::cannotBeEmpty();
+        return new BooleanNode($this->name, $this->parent);
     }
 
     /**
-     * Instantiate a Node.
+     * {@inheritdoc}
      *
-     * @return BooleanNode The node
+     * @throws InvalidDefinitionException
      */
-    protected function instantiateNode()
+    public function cannotBeEmpty()
     {
-        return new BooleanNode($this->name, $this->parent);
+        throw new InvalidDefinitionException('->cannotBeEmpty() is not applicable to BooleanNodeDefinition.');
     }
 }
diff --git a/civicrm/vendor/symfony/config/Definition/Builder/ExprBuilder.php b/civicrm/vendor/symfony/config/Definition/Builder/ExprBuilder.php
index ddbe5b0401ab6bc8756e1366586904683bcf1405..5db229dccab7f759039c635e75a9031f6042f99f 100644
--- a/civicrm/vendor/symfony/config/Definition/Builder/ExprBuilder.php
+++ b/civicrm/vendor/symfony/config/Definition/Builder/ExprBuilder.php
@@ -88,6 +88,18 @@ class ExprBuilder
         return $this;
     }
 
+    /**
+     * Tests if the value is empty.
+     *
+     * @return ExprBuilder
+     */
+    public function ifEmpty()
+    {
+        $this->ifPart = function ($v) { return empty($v); };
+
+        return $this;
+    }
+
     /**
      * Tests if the value is an array.
      *
@@ -124,6 +136,19 @@ class ExprBuilder
         return $this;
     }
 
+    /**
+     * Transforms variables of any type into an array.
+     *
+     * @return $this
+     */
+    public function castToArray()
+    {
+        $this->ifPart = function ($v) { return !\is_array($v); };
+        $this->thenPart = function ($v) { return [$v]; };
+
+        return $this;
+    }
+
     /**
      * Sets the closure to run if the test pass.
      *
@@ -143,7 +168,7 @@ class ExprBuilder
      */
     public function thenEmptyArray()
     {
-        $this->thenPart = function ($v) { return array(); };
+        $this->thenPart = function ($v) { return []; };
 
         return $this;
     }
@@ -175,7 +200,7 @@ class ExprBuilder
      */
     public function thenUnset()
     {
-        $this->thenPart = function ($v) { throw new UnsetKeyException('Unsetting key'); };
+        $this->thenPart = function ($v) { throw new UnsetKeyException('Unsetting key.'); };
 
         return $this;
     }
diff --git a/civicrm/vendor/symfony/config/Definition/Builder/NodeBuilder.php b/civicrm/vendor/symfony/config/Definition/Builder/NodeBuilder.php
index 95863d68f9bba317436981060bac62d6cba0c39d..2809cb6c6f601ac2cffd6d03e5d49c581af516e7 100644
--- a/civicrm/vendor/symfony/config/Definition/Builder/NodeBuilder.php
+++ b/civicrm/vendor/symfony/config/Definition/Builder/NodeBuilder.php
@@ -23,15 +23,15 @@ class NodeBuilder implements NodeParentInterface
 
     public function __construct()
     {
-        $this->nodeMapping = array(
-            'variable' => __NAMESPACE__.'\\VariableNodeDefinition',
-            'scalar' => __NAMESPACE__.'\\ScalarNodeDefinition',
-            'boolean' => __NAMESPACE__.'\\BooleanNodeDefinition',
-            'integer' => __NAMESPACE__.'\\IntegerNodeDefinition',
-            'float' => __NAMESPACE__.'\\FloatNodeDefinition',
-            'array' => __NAMESPACE__.'\\ArrayNodeDefinition',
-            'enum' => __NAMESPACE__.'\\EnumNodeDefinition',
-        );
+        $this->nodeMapping = [
+            'variable' => VariableNodeDefinition::class,
+            'scalar' => ScalarNodeDefinition::class,
+            'boolean' => BooleanNodeDefinition::class,
+            'integer' => IntegerNodeDefinition::class,
+            'float' => FloatNodeDefinition::class,
+            'array' => ArrayNodeDefinition::class,
+            'enum' => EnumNodeDefinition::class,
+        ];
     }
 
     /**
diff --git a/civicrm/vendor/symfony/config/Definition/Builder/NodeDefinition.php b/civicrm/vendor/symfony/config/Definition/Builder/NodeDefinition.php
index a14161f082b9d99e7b28493861e13588470f2b37..cc245d7489b0d6ff3c049bd6a93583794da5fab5 100644
--- a/civicrm/vendor/symfony/config/Definition/Builder/NodeDefinition.php
+++ b/civicrm/vendor/symfony/config/Definition/Builder/NodeDefinition.php
@@ -27,13 +27,14 @@ abstract class NodeDefinition implements NodeParentInterface
     protected $defaultValue;
     protected $default = false;
     protected $required = false;
+    protected $deprecationMessage = null;
     protected $merge;
     protected $allowEmptyValue = true;
     protected $nullEquivalent;
     protected $trueEquivalent = true;
     protected $falseEquivalent = false;
     protected $parent;
-    protected $attributes = array();
+    protected $attributes = [];
 
     /**
      * @param string|null              $name   The name of the node
@@ -160,6 +161,23 @@ abstract class NodeDefinition implements NodeParentInterface
         return $this;
     }
 
+    /**
+     * Sets the node as deprecated.
+     *
+     * You can use %node% and %path% placeholders in your message to display,
+     * respectively, the node name and its complete path.
+     *
+     * @param string $message Deprecation message
+     *
+     * @return $this
+     */
+    public function setDeprecated($message = 'The child node "%node%" at path "%path%" is deprecated.')
+    {
+        $this->deprecationMessage = $message;
+
+        return $this;
+    }
+
     /**
      * Sets the equivalent value used when the node contains null.
      *
diff --git a/civicrm/vendor/symfony/config/Definition/Builder/NormalizationBuilder.php b/civicrm/vendor/symfony/config/Definition/Builder/NormalizationBuilder.php
index 35e30487a60ebd21106ecb15c04ade3aa5ff700d..d3cdca90df1c3e630856e2e5761176c3ff42fdee 100644
--- a/civicrm/vendor/symfony/config/Definition/Builder/NormalizationBuilder.php
+++ b/civicrm/vendor/symfony/config/Definition/Builder/NormalizationBuilder.php
@@ -19,8 +19,8 @@ namespace Symfony\Component\Config\Definition\Builder;
 class NormalizationBuilder
 {
     protected $node;
-    public $before = array();
-    public $remappings = array();
+    public $before = [];
+    public $remappings = [];
 
     public function __construct(NodeDefinition $node)
     {
@@ -37,7 +37,7 @@ class NormalizationBuilder
      */
     public function remap($key, $plural = null)
     {
-        $this->remappings[] = array($key, null === $plural ? $key.'s' : $plural);
+        $this->remappings[] = [$key, null === $plural ? $key.'s' : $plural];
 
         return $this;
     }
diff --git a/civicrm/vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php b/civicrm/vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php
index 40c0bfcc6e96119f7354579de1884855c3e6102c..390b1136567e6ce3e175ffffc28ba6af92688388 100644
--- a/civicrm/vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php
+++ b/civicrm/vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php
@@ -11,6 +11,8 @@
 
 namespace Symfony\Component\Config\Definition\Builder;
 
+use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException;
+
 /**
  * Abstract class that contains common code of integer and float node definitions.
  *
@@ -33,7 +35,7 @@ abstract class NumericNodeDefinition extends ScalarNodeDefinition
     public function max($max)
     {
         if (isset($this->min) && $this->min > $max) {
-            throw new \InvalidArgumentException(sprintf('You cannot define a max(%s) as you already have a min(%s)', $max, $this->min));
+            throw new \InvalidArgumentException(sprintf('You cannot define a max(%s) as you already have a min(%s).', $max, $this->min));
         }
         $this->max = $max;
 
@@ -52,7 +54,7 @@ abstract class NumericNodeDefinition extends ScalarNodeDefinition
     public function min($min)
     {
         if (isset($this->max) && $this->max < $min) {
-            throw new \InvalidArgumentException(sprintf('You cannot define a min(%s) as you already have a max(%s)', $min, $this->max));
+            throw new \InvalidArgumentException(sprintf('You cannot define a min(%s) as you already have a max(%s).', $min, $this->max));
         }
         $this->min = $min;
 
@@ -62,12 +64,10 @@ abstract class NumericNodeDefinition extends ScalarNodeDefinition
     /**
      * {@inheritdoc}
      *
-     * @deprecated Deprecated since version 2.8, to be removed in 3.0.
+     * @throws InvalidDefinitionException
      */
     public function cannotBeEmpty()
     {
-        @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-
-        return parent::cannotBeEmpty();
+        throw new InvalidDefinitionException('->cannotBeEmpty() is not applicable to NumericNodeDefinition.');
     }
 }
diff --git a/civicrm/vendor/symfony/config/Definition/Builder/TreeBuilder.php b/civicrm/vendor/symfony/config/Definition/Builder/TreeBuilder.php
index 5d02848a0900315c399f1f8eac6f5d466d0081cb..384477c501082e944d6deeb7f4d91d2d1df853a3 100644
--- a/civicrm/vendor/symfony/config/Definition/Builder/TreeBuilder.php
+++ b/civicrm/vendor/symfony/config/Definition/Builder/TreeBuilder.php
@@ -22,6 +22,10 @@ class TreeBuilder implements NodeParentInterface
 {
     protected $tree;
     protected $root;
+
+    /**
+     * @deprecated since 3.4. To be removed in 4.0
+     */
     protected $builder;
 
     /**
diff --git a/civicrm/vendor/symfony/config/Definition/Builder/ValidationBuilder.php b/civicrm/vendor/symfony/config/Definition/Builder/ValidationBuilder.php
index bb2b9eb33924b62cbd4269efba97fdb43429676d..4efc726c0cf2d3be24060180bdca74398a50c1fd 100644
--- a/civicrm/vendor/symfony/config/Definition/Builder/ValidationBuilder.php
+++ b/civicrm/vendor/symfony/config/Definition/Builder/ValidationBuilder.php
@@ -19,7 +19,7 @@ namespace Symfony\Component\Config\Definition\Builder;
 class ValidationBuilder
 {
     protected $node;
-    public $rules = array();
+    public $rules = [];
 
     public function __construct(NodeDefinition $node)
     {
diff --git a/civicrm/vendor/symfony/config/Definition/Builder/VariableNodeDefinition.php b/civicrm/vendor/symfony/config/Definition/Builder/VariableNodeDefinition.php
index a46b7ea61ded07a2f1428005b6db96010a8ad274..26565e1771d849a8a0abdbdff4a0386bc6e1a80f 100644
--- a/civicrm/vendor/symfony/config/Definition/Builder/VariableNodeDefinition.php
+++ b/civicrm/vendor/symfony/config/Definition/Builder/VariableNodeDefinition.php
@@ -54,6 +54,7 @@ class VariableNodeDefinition extends NodeDefinition
         $node->addEquivalentValue(true, $this->trueEquivalent);
         $node->addEquivalentValue(false, $this->falseEquivalent);
         $node->setRequired($this->required);
+        $node->setDeprecated($this->deprecationMessage);
 
         if (null !== $this->validation) {
             $node->setFinalValidationClosures($this->validation->rules);
diff --git a/civicrm/vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php b/civicrm/vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php
index 0e297318d26227d2d50fac397bbea96a8743f32c..744f15fd81b5aa63f7128b764c18f0214e24944f 100644
--- a/civicrm/vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php
+++ b/civicrm/vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php
@@ -42,10 +42,9 @@ class XmlReferenceDumper
     }
 
     /**
-     * @param NodeInterface $node
-     * @param int           $depth
-     * @param bool          $root      If the node is the root node
-     * @param string        $namespace The namespace of the node
+     * @param int    $depth
+     * @param bool   $root      If the node is the root node
+     * @param string $namespace The namespace of the node
      */
     private function writeNode(NodeInterface $node, $depth = 0, $root = false, $namespace = null)
     {
@@ -65,10 +64,10 @@ class XmlReferenceDumper
         }
         $rootName = str_replace('_', '-', $rootName);
 
-        $rootAttributes = array();
-        $rootAttributeComments = array();
-        $rootChildren = array();
-        $rootComments = array();
+        $rootAttributes = [];
+        $rootAttributeComments = [];
+        $rootChildren = [];
+        $rootComments = [];
 
         if ($node instanceof ArrayNode) {
             $children = $node->getChildren();
@@ -96,7 +95,10 @@ class XmlReferenceDumper
                     $rootAttributes[$key] = str_replace('-', ' ', $rootName).' '.$key;
                 }
 
-                if ($prototype instanceof ArrayNode) {
+                if ($prototype instanceof PrototypedArrayNode) {
+                    $prototype->setName($key);
+                    $children = [$key => $prototype];
+                } elseif ($prototype instanceof ArrayNode) {
                     $children = $prototype->getChildren();
                 } else {
                     if ($prototype->hasDefaultValue()) {
@@ -137,7 +139,7 @@ class XmlReferenceDumper
                     $value = '%%%%not_defined%%%%'; // use a string which isn't used in the normal world
 
                     // comments
-                    $comments = array();
+                    $comments = [];
                     if ($info = $child->getInfo()) {
                         $comments[] = $info;
                     }
@@ -150,6 +152,10 @@ class XmlReferenceDumper
                         $comments[] = 'Required';
                     }
 
+                    if ($child->isDeprecated()) {
+                        $comments[] = sprintf('Deprecated (%s)', $child->getDeprecationMessage($child->getName(), $node->getPath()));
+                    }
+
                     if ($child instanceof EnumNode) {
                         $comments[] = 'One of '.implode('; ', array_map('json_encode', $child->getValues()));
                     }
@@ -300,5 +306,7 @@ class XmlReferenceDumper
         if (\is_array($value)) {
             return implode(',', $value);
         }
+
+        return '';
     }
 }
diff --git a/civicrm/vendor/symfony/config/Definition/Dumper/YamlReferenceDumper.php b/civicrm/vendor/symfony/config/Definition/Dumper/YamlReferenceDumper.php
index 8945cba0057d79419892a300c5b4d80ba477d767..ba355394602f7f8309c1869107b9e1b657222ca1 100644
--- a/civicrm/vendor/symfony/config/Definition/Dumper/YamlReferenceDumper.php
+++ b/civicrm/vendor/symfony/config/Definition/Dumper/YamlReferenceDumper.php
@@ -16,6 +16,7 @@ use Symfony\Component\Config\Definition\ConfigurationInterface;
 use Symfony\Component\Config\Definition\EnumNode;
 use Symfony\Component\Config\Definition\NodeInterface;
 use Symfony\Component\Config\Definition\PrototypedArrayNode;
+use Symfony\Component\Config\Definition\ScalarNode;
 use Symfony\Component\Yaml\Inline;
 
 /**
@@ -32,6 +33,32 @@ class YamlReferenceDumper
         return $this->dumpNode($configuration->getConfigTreeBuilder()->buildTree());
     }
 
+    public function dumpAtPath(ConfigurationInterface $configuration, $path)
+    {
+        $rootNode = $node = $configuration->getConfigTreeBuilder()->buildTree();
+
+        foreach (explode('.', $path) as $step) {
+            if (!$node instanceof ArrayNode) {
+                throw new \UnexpectedValueException(sprintf('Unable to find node at path "%s.%s".', $rootNode->getName(), $path));
+            }
+
+            /** @var NodeInterface[] $children */
+            $children = $node instanceof PrototypedArrayNode ? $this->getPrototypeChildren($node) : $node->getChildren();
+
+            foreach ($children as $child) {
+                if ($child->getName() === $step) {
+                    $node = $child;
+
+                    continue 2;
+                }
+            }
+
+            throw new \UnexpectedValueException(sprintf('Unable to find node at path "%s.%s".', $rootNode->getName(), $path));
+        }
+
+        return $this->dumpNode($node);
+    }
+
     public function dumpNode(NodeInterface $node)
     {
         $this->reference = '';
@@ -43,12 +70,12 @@ class YamlReferenceDumper
     }
 
     /**
-     * @param NodeInterface $node
-     * @param int           $depth
+     * @param int  $depth
+     * @param bool $prototypedArray
      */
-    private function writeNode(NodeInterface $node, $depth = 0)
+    private function writeNode(NodeInterface $node, NodeInterface $parentNode = null, $depth = 0, $prototypedArray = false)
     {
-        $comments = array();
+        $comments = [];
         $default = '';
         $defaultArray = null;
         $children = null;
@@ -59,29 +86,7 @@ class YamlReferenceDumper
             $children = $node->getChildren();
 
             if ($node instanceof PrototypedArrayNode) {
-                $prototype = $node->getPrototype();
-
-                if ($prototype instanceof ArrayNode) {
-                    $children = $prototype->getChildren();
-                }
-
-                // check for attribute as key
-                if ($key = $node->getKeyAttribute()) {
-                    $keyNodeClass = 'Symfony\Component\Config\Definition\\'.($prototype instanceof ArrayNode ? 'ArrayNode' : 'ScalarNode');
-                    $keyNode = new $keyNodeClass($key, $node);
-
-                    $info = 'Prototype';
-                    if (null !== $prototype->getInfo()) {
-                        $info .= ': '.$prototype->getInfo();
-                    }
-                    $keyNode->setInfo($info);
-
-                    // add children
-                    foreach ($children as $childNode) {
-                        $keyNode->addChild($childNode);
-                    }
-                    $children = array($key => $keyNode);
-                }
+                $children = $this->getPrototypeChildren($node);
             }
 
             if (!$children) {
@@ -117,6 +122,11 @@ class YamlReferenceDumper
             $comments[] = 'Required';
         }
 
+        // deprecated?
+        if ($node->isDeprecated()) {
+            $comments[] = sprintf('Deprecated (%s)', $node->getDeprecationMessage($node->getName(), $parentNode ? $parentNode->getPath() : $node->getPath()));
+        }
+
         // example
         if ($example && !\is_array($example)) {
             $comments[] = 'Example: '.$example;
@@ -125,7 +135,8 @@ class YamlReferenceDumper
         $default = '' != (string) $default ? ' '.$default : '';
         $comments = \count($comments) ? '# '.implode(', ', $comments) : '';
 
-        $text = rtrim(sprintf('%-21s%s %s', $node->getName().':', $default, $comments), ' ');
+        $key = $prototypedArray ? '-' : $node->getName().':';
+        $text = rtrim(sprintf('%-21s%s %s', $key, $default, $comments), ' ');
 
         if ($info = $node->getInfo()) {
             $this->writeLine('');
@@ -159,7 +170,7 @@ class YamlReferenceDumper
 
         if ($children) {
             foreach ($children as $childNode) {
-                $this->writeNode($childNode, $depth + 1);
+                $this->writeNode($childNode, $node, $depth + 1, $node instanceof PrototypedArrayNode && !$node->getKeyAttribute());
             }
         }
     }
@@ -200,4 +211,42 @@ class YamlReferenceDumper
             }
         }
     }
+
+    /**
+     * @return array
+     */
+    private function getPrototypeChildren(PrototypedArrayNode $node)
+    {
+        $prototype = $node->getPrototype();
+        $key = $node->getKeyAttribute();
+
+        // Do not expand prototype if it isn't an array node nor uses attribute as key
+        if (!$key && !$prototype instanceof ArrayNode) {
+            return $node->getChildren();
+        }
+
+        if ($prototype instanceof ArrayNode) {
+            $keyNode = new ArrayNode($key, $node);
+            $children = $prototype->getChildren();
+
+            if ($prototype instanceof PrototypedArrayNode && $prototype->getKeyAttribute()) {
+                $children = $this->getPrototypeChildren($prototype);
+            }
+
+            // add children
+            foreach ($children as $childNode) {
+                $keyNode->addChild($childNode);
+            }
+        } else {
+            $keyNode = new ScalarNode($key, $node);
+        }
+
+        $info = 'Prototype';
+        if (null !== $prototype->getInfo()) {
+            $info .= ': '.$prototype->getInfo();
+        }
+        $keyNode->setInfo($info);
+
+        return [$key => $keyNode];
+    }
 }
diff --git a/civicrm/vendor/symfony/config/Definition/EnumNode.php b/civicrm/vendor/symfony/config/Definition/EnumNode.php
index a214a854b758700ffa4026685baef60208dbfe2a..15c8db3e66e52c94824410392cbab761684a51ac 100644
--- a/civicrm/vendor/symfony/config/Definition/EnumNode.php
+++ b/civicrm/vendor/symfony/config/Definition/EnumNode.php
@@ -22,7 +22,7 @@ class EnumNode extends ScalarNode
 {
     private $values;
 
-    public function __construct($name, NodeInterface $parent = null, array $values = array())
+    public function __construct($name, NodeInterface $parent = null, array $values = [])
     {
         $values = array_unique($values);
         if (empty($values)) {
diff --git a/civicrm/vendor/symfony/config/Definition/Processor.php b/civicrm/vendor/symfony/config/Definition/Processor.php
index 3e0feab8693e154897771ccb4a60aa3403284222..0a935eeb7ff1d1deeb48a95e373ffe2ab84bcc51 100644
--- a/civicrm/vendor/symfony/config/Definition/Processor.php
+++ b/civicrm/vendor/symfony/config/Definition/Processor.php
@@ -28,7 +28,7 @@ class Processor
      */
     public function process(NodeInterface $configTree, array $configs)
     {
-        $currentConfig = array();
+        $currentConfig = [];
         foreach ($configs as $config) {
             $config = $configTree->normalize($config);
             $currentConfig = $configTree->merge($currentConfig, $config);
@@ -86,12 +86,12 @@ class Processor
         if (isset($config[$key])) {
             if (\is_string($config[$key]) || !\is_int(key($config[$key]))) {
                 // only one
-                return  array($config[$key]);
+                return  [$config[$key]];
             }
 
             return  $config[$key];
         }
 
-        return array();
+        return [];
     }
 }
diff --git a/civicrm/vendor/symfony/config/Definition/PrototypedArrayNode.php b/civicrm/vendor/symfony/config/Definition/PrototypedArrayNode.php
index eddcb32a77871b2c942b7fd2680b781e00b58ad3..d18a109abc6a40640a9b29ed91bf72caa1e2d682 100644
--- a/civicrm/vendor/symfony/config/Definition/PrototypedArrayNode.php
+++ b/civicrm/vendor/symfony/config/Definition/PrototypedArrayNode.php
@@ -27,12 +27,12 @@ class PrototypedArrayNode extends ArrayNode
     protected $keyAttribute;
     protected $removeKeyAttribute = false;
     protected $minNumberOfElements = 0;
-    protected $defaultValue = array();
+    protected $defaultValue = [];
     protected $defaultChildren;
     /**
      * @var NodeInterface[] An array of the prototypes of the simplified value children
      */
-    private $valuePrototypes = array();
+    private $valuePrototypes = [];
 
     /**
      * Sets the minimum number of elements that a prototype based node must
@@ -53,15 +53,15 @@ class PrototypedArrayNode extends ArrayNode
      * to be the key of the particular item. For example, if "id" is the
      * "key", then:
      *
-     *     array(
-     *         array('id' => 'my_name', 'foo' => 'bar'),
-     *     );
+     *     [
+     *         ['id' => 'my_name', 'foo' => 'bar'],
+     *     ];
      *
      *  becomes
      *
-     *      array(
-     *          'my_name' => array('foo' => 'bar'),
-     *      );
+     *      [
+     *          'my_name' => ['foo' => 'bar'],
+     *      ];
      *
      * If you'd like "'id' => 'my_name'" to still be present in the resulting
      * array, then you can set the second argument of this method to false.
@@ -78,7 +78,7 @@ class PrototypedArrayNode extends ArrayNode
     /**
      * Retrieves the name of the attribute which value should be used as key.
      *
-     * @return string The name of the attribute
+     * @return string|null The name of the attribute
      */
     public function getKeyAttribute()
     {
@@ -114,10 +114,10 @@ class PrototypedArrayNode extends ArrayNode
      *
      * @param int|string|array|null $children The number of children|The child name|The children names to be added
      */
-    public function setAddChildrenIfNoneSet($children = array('defaults'))
+    public function setAddChildrenIfNoneSet($children = ['defaults'])
     {
         if (null === $children) {
-            $this->defaultChildren = array('defaults');
+            $this->defaultChildren = ['defaults'];
         } else {
             $this->defaultChildren = \is_int($children) && $children > 0 ? range(1, $children) : (array) $children;
         }
@@ -132,8 +132,8 @@ class PrototypedArrayNode extends ArrayNode
     public function getDefaultValue()
     {
         if (null !== $this->defaultChildren) {
-            $default = $this->prototype->hasDefaultValue() ? $this->prototype->getDefaultValue() : array();
-            $defaults = array();
+            $default = $this->prototype->hasDefaultValue() ? $this->prototype->getDefaultValue() : [];
+            $defaults = [];
             foreach (array_values($this->defaultChildren) as $i => $name) {
                 $defaults[null === $this->keyAttribute ? $i : $name] = $default;
             }
@@ -185,7 +185,7 @@ class PrototypedArrayNode extends ArrayNode
     protected function finalizeValue($value)
     {
         if (false === $value) {
-            throw new UnsetKeyException(sprintf('Unsetting key for path "%s", value: %s', $this->getPath(), json_encode($value)));
+            throw new UnsetKeyException(sprintf('Unsetting key for path "%s", value: "%s".', $this->getPath(), json_encode($value)));
         }
 
         foreach ($value as $k => $v) {
@@ -226,7 +226,7 @@ class PrototypedArrayNode extends ArrayNode
         $value = $this->remapXml($value);
 
         $isAssoc = array_keys($value) !== range(0, \count($value) - 1);
-        $normalized = array();
+        $normalized = [];
         foreach ($value as $k => $v) {
             if (null !== $this->keyAttribute && \is_array($v)) {
                 if (!isset($v[$this->keyAttribute]) && \is_int($k) && !$isAssoc) {
@@ -243,9 +243,9 @@ class PrototypedArrayNode extends ArrayNode
                     }
 
                     // if only "value" is left
-                    if (array_keys($v) === array('value')) {
+                    if (array_keys($v) === ['value']) {
                         $v = $v['value'];
-                        if ($this->prototype instanceof ArrayNode && ($children = $this->prototype->getChildren()) && array_key_exists('value', $children)) {
+                        if ($this->prototype instanceof ArrayNode && ($children = $this->prototype->getChildren()) && \array_key_exists('value', $children)) {
                             $valuePrototype = current($this->valuePrototypes) ?: clone $children['value'];
                             $valuePrototype->parent = $this;
                             $originalClosures = $this->prototype->normalizationClosures;
@@ -258,7 +258,7 @@ class PrototypedArrayNode extends ArrayNode
                     }
                 }
 
-                if (array_key_exists($k, $normalized)) {
+                if (\array_key_exists($k, $normalized)) {
                     $ex = new DuplicateKeyException(sprintf('Duplicate key "%s" for path "%s".', $k, $this->getPath()));
                     $ex->setPath($this->getPath());
 
@@ -301,14 +301,14 @@ class PrototypedArrayNode extends ArrayNode
         }
 
         foreach ($rightSide as $k => $v) {
-            // prototype, and key is irrelevant, so simply append the element
+            // prototype, and key is irrelevant, append the element
             if (null === $this->keyAttribute) {
                 $leftSide[] = $v;
                 continue;
             }
 
             // no conflict
-            if (!array_key_exists($k, $leftSide)) {
+            if (!\array_key_exists($k, $leftSide)) {
                 if (!$this->allowNewKeys) {
                     $ex = new InvalidConfigurationException(sprintf('You are not allowed to define new elements for path "%s". Please define all elements for this path in one config file.', $this->getPath()));
                     $ex->setPath($this->getPath());
@@ -335,30 +335,30 @@ class PrototypedArrayNode extends ArrayNode
      *
      * For example, assume $this->keyAttribute is 'name' and the value array is as follows:
      *
-     *     array(
-     *         array(
+     *     [
+     *         [
      *             'name' => 'name001',
      *             'value' => 'value001'
-     *         )
-     *     )
+     *         ]
+     *     ]
      *
      * Now, the key is 0 and the child node is:
      *
-     *     array(
+     *     [
      *        'name' => 'name001',
      *        'value' => 'value001'
-     *     )
+     *     ]
      *
      * When normalizing the value array, the 'name' element will removed from the child node
      * and its value becomes the new key of the child node:
      *
-     *     array(
-     *         'name001' => array('value' => 'value001')
-     *     )
+     *     [
+     *         'name001' => ['value' => 'value001']
+     *     ]
      *
      * Now only 'value' element is left in the child node which can be further simplified into a string:
      *
-     *     array('name001' => 'value001')
+     *     ['name001' => 'value001']
      *
      * Now, the key becomes 'name001' and the child node becomes 'value001' and
      * the prototype of child node 'name001' should be a ScalarNode instead of an ArrayNode instance.
diff --git a/civicrm/vendor/symfony/config/Definition/ReferenceDumper.php b/civicrm/vendor/symfony/config/Definition/ReferenceDumper.php
deleted file mode 100644
index 047b258ad3377c4672e7560acd2ed3bbeec469f7..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/config/Definition/ReferenceDumper.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Config\Definition;
-
-@trigger_error('The '.__NAMESPACE__.'\ReferenceDumper class is deprecated since Symfony 2.4 and will be removed in 3.0. Use the Symfony\Component\Config\Definition\Dumper\YamlReferenceDumper class instead.', E_USER_DEPRECATED);
-
-use Symfony\Component\Config\Definition\Dumper\YamlReferenceDumper;
-
-/**
- * @deprecated since version 2.4, to be removed in 3.0.
- *             Use {@link \Symfony\Component\Config\Definition\Dumper\YamlReferenceDumper} instead.
- */
-class ReferenceDumper extends YamlReferenceDumper
-{
-}
diff --git a/civicrm/vendor/symfony/config/DependencyInjection/ConfigCachePass.php b/civicrm/vendor/symfony/config/DependencyInjection/ConfigCachePass.php
new file mode 100644
index 0000000000000000000000000000000000000000..128bf7e28cd212011847b45bfdf8eb5545e53213
--- /dev/null
+++ b/civicrm/vendor/symfony/config/DependencyInjection/ConfigCachePass.php
@@ -0,0 +1,52 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Config\DependencyInjection;
+
+@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use tagged iterator arguments instead.', ConfigCachePass::class), E_USER_DEPRECATED);
+
+use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+/**
+ * Adds services tagged config_cache.resource_checker to the config_cache_factory service, ordering them by priority.
+ *
+ * @author Matthias Pigulla <mp@webfactory.de>
+ * @author Benjamin Klotz <bk@webfactory.de>
+ *
+ * @deprecated since version 3.4, to be removed in 4.0. Use tagged iterator arguments instead.
+ */
+class ConfigCachePass implements CompilerPassInterface
+{
+    use PriorityTaggedServiceTrait;
+
+    private $factoryServiceId;
+    private $resourceCheckerTag;
+
+    public function __construct($factoryServiceId = 'config_cache_factory', $resourceCheckerTag = 'config_cache.resource_checker')
+    {
+        $this->factoryServiceId = $factoryServiceId;
+        $this->resourceCheckerTag = $resourceCheckerTag;
+    }
+
+    public function process(ContainerBuilder $container)
+    {
+        $resourceCheckers = $this->findAndSortTaggedServices($this->resourceCheckerTag, $container);
+
+        if (empty($resourceCheckers)) {
+            return;
+        }
+
+        $container->getDefinition($this->factoryServiceId)->replaceArgument(0, new IteratorArgument($resourceCheckers));
+    }
+}
diff --git a/civicrm/vendor/symfony/config/Exception/FileLoaderLoadException.php b/civicrm/vendor/symfony/config/Exception/FileLoaderLoadException.php
index 819abe6ef1e78290dd4f33d0a319dd6a86359b81..82d90eb39b6efe067e0dc760bf3f496a22885007 100644
--- a/civicrm/vendor/symfony/config/Exception/FileLoaderLoadException.php
+++ b/civicrm/vendor/symfony/config/Exception/FileLoaderLoadException.php
@@ -23,8 +23,9 @@ class FileLoaderLoadException extends \Exception
      * @param string     $sourceResource The original resource importing the new resource
      * @param int        $code           The error code
      * @param \Exception $previous       A previous exception
+     * @param string     $type           The type of resource
      */
-    public function __construct($resource, $sourceResource = null, $code = null, $previous = null)
+    public function __construct($resource, $sourceResource = null, $code = null, $previous = null, $type = null)
     {
         $message = '';
         if ($previous) {
@@ -60,6 +61,13 @@ class FileLoaderLoadException extends \Exception
             $bundle = substr($parts[0], 1);
             $message .= sprintf(' Make sure the "%s" bundle is correctly registered and loaded in the application kernel class.', $bundle);
             $message .= sprintf(' If the bundle is registered, make sure the bundle path "%s" is not empty.', $resource);
+        } elseif (null !== $type) {
+            // maybe there is no loader for this specific type
+            if ('annotation' === $type) {
+                $message .= ' Make sure annotations are installed and enabled.';
+            } else {
+                $message .= sprintf(' Make sure there is a loader supporting the "%s" type.', $type);
+            }
         }
 
         parent::__construct($message, $code, $previous);
@@ -72,7 +80,7 @@ class FileLoaderLoadException extends \Exception
         }
 
         if (\is_array($var)) {
-            $a = array();
+            $a = [];
             foreach ($var as $k => $v) {
                 $a[] = sprintf('%s => %s', $k, $this->varToString($v));
             }
diff --git a/civicrm/vendor/symfony/config/Exception/FileLocatorFileNotFoundException.php b/civicrm/vendor/symfony/config/Exception/FileLocatorFileNotFoundException.php
new file mode 100644
index 0000000000000000000000000000000000000000..648cf0e70769050791b198711eade17f298c343a
--- /dev/null
+++ b/civicrm/vendor/symfony/config/Exception/FileLocatorFileNotFoundException.php
@@ -0,0 +1,34 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Config\Exception;
+
+/**
+ * File locator exception if a file does not exist.
+ *
+ * @author Leo Feyer <https://github.com/leofeyer>
+ */
+class FileLocatorFileNotFoundException extends \InvalidArgumentException
+{
+    private $paths;
+
+    public function __construct($message = '', $code = 0, $previous = null, array $paths = [])
+    {
+        parent::__construct($message, $code, $previous);
+
+        $this->paths = $paths;
+    }
+
+    public function getPaths()
+    {
+        return $this->paths;
+    }
+}
diff --git a/civicrm/vendor/symfony/config/FileLocator.php b/civicrm/vendor/symfony/config/FileLocator.php
index 368cc19ae68ba6d47dd718428e23ae9c8c3d12be..c125ba0b1377170c3ac5475af1fd46e5228b3e65 100644
--- a/civicrm/vendor/symfony/config/FileLocator.php
+++ b/civicrm/vendor/symfony/config/FileLocator.php
@@ -11,6 +11,8 @@
 
 namespace Symfony\Component\Config;
 
+use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException;
+
 /**
  * FileLocator uses an array of pre-defined paths to find files.
  *
@@ -23,7 +25,7 @@ class FileLocator implements FileLocatorInterface
     /**
      * @param string|array $paths A path or an array of paths where to look for resources
      */
-    public function __construct($paths = array())
+    public function __construct($paths = [])
     {
         $this->paths = (array) $paths;
     }
@@ -39,7 +41,7 @@ class FileLocator implements FileLocatorInterface
 
         if ($this->isAbsolutePath($name)) {
             if (!file_exists($name)) {
-                throw new \InvalidArgumentException(sprintf('The file "%s" does not exist.', $name));
+                throw new FileLocatorFileNotFoundException(sprintf('The file "%s" does not exist.', $name), 0, null, [$name]);
             }
 
             return $name;
@@ -52,7 +54,7 @@ class FileLocator implements FileLocatorInterface
         }
 
         $paths = array_unique($paths);
-        $filepaths = array();
+        $filepaths = $notfound = [];
 
         foreach ($paths as $path) {
             if (@file_exists($file = $path.\DIRECTORY_SEPARATOR.$name)) {
@@ -60,11 +62,13 @@ class FileLocator implements FileLocatorInterface
                     return $file;
                 }
                 $filepaths[] = $file;
+            } else {
+                $notfound[] = $file;
             }
         }
 
         if (!$filepaths) {
-            throw new \InvalidArgumentException(sprintf('The file "%s" does not exist (in: %s).', $name, implode(', ', $paths)));
+            throw new FileLocatorFileNotFoundException(sprintf('The file "%s" does not exist (in: "%s").', $name, implode('", "', $paths)), 0, null, $notfound);
         }
 
         return $filepaths;
diff --git a/civicrm/vendor/symfony/config/FileLocatorInterface.php b/civicrm/vendor/symfony/config/FileLocatorInterface.php
index 66057982db893aa4c625434c65cf544424ada94c..cf3c2e594df85977cb8bbc5d1970951b3c4ac63a 100644
--- a/civicrm/vendor/symfony/config/FileLocatorInterface.php
+++ b/civicrm/vendor/symfony/config/FileLocatorInterface.php
@@ -11,6 +11,8 @@
 
 namespace Symfony\Component\Config;
 
+use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException;
+
 /**
  * @author Fabien Potencier <fabien@symfony.com>
  */
@@ -25,7 +27,8 @@ interface FileLocatorInterface
      *
      * @return string|array The full path to the file or an array of file paths
      *
-     * @throws \InvalidArgumentException When file is not found
+     * @throws \InvalidArgumentException        If $name is empty
+     * @throws FileLocatorFileNotFoundException If a file is not found
      */
     public function locate($name, $currentPath = null, $first = true);
 }
diff --git a/civicrm/vendor/symfony/config/LICENSE b/civicrm/vendor/symfony/config/LICENSE
index 21d7fb9e2f29b50caca3a76f0647e94e2cc8ddc1..9e936ec0448b8549e5edf08e5ac5f01491a8bfc8 100644
--- a/civicrm/vendor/symfony/config/LICENSE
+++ b/civicrm/vendor/symfony/config/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2018 Fabien Potencier
+Copyright (c) 2004-2020 Fabien Potencier
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/civicrm/vendor/symfony/config/Loader/DelegatingLoader.php b/civicrm/vendor/symfony/config/Loader/DelegatingLoader.php
index 237009d45e6d169063f8d5b7ffcccdb5eb5b8b6b..452e81c58bf01110901f42e0dc20e0b1a57dca4a 100644
--- a/civicrm/vendor/symfony/config/Loader/DelegatingLoader.php
+++ b/civicrm/vendor/symfony/config/Loader/DelegatingLoader.php
@@ -34,7 +34,7 @@ class DelegatingLoader extends Loader
     public function load($resource, $type = null)
     {
         if (false === $loader = $this->resolver->resolve($resource, $type)) {
-            throw new FileLoaderLoadException($resource);
+            throw new FileLoaderLoadException($resource, null, null, null, $type);
         }
 
         return $loader->load($resource, $type);
diff --git a/civicrm/vendor/symfony/config/Loader/FileLoader.php b/civicrm/vendor/symfony/config/Loader/FileLoader.php
index 506eb1be082a1d5222aa25acd3d38e9abad873b8..2f1d471bddc0e7ba9648576b1d094814a1ae146e 100644
--- a/civicrm/vendor/symfony/config/Loader/FileLoader.php
+++ b/civicrm/vendor/symfony/config/Loader/FileLoader.php
@@ -13,7 +13,10 @@ namespace Symfony\Component\Config\Loader;
 
 use Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException;
 use Symfony\Component\Config\Exception\FileLoaderLoadException;
+use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException;
 use Symfony\Component\Config\FileLocatorInterface;
+use Symfony\Component\Config\Resource\FileExistenceResource;
+use Symfony\Component\Config\Resource\GlobResource;
 
 /**
  * FileLoader is the abstract class used by all built-in loaders that are file based.
@@ -22,7 +25,7 @@ use Symfony\Component\Config\FileLocatorInterface;
  */
 abstract class FileLoader extends Loader
 {
-    protected static $loading = array();
+    protected static $loading = [];
 
     protected $locator;
 
@@ -65,26 +68,75 @@ abstract class FileLoader extends Loader
      *
      * @throws FileLoaderLoadException
      * @throws FileLoaderImportCircularReferenceException
+     * @throws FileLocatorFileNotFoundException
      */
     public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null)
+    {
+        if (\is_string($resource) && \strlen($resource) !== $i = strcspn($resource, '*?{[')) {
+            $ret = [];
+            $isSubpath = 0 !== $i && false !== strpos(substr($resource, 0, $i), '/');
+            foreach ($this->glob($resource, false, $_, $ignoreErrors || !$isSubpath) as $path => $info) {
+                if (null !== $res = $this->doImport($path, 'glob' === $type ? null : $type, $ignoreErrors, $sourceResource)) {
+                    $ret[] = $res;
+                }
+                $isSubpath = true;
+            }
+
+            if ($isSubpath) {
+                return isset($ret[1]) ? $ret : (isset($ret[0]) ? $ret[0] : null);
+            }
+        }
+
+        return $this->doImport($resource, $type, $ignoreErrors, $sourceResource);
+    }
+
+    /**
+     * @internal
+     */
+    protected function glob($pattern, $recursive, &$resource = null, $ignoreErrors = false)
+    {
+        if (\strlen($pattern) === $i = strcspn($pattern, '*?{[')) {
+            $prefix = $pattern;
+            $pattern = '';
+        } elseif (0 === $i || false === strpos(substr($pattern, 0, $i), '/')) {
+            $prefix = '.';
+            $pattern = '/'.$pattern;
+        } else {
+            $prefix = \dirname(substr($pattern, 0, 1 + $i));
+            $pattern = substr($pattern, \strlen($prefix));
+        }
+
+        try {
+            $prefix = $this->locator->locate($prefix, $this->currentDir, true);
+        } catch (FileLocatorFileNotFoundException $e) {
+            if (!$ignoreErrors) {
+                throw $e;
+            }
+
+            $resource = [];
+            foreach ($e->getPaths() as $path) {
+                $resource[] = new FileExistenceResource($path);
+            }
+
+            return;
+        }
+        $resource = new GlobResource($prefix, $pattern, $recursive);
+
+        foreach ($resource as $path => $info) {
+            yield $path => $info;
+        }
+    }
+
+    private function doImport($resource, $type = null, $ignoreErrors = false, $sourceResource = null)
     {
         try {
             $loader = $this->resolve($resource, $type);
 
             if ($loader instanceof self && null !== $this->currentDir) {
-                // we fallback to the current locator to keep BC
-                // as some some loaders do not call the parent __construct()
-                // @deprecated should be removed in 3.0
-                $locator = $loader->getLocator();
-                if (null === $locator) {
-                    @trigger_error('Not calling the parent constructor in '.\get_class($loader).' which extends '.__CLASS__.' is deprecated since Symfony 2.7 and will not be supported anymore in 3.0.', E_USER_DEPRECATED);
-                    $locator = $this->locator;
-                }
-
-                $resource = $locator->locate($resource, $this->currentDir, false);
+                $resource = $loader->getLocator()->locate($resource, $this->currentDir, false);
             }
 
-            $resources = \is_array($resource) ? $resource : array($resource);
+            $resources = \is_array($resource) ? $resource : [$resource];
             for ($i = 0; $i < $resourcesCount = \count($resources); ++$i) {
                 if (isset(self::$loading[$resources[$i]])) {
                     if ($i == $resourcesCount - 1) {
@@ -99,16 +151,10 @@ abstract class FileLoader extends Loader
 
             try {
                 $ret = $loader->load($resource, $type);
-            } catch (\Exception $e) {
-                unset(self::$loading[$resource]);
-                throw $e;
-            } catch (\Throwable $e) {
+            } finally {
                 unset(self::$loading[$resource]);
-                throw $e;
             }
 
-            unset(self::$loading[$resource]);
-
             return $ret;
         } catch (FileLoaderImportCircularReferenceException $e) {
             throw $e;
@@ -119,8 +165,10 @@ abstract class FileLoader extends Loader
                     throw $e;
                 }
 
-                throw new FileLoaderLoadException($resource, $sourceResource, null, $e);
+                throw new FileLoaderLoadException($resource, $sourceResource, null, $e, $type);
             }
         }
+
+        return null;
     }
 }
diff --git a/civicrm/vendor/symfony/config/Loader/GlobFileLoader.php b/civicrm/vendor/symfony/config/Loader/GlobFileLoader.php
new file mode 100644
index 0000000000000000000000000000000000000000..f432b45ba4fe864423b8271a8e24492d4882fad5
--- /dev/null
+++ b/civicrm/vendor/symfony/config/Loader/GlobFileLoader.php
@@ -0,0 +1,36 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Config\Loader;
+
+/**
+ * GlobFileLoader loads files from a glob pattern.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class GlobFileLoader extends FileLoader
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function load($resource, $type = null)
+    {
+        return $this->import($resource);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function supports($resource, $type = null)
+    {
+        return 'glob' === $type;
+    }
+}
diff --git a/civicrm/vendor/symfony/config/Loader/Loader.php b/civicrm/vendor/symfony/config/Loader/Loader.php
index a6f8d9c66454c3ef76208214e26ea2af875de5ed..d2f2ec90b9b033e93cb5b5a3939b0572b125d1eb 100644
--- a/civicrm/vendor/symfony/config/Loader/Loader.php
+++ b/civicrm/vendor/symfony/config/Loader/Loader.php
@@ -70,7 +70,7 @@ abstract class Loader implements LoaderInterface
         $loader = null === $this->resolver ? false : $this->resolver->resolve($resource, $type);
 
         if (false === $loader) {
-            throw new FileLoaderLoadException($resource);
+            throw new FileLoaderLoadException($resource, null, null, null, $type);
         }
 
         return $loader;
diff --git a/civicrm/vendor/symfony/config/Loader/LoaderResolver.php b/civicrm/vendor/symfony/config/Loader/LoaderResolver.php
index 9299bc000f5f45a5ca52ac6e4a15b0f4e06d0797..c99efda4fd365fe6b561f406729545e0cd86018d 100644
--- a/civicrm/vendor/symfony/config/Loader/LoaderResolver.php
+++ b/civicrm/vendor/symfony/config/Loader/LoaderResolver.php
@@ -24,12 +24,12 @@ class LoaderResolver implements LoaderResolverInterface
     /**
      * @var LoaderInterface[] An array of LoaderInterface objects
      */
-    private $loaders = array();
+    private $loaders = [];
 
     /**
      * @param LoaderInterface[] $loaders An array of loaders
      */
-    public function __construct(array $loaders = array())
+    public function __construct(array $loaders = [])
     {
         foreach ($loaders as $loader) {
             $this->addLoader($loader);
diff --git a/civicrm/vendor/symfony/config/README.md b/civicrm/vendor/symfony/config/README.md
index bf400da196b224a49b83f7caf696a90216e6d3e5..0bbde55230a21e7225cde197420af171bf8c2547 100644
--- a/civicrm/vendor/symfony/config/README.md
+++ b/civicrm/vendor/symfony/config/README.md
@@ -8,7 +8,7 @@ may be (YAML, XML, INI files, or for instance a database).
 Resources
 ---------
 
-  * [Documentation](https://symfony.com/doc/current/components/config/index.html)
+  * [Documentation](https://symfony.com/doc/current/components/config.html)
   * [Contributing](https://symfony.com/doc/current/contributing/index.html)
   * [Report issues](https://github.com/symfony/symfony/issues) and
     [send Pull Requests](https://github.com/symfony/symfony/pulls)
diff --git a/civicrm/vendor/symfony/config/Resource/BCResourceInterfaceChecker.php b/civicrm/vendor/symfony/config/Resource/BCResourceInterfaceChecker.php
deleted file mode 100644
index 64c93094aca97a1eae4447300ec0ffa40c380eaa..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/config/Resource/BCResourceInterfaceChecker.php
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Config\Resource;
-
-/**
- * Resource checker for the ResourceInterface. Exists for BC.
- *
- * @author Matthias Pigulla <mp@webfactory.de>
- *
- * @deprecated since 2.8, to be removed in 3.0.
- */
-class BCResourceInterfaceChecker extends SelfCheckingResourceChecker
-{
-    public function supports(ResourceInterface $metadata)
-    {
-        /* As all resources must be instanceof ResourceInterface,
-           we support them all. */
-        return true;
-    }
-
-    public function isFresh(ResourceInterface $resource, $timestamp)
-    {
-        @trigger_error(sprintf('The class "%s" is performing resource checking through ResourceInterface::isFresh(), which is deprecated since Symfony 2.8 and will be removed in 3.0', \get_class($resource)), E_USER_DEPRECATED);
-
-        return parent::isFresh($resource, $timestamp); // For now, $metadata features the isFresh() method, so off we go (quack quack)
-    }
-}
diff --git a/civicrm/vendor/symfony/config/Resource/ClassExistenceResource.php b/civicrm/vendor/symfony/config/Resource/ClassExistenceResource.php
new file mode 100644
index 0000000000000000000000000000000000000000..fc0259f418922cfe35a2dccb0560f87ba114e63e
--- /dev/null
+++ b/civicrm/vendor/symfony/config/Resource/ClassExistenceResource.php
@@ -0,0 +1,233 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Config\Resource;
+
+/**
+ * ClassExistenceResource represents a class existence.
+ * Freshness is only evaluated against resource existence.
+ *
+ * The resource must be a fully-qualified class name.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializable
+{
+    private $resource;
+    private $exists;
+
+    private static $autoloadLevel = 0;
+    private static $autoloadedClass;
+    private static $existsCache = [];
+
+    /**
+     * @param string    $resource The fully-qualified class name
+     * @param bool|null $exists   Boolean when the existency check has already been done
+     */
+    public function __construct($resource, $exists = null)
+    {
+        $this->resource = $resource;
+        if (null !== $exists) {
+            $this->exists = [(bool) $exists, null];
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __toString()
+    {
+        return $this->resource;
+    }
+
+    /**
+     * @return string The file path to the resource
+     */
+    public function getResource()
+    {
+        return $this->resource;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @throws \ReflectionException when a parent class/interface/trait is not found
+     */
+    public function isFresh($timestamp)
+    {
+        $loaded = class_exists($this->resource, false) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
+
+        if (null !== $exists = &self::$existsCache[$this->resource]) {
+            if ($loaded) {
+                $exists = [true, null];
+            } elseif (0 >= $timestamp && !$exists[0] && null !== $exists[1]) {
+                throw new \ReflectionException($exists[1]);
+            }
+        } elseif ([false, null] === $exists = [$loaded, null]) {
+            if (!self::$autoloadLevel++) {
+                spl_autoload_register(__CLASS__.'::throwOnRequiredClass');
+            }
+            $autoloadedClass = self::$autoloadedClass;
+            self::$autoloadedClass = ltrim($this->resource, '\\');
+
+            try {
+                $exists[0] = class_exists($this->resource) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
+            } catch (\Exception $e) {
+                $exists[1] = $e->getMessage();
+
+                try {
+                    self::throwOnRequiredClass($this->resource, $e);
+                } catch (\ReflectionException $e) {
+                    if (0 >= $timestamp) {
+                        throw $e;
+                    }
+                }
+            } catch (\Throwable $e) {
+                $exists[1] = $e->getMessage();
+
+                throw $e;
+            } finally {
+                self::$autoloadedClass = $autoloadedClass;
+                if (!--self::$autoloadLevel) {
+                    spl_autoload_unregister(__CLASS__.'::throwOnRequiredClass');
+                }
+            }
+        }
+
+        if (null === $this->exists) {
+            $this->exists = $exists;
+        }
+
+        return $this->exists[0] xor !$exists[0];
+    }
+
+    /**
+     * @internal
+     */
+    public function serialize()
+    {
+        if (null === $this->exists) {
+            $this->isFresh(0);
+        }
+
+        return serialize([$this->resource, $this->exists]);
+    }
+
+    /**
+     * @internal
+     */
+    public function unserialize($serialized)
+    {
+        list($this->resource, $this->exists) = unserialize($serialized);
+
+        if (\is_bool($this->exists)) {
+            $this->exists = [$this->exists, null];
+        }
+    }
+
+    /**
+     * Throws a reflection exception when the passed class does not exist but is required.
+     *
+     * A class is considered "not required" when it's loaded as part of a "class_exists" or similar check.
+     *
+     * This function can be used as an autoload function to throw a reflection
+     * exception if the class was not found by previous autoload functions.
+     *
+     * A previous exception can be passed. In this case, the class is considered as being
+     * required totally, so if it doesn't exist, a reflection exception is always thrown.
+     * If it exists, the previous exception is rethrown.
+     *
+     * @throws \ReflectionException
+     *
+     * @internal
+     */
+    public static function throwOnRequiredClass($class, \Exception $previous = null)
+    {
+        // If the passed class is the resource being checked, we shouldn't throw.
+        if (null === $previous && self::$autoloadedClass === $class) {
+            return;
+        }
+
+        if (class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false)) {
+            if (null !== $previous) {
+                throw $previous;
+            }
+
+            return;
+        }
+
+        if ($previous instanceof \ReflectionException) {
+            throw $previous;
+        }
+
+        $message = sprintf('Class "%s" not found.', $class);
+
+        if (self::$autoloadedClass !== $class) {
+            $message = substr_replace($message, sprintf(' while loading "%s"', self::$autoloadedClass), -1, 0);
+        }
+
+        if (null !== $previous) {
+            $message = $previous->getMessage();
+        }
+
+        $e = new \ReflectionException($message, 0, $previous);
+
+        if (null !== $previous) {
+            throw $e;
+        }
+
+        $trace = debug_backtrace();
+        $autoloadFrame = [
+            'function' => 'spl_autoload_call',
+            'args' => [$class],
+        ];
+
+        if (false === $i = array_search($autoloadFrame, $trace, true)) {
+            throw $e;
+        }
+
+        if (isset($trace[++$i]['function']) && !isset($trace[$i]['class'])) {
+            switch ($trace[$i]['function']) {
+                case 'get_class_methods':
+                case 'get_class_vars':
+                case 'get_parent_class':
+                case 'is_a':
+                case 'is_subclass_of':
+                case 'class_exists':
+                case 'class_implements':
+                case 'class_parents':
+                case 'trait_exists':
+                case 'defined':
+                case 'interface_exists':
+                case 'method_exists':
+                case 'property_exists':
+                case 'is_callable':
+                    return;
+            }
+
+            $props = [
+                'file' => isset($trace[$i]['file']) ? $trace[$i]['file'] : null,
+                'line' => isset($trace[$i]['line']) ? $trace[$i]['line'] : null,
+                'trace' => \array_slice($trace, 1 + $i),
+            ];
+
+            foreach ($props as $p => $v) {
+                if (null !== $v) {
+                    $r = new \ReflectionProperty('Exception', $p);
+                    $r->setAccessible(true);
+                    $r->setValue($e, $v);
+                }
+            }
+        }
+
+        throw $e;
+    }
+}
diff --git a/civicrm/vendor/symfony/config/Resource/ComposerResource.php b/civicrm/vendor/symfony/config/Resource/ComposerResource.php
new file mode 100644
index 0000000000000000000000000000000000000000..9fb304bea8f0602c8742811d4d15b59923e274b0
--- /dev/null
+++ b/civicrm/vendor/symfony/config/Resource/ComposerResource.php
@@ -0,0 +1,84 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Config\Resource;
+
+/**
+ * ComposerResource tracks the PHP version and Composer dependencies.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class ComposerResource implements SelfCheckingResourceInterface, \Serializable
+{
+    private $vendors;
+
+    private static $runtimeVendors;
+
+    public function __construct()
+    {
+        self::refresh();
+        $this->vendors = self::$runtimeVendors;
+    }
+
+    public function getVendors()
+    {
+        return array_keys($this->vendors);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __toString()
+    {
+        return __CLASS__;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isFresh($timestamp)
+    {
+        self::refresh();
+
+        return array_values(self::$runtimeVendors) === array_values($this->vendors);
+    }
+
+    /**
+     * @internal
+     */
+    public function serialize()
+    {
+        return serialize($this->vendors);
+    }
+
+    /**
+     * @internal
+     */
+    public function unserialize($serialized)
+    {
+        $this->vendors = unserialize($serialized);
+    }
+
+    private static function refresh()
+    {
+        self::$runtimeVendors = [];
+
+        foreach (get_declared_classes() as $class) {
+            if ('C' === $class[0] && 0 === strpos($class, 'ComposerAutoloaderInit')) {
+                $r = new \ReflectionClass($class);
+                $v = \dirname(\dirname($r->getFileName()));
+                if (file_exists($v.'/composer/installed.json')) {
+                    self::$runtimeVendors[$v] = @filemtime($v.'/composer/installed.json');
+                }
+            }
+        }
+    }
+}
diff --git a/civicrm/vendor/symfony/config/Resource/DirectoryResource.php b/civicrm/vendor/symfony/config/Resource/DirectoryResource.php
index b65d40ae4400ed735c11096e4c385569e49c7959..e79b19ec2d7a0fa65c8a8e293988612e07cdaa01 100644
--- a/civicrm/vendor/symfony/config/Resource/DirectoryResource.php
+++ b/civicrm/vendor/symfony/config/Resource/DirectoryResource.php
@@ -24,11 +24,17 @@ class DirectoryResource implements SelfCheckingResourceInterface, \Serializable
     /**
      * @param string      $resource The file path to the resource
      * @param string|null $pattern  A pattern to restrict monitored files
+     *
+     * @throws \InvalidArgumentException
      */
     public function __construct($resource, $pattern = null)
     {
-        $this->resource = $resource;
+        $this->resource = realpath($resource) ?: (file_exists($resource) ? $resource : false);
         $this->pattern = $pattern;
+
+        if (false === $this->resource || !is_dir($this->resource)) {
+            throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist.', $resource));
+        }
     }
 
     /**
@@ -36,11 +42,11 @@ class DirectoryResource implements SelfCheckingResourceInterface, \Serializable
      */
     public function __toString()
     {
-        return md5(serialize(array($this->resource, $this->pattern)));
+        return md5(serialize([$this->resource, $this->pattern]));
     }
 
     /**
-     * {@inheritdoc}
+     * @return string The file path to the resource
      */
     public function getResource()
     {
@@ -98,11 +104,17 @@ class DirectoryResource implements SelfCheckingResourceInterface, \Serializable
         return true;
     }
 
+    /**
+     * @internal
+     */
     public function serialize()
     {
-        return serialize(array($this->resource, $this->pattern));
+        return serialize([$this->resource, $this->pattern]);
     }
 
+    /**
+     * @internal
+     */
     public function unserialize($serialized)
     {
         list($this->resource, $this->pattern) = unserialize($serialized);
diff --git a/civicrm/vendor/symfony/config/Resource/FileExistenceResource.php b/civicrm/vendor/symfony/config/Resource/FileExistenceResource.php
index 0e87aabb52a8163373e4af62f84ba87f45663aee..34047651b7022324cc3973375420eb8d867194d8 100644
--- a/civicrm/vendor/symfony/config/Resource/FileExistenceResource.php
+++ b/civicrm/vendor/symfony/config/Resource/FileExistenceResource.php
@@ -43,7 +43,7 @@ class FileExistenceResource implements SelfCheckingResourceInterface, \Serializa
     }
 
     /**
-     * {@inheritdoc}
+     * @return string The file path to the resource
      */
     public function getResource()
     {
@@ -59,15 +59,15 @@ class FileExistenceResource implements SelfCheckingResourceInterface, \Serializa
     }
 
     /**
-     * {@inheritdoc}
+     * @internal
      */
     public function serialize()
     {
-        return serialize(array($this->resource, $this->exists));
+        return serialize([$this->resource, $this->exists]);
     }
 
     /**
-     * {@inheritdoc}
+     * @internal
      */
     public function unserialize($serialized)
     {
diff --git a/civicrm/vendor/symfony/config/Resource/FileResource.php b/civicrm/vendor/symfony/config/Resource/FileResource.php
index 11770106e9b23c212a5e1f5df4d8564896455080..bee062377b6b26f8444820f941578bd80f747574 100644
--- a/civicrm/vendor/symfony/config/Resource/FileResource.php
+++ b/civicrm/vendor/symfony/config/Resource/FileResource.php
@@ -27,10 +27,16 @@ class FileResource implements SelfCheckingResourceInterface, \Serializable
 
     /**
      * @param string $resource The file path to the resource
+     *
+     * @throws \InvalidArgumentException
      */
     public function __construct($resource)
     {
         $this->resource = realpath($resource) ?: (file_exists($resource) ? $resource : false);
+
+        if (false === $this->resource) {
+            throw new \InvalidArgumentException(sprintf('The file "%s" does not exist.', $resource));
+        }
     }
 
     /**
@@ -38,11 +44,11 @@ class FileResource implements SelfCheckingResourceInterface, \Serializable
      */
     public function __toString()
     {
-        return (string) $this->resource;
+        return $this->resource;
     }
 
     /**
-     * {@inheritdoc}
+     * @return string The canonicalized, absolute path to the resource
      */
     public function getResource()
     {
@@ -54,18 +60,20 @@ class FileResource implements SelfCheckingResourceInterface, \Serializable
      */
     public function isFresh($timestamp)
     {
-        if (false === $this->resource || !file_exists($this->resource)) {
-            return false;
-        }
-
-        return filemtime($this->resource) <= $timestamp;
+        return false !== ($filemtime = @filemtime($this->resource)) && $filemtime <= $timestamp;
     }
 
+    /**
+     * @internal
+     */
     public function serialize()
     {
         return serialize($this->resource);
     }
 
+    /**
+     * @internal
+     */
     public function unserialize($serialized)
     {
         $this->resource = unserialize($serialized);
diff --git a/civicrm/vendor/symfony/config/Resource/GlobResource.php b/civicrm/vendor/symfony/config/Resource/GlobResource.php
new file mode 100644
index 0000000000000000000000000000000000000000..13d2ee7d703d737610fbe8e8c6e06c5f5877c532
--- /dev/null
+++ b/civicrm/vendor/symfony/config/Resource/GlobResource.php
@@ -0,0 +1,159 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Config\Resource;
+
+use Symfony\Component\Finder\Finder;
+use Symfony\Component\Finder\Glob;
+
+/**
+ * GlobResource represents a set of resources stored on the filesystem.
+ *
+ * Only existence/removal is tracked (not mtimes.)
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class GlobResource implements \IteratorAggregate, SelfCheckingResourceInterface, \Serializable
+{
+    private $prefix;
+    private $pattern;
+    private $recursive;
+    private $hash;
+
+    /**
+     * @param string $prefix    A directory prefix
+     * @param string $pattern   A glob pattern
+     * @param bool   $recursive Whether directories should be scanned recursively or not
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function __construct($prefix, $pattern, $recursive)
+    {
+        $this->prefix = realpath($prefix) ?: (file_exists($prefix) ? $prefix : false);
+        $this->pattern = $pattern;
+        $this->recursive = $recursive;
+
+        if (false === $this->prefix) {
+            throw new \InvalidArgumentException(sprintf('The path "%s" does not exist.', $prefix));
+        }
+    }
+
+    public function getPrefix()
+    {
+        return $this->prefix;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __toString()
+    {
+        return 'glob.'.$this->prefix.$this->pattern.(int) $this->recursive;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isFresh($timestamp)
+    {
+        $hash = $this->computeHash();
+
+        if (null === $this->hash) {
+            $this->hash = $hash;
+        }
+
+        return $this->hash === $hash;
+    }
+
+    /**
+     * @internal
+     */
+    public function serialize()
+    {
+        if (null === $this->hash) {
+            $this->hash = $this->computeHash();
+        }
+
+        return serialize([$this->prefix, $this->pattern, $this->recursive, $this->hash]);
+    }
+
+    /**
+     * @internal
+     */
+    public function unserialize($serialized)
+    {
+        list($this->prefix, $this->pattern, $this->recursive, $this->hash) = unserialize($serialized);
+    }
+
+    public function getIterator()
+    {
+        if (!file_exists($this->prefix) || (!$this->recursive && '' === $this->pattern)) {
+            return;
+        }
+
+        if (0 !== strpos($this->prefix, 'phar://') && false === strpos($this->pattern, '/**/') && (\defined('GLOB_BRACE') || false === strpos($this->pattern, '{'))) {
+            $paths = glob($this->prefix.$this->pattern, GLOB_NOSORT | (\defined('GLOB_BRACE') ? GLOB_BRACE : 0));
+            sort($paths);
+            foreach ($paths as $path) {
+                if ($this->recursive && is_dir($path)) {
+                    $files = iterator_to_array(new \RecursiveIteratorIterator(
+                        new \RecursiveCallbackFilterIterator(
+                            new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS),
+                            function (\SplFileInfo $file) { return '.' !== $file->getBasename()[0]; }
+                        ),
+                        \RecursiveIteratorIterator::LEAVES_ONLY
+                    ));
+                    uasort($files, function (\SplFileInfo $a, \SplFileInfo $b) {
+                        return (string) $a > (string) $b ? 1 : -1;
+                    });
+
+                    foreach ($files as $path => $info) {
+                        if ($info->isFile()) {
+                            yield $path => $info;
+                        }
+                    }
+                } elseif (is_file($path)) {
+                    yield $path => new \SplFileInfo($path);
+                }
+            }
+
+            return;
+        }
+
+        if (!class_exists(Finder::class)) {
+            throw new \LogicException(sprintf('Extended glob pattern "%s" cannot be used as the Finder component is not installed.', $this->pattern));
+        }
+
+        $finder = new Finder();
+        $regex = Glob::toRegex($this->pattern);
+        if ($this->recursive) {
+            $regex = substr_replace($regex, '(/|$)', -2, 1);
+        }
+
+        $prefixLen = \strlen($this->prefix);
+        foreach ($finder->followLinks()->sortByName()->in($this->prefix) as $path => $info) {
+            if (preg_match($regex, substr('\\' === \DIRECTORY_SEPARATOR ? str_replace('\\', '/', $path) : $path, $prefixLen)) && $info->isFile()) {
+                yield $path => $info;
+            }
+        }
+    }
+
+    private function computeHash()
+    {
+        $hash = hash_init('md5');
+
+        foreach ($this->getIterator() as $path => $info) {
+            hash_update($hash, $path."\n");
+        }
+
+        return hash_final($hash);
+    }
+}
diff --git a/civicrm/vendor/symfony/config/Resource/ReflectionClassResource.php b/civicrm/vendor/symfony/config/Resource/ReflectionClassResource.php
new file mode 100644
index 0000000000000000000000000000000000000000..4c8f89cd3f2040541e3fd9364b9d526af8cdf5a4
--- /dev/null
+++ b/civicrm/vendor/symfony/config/Resource/ReflectionClassResource.php
@@ -0,0 +1,250 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Config\Resource;
+
+use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class ReflectionClassResource implements SelfCheckingResourceInterface, \Serializable
+{
+    private $files = [];
+    private $className;
+    private $classReflector;
+    private $excludedVendors = [];
+    private $hash;
+
+    public function __construct(\ReflectionClass $classReflector, $excludedVendors = [])
+    {
+        $this->className = $classReflector->name;
+        $this->classReflector = $classReflector;
+        $this->excludedVendors = $excludedVendors;
+    }
+
+    public function isFresh($timestamp)
+    {
+        if (null === $this->hash) {
+            $this->hash = $this->computeHash();
+            $this->loadFiles($this->classReflector);
+        }
+
+        foreach ($this->files as $file => $v) {
+            if (false === $filemtime = @filemtime($file)) {
+                return false;
+            }
+
+            if ($filemtime > $timestamp) {
+                return $this->hash === $this->computeHash();
+            }
+        }
+
+        return true;
+    }
+
+    public function __toString()
+    {
+        return 'reflection.'.$this->className;
+    }
+
+    /**
+     * @internal
+     */
+    public function serialize()
+    {
+        if (null === $this->hash) {
+            $this->hash = $this->computeHash();
+            $this->loadFiles($this->classReflector);
+        }
+
+        return serialize([$this->files, $this->className, $this->hash]);
+    }
+
+    /**
+     * @internal
+     */
+    public function unserialize($serialized)
+    {
+        list($this->files, $this->className, $this->hash) = unserialize($serialized);
+    }
+
+    private function loadFiles(\ReflectionClass $class)
+    {
+        foreach ($class->getInterfaces() as $v) {
+            $this->loadFiles($v);
+        }
+        do {
+            $file = $class->getFileName();
+            if (false !== $file && file_exists($file)) {
+                foreach ($this->excludedVendors as $vendor) {
+                    if (0 === strpos($file, $vendor) && false !== strpbrk(substr($file, \strlen($vendor), 1), '/'.\DIRECTORY_SEPARATOR)) {
+                        $file = false;
+                        break;
+                    }
+                }
+                if ($file) {
+                    $this->files[$file] = null;
+                }
+            }
+            foreach ($class->getTraits() as $v) {
+                $this->loadFiles($v);
+            }
+        } while ($class = $class->getParentClass());
+    }
+
+    private function computeHash()
+    {
+        if (null === $this->classReflector) {
+            try {
+                $this->classReflector = new \ReflectionClass($this->className);
+            } catch (\ReflectionException $e) {
+                // the class does not exist anymore
+                return false;
+            }
+        }
+        $hash = hash_init('md5');
+
+        foreach ($this->generateSignature($this->classReflector) as $info) {
+            hash_update($hash, $info);
+        }
+
+        return hash_final($hash);
+    }
+
+    private function generateSignature(\ReflectionClass $class)
+    {
+        yield $class->getDocComment();
+        yield (int) $class->isFinal();
+        yield (int) $class->isAbstract();
+
+        if ($class->isTrait()) {
+            yield print_r(class_uses($class->name), true);
+        } else {
+            yield print_r(class_parents($class->name), true);
+            yield print_r(class_implements($class->name), true);
+            yield print_r($class->getConstants(), true);
+        }
+
+        if (!$class->isInterface()) {
+            $defaults = $class->getDefaultProperties();
+
+            foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED) as $p) {
+                yield $p->getDocComment().$p;
+                yield print_r(isset($defaults[$p->name]) && !\is_object($defaults[$p->name]) ? $defaults[$p->name] : null, true);
+            }
+        }
+
+        if (\defined('HHVM_VERSION')) {
+            foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $m) {
+                // workaround HHVM bug with variadics, see https://github.com/facebook/hhvm/issues/5762
+                yield preg_replace('/^  @@.*/m', '', new ReflectionMethodHhvmWrapper($m->class, $m->name));
+            }
+        } else {
+            foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $m) {
+                $defaults = [];
+                $parametersWithUndefinedConstants = [];
+                foreach ($m->getParameters() as $p) {
+                    if (!$p->isDefaultValueAvailable()) {
+                        $defaults[$p->name] = null;
+
+                        continue;
+                    }
+
+                    if (!$p->isDefaultValueConstant() || \defined($p->getDefaultValueConstantName())) {
+                        $defaults[$p->name] = $p->getDefaultValue();
+
+                        continue;
+                    }
+
+                    $defaults[$p->name] = $p->getDefaultValueConstantName();
+                    $parametersWithUndefinedConstants[$p->name] = true;
+                }
+
+                if (!$parametersWithUndefinedConstants) {
+                    yield preg_replace('/^  @@.*/m', '', $m);
+                } else {
+                    $stack = [
+                        $m->getDocComment(),
+                        $m->getName(),
+                        $m->isAbstract(),
+                        $m->isFinal(),
+                        $m->isStatic(),
+                        $m->isPublic(),
+                        $m->isPrivate(),
+                        $m->isProtected(),
+                        $m->returnsReference(),
+                        \PHP_VERSION_ID >= 70000 && $m->hasReturnType() ? (\PHP_VERSION_ID >= 70100 ? $m->getReturnType()->getName() : (string) $m->getReturnType()) : '',
+                    ];
+
+                    foreach ($m->getParameters() as $p) {
+                        if (!isset($parametersWithUndefinedConstants[$p->name])) {
+                            $stack[] = (string) $p;
+                        } else {
+                            $stack[] = $p->isOptional();
+                            $stack[] = \PHP_VERSION_ID >= 70000 && $p->hasType() ? (\PHP_VERSION_ID >= 70100 ? $p->getType()->getName() : (string) $p->getType()) : '';
+                            $stack[] = $p->isPassedByReference();
+                            $stack[] = \PHP_VERSION_ID >= 50600 ? $p->isVariadic() : '';
+                            $stack[] = $p->getName();
+                        }
+                    }
+
+                    yield implode(',', $stack);
+                }
+
+                yield print_r($defaults, true);
+            }
+        }
+
+        if ($class->isAbstract() || $class->isInterface() || $class->isTrait()) {
+            return;
+        }
+
+        if (interface_exists(EventSubscriberInterface::class, false) && $class->isSubclassOf(EventSubscriberInterface::class)) {
+            yield EventSubscriberInterface::class;
+            yield print_r(\call_user_func([$class->name, 'getSubscribedEvents']), true);
+        }
+
+        if (interface_exists(ServiceSubscriberInterface::class, false) && $class->isSubclassOf(ServiceSubscriberInterface::class)) {
+            yield ServiceSubscriberInterface::class;
+            yield print_r(\call_user_func([$class->name, 'getSubscribedServices']), true);
+        }
+    }
+}
+
+/**
+ * @internal
+ */
+class ReflectionMethodHhvmWrapper extends \ReflectionMethod
+{
+    public function getParameters()
+    {
+        $params = [];
+
+        foreach (parent::getParameters() as $i => $p) {
+            $params[] = new ReflectionParameterHhvmWrapper([$this->class, $this->name], $i);
+        }
+
+        return $params;
+    }
+}
+
+/**
+ * @internal
+ */
+class ReflectionParameterHhvmWrapper extends \ReflectionParameter
+{
+    public function getDefaultValue()
+    {
+        return [$this->isVariadic(), $this->isDefaultValueAvailable() ? parent::getDefaultValue() : null];
+    }
+}
diff --git a/civicrm/vendor/symfony/config/Resource/ResourceInterface.php b/civicrm/vendor/symfony/config/Resource/ResourceInterface.php
index 55b3e09648a6b0049bff7ddf260a911ff78af9bc..d98fd427a25eb64e12805fa9d51ea9c9e535c2c2 100644
--- a/civicrm/vendor/symfony/config/Resource/ResourceInterface.php
+++ b/civicrm/vendor/symfony/config/Resource/ResourceInterface.php
@@ -30,29 +30,4 @@ interface ResourceInterface
      * @return string A string representation unique to the underlying Resource
      */
     public function __toString();
-
-    /**
-     * Returns true if the resource has not been updated since the given timestamp.
-     *
-     * @param int $timestamp The last time the resource was loaded
-     *
-     * @return bool True if the resource has not been updated, false otherwise
-     *
-     * @deprecated since 2.8, to be removed in 3.0. If your resource can check itself for
-     *             freshness implement the SelfCheckingResourceInterface instead.
-     */
-    public function isFresh($timestamp);
-
-    /**
-     * Returns the tied resource.
-     *
-     * @return mixed The resource
-     *
-     * @deprecated since 2.8, to be removed in 3.0. As there are many different kinds of resource,
-     *             a single getResource() method does not make sense at the interface level. You
-     *             can still call getResource() on implementing classes, probably after performing
-     *             a type check. If you know the concrete type of Resource at hand, the return value
-     *             of this method may make sense to you.
-     */
-    public function getResource();
 }
diff --git a/civicrm/vendor/symfony/config/ResourceCheckerConfigCache.php b/civicrm/vendor/symfony/config/ResourceCheckerConfigCache.php
index 81c1b972734c7ffdc572b844f0a33e21f190cc0d..092b95bf0188b17ba38c63db81f8da267ccef189 100644
--- a/civicrm/vendor/symfony/config/ResourceCheckerConfigCache.php
+++ b/civicrm/vendor/symfony/config/ResourceCheckerConfigCache.php
@@ -29,15 +29,15 @@ class ResourceCheckerConfigCache implements ConfigCacheInterface
     private $file;
 
     /**
-     * @var ResourceCheckerInterface[]
+     * @var iterable|ResourceCheckerInterface[]
      */
     private $resourceCheckers;
 
     /**
-     * @param string                     $file             The absolute cache path
-     * @param ResourceCheckerInterface[] $resourceCheckers The ResourceCheckers to use for the freshness check
+     * @param string                              $file             The absolute cache path
+     * @param iterable|ResourceCheckerInterface[] $resourceCheckers The ResourceCheckers to use for the freshness check
      */
-    public function __construct($file, array $resourceCheckers = array())
+    public function __construct($file, $resourceCheckers = [])
     {
         $this->file = $file;
         $this->resourceCheckers = $resourceCheckers;
@@ -68,42 +68,28 @@ class ResourceCheckerConfigCache implements ConfigCacheInterface
             return false;
         }
 
-        if (!$this->resourceCheckers) {
+        if ($this->resourceCheckers instanceof \Traversable && !$this->resourceCheckers instanceof \Countable) {
+            $this->resourceCheckers = iterator_to_array($this->resourceCheckers);
+        }
+
+        if (!\count($this->resourceCheckers)) {
             return true; // shortcut - if we don't have any checkers we don't need to bother with the meta file at all
         }
 
         $metadata = $this->getMetaFile();
+
         if (!is_file($metadata)) {
             return false;
         }
 
-        $e = null;
-        $meta = false;
-        $time = filemtime($this->file);
-        $signalingException = new \UnexpectedValueException();
-        $prevUnserializeHandler = ini_set('unserialize_callback_func', '');
-        $prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context) use (&$prevErrorHandler, $signalingException) {
-            if (E_WARNING === $type && 'Class __PHP_Incomplete_Class has no unserializer' === $msg) {
-                throw $signalingException;
-            }
-
-            return $prevErrorHandler ? $prevErrorHandler($type, $msg, $file, $line, $context) : false;
-        });
+        $meta = $this->safelyUnserialize($metadata);
 
-        try {
-            $meta = unserialize(file_get_contents($metadata));
-        } catch (\Error $e) {
-        } catch (\Exception $e) {
-        }
-        restore_error_handler();
-        ini_set('unserialize_callback_func', $prevUnserializeHandler);
-        if (null !== $e && $e !== $signalingException) {
-            throw $e;
-        }
         if (false === $meta) {
             return false;
         }
 
+        $time = filemtime($this->file);
+
         foreach ($meta as $resource) {
             /* @var ResourceInterface $resource */
             foreach ($this->resourceCheckers as $checker) {
@@ -135,7 +121,7 @@ class ResourceCheckerConfigCache implements ConfigCacheInterface
         $mode = 0666;
         $umask = umask();
         $filesystem = new Filesystem();
-        $filesystem->dumpFile($this->file, $content, null);
+        $filesystem->dumpFile($this->file, $content);
         try {
             $filesystem->chmod($this->file, $mode, $umask);
         } catch (IOException $e) {
@@ -143,7 +129,7 @@ class ResourceCheckerConfigCache implements ConfigCacheInterface
         }
 
         if (null !== $metadata) {
-            $filesystem->dumpFile($this->getMetaFile(), serialize($metadata), null);
+            $filesystem->dumpFile($this->getMetaFile(), serialize($metadata));
             try {
                 $filesystem->chmod($this->getMetaFile(), $mode, $umask);
             } catch (IOException $e) {
@@ -165,4 +151,33 @@ class ResourceCheckerConfigCache implements ConfigCacheInterface
     {
         return $this->file.'.meta';
     }
+
+    private function safelyUnserialize($file)
+    {
+        $e = null;
+        $meta = false;
+        $content = file_get_contents($file);
+        $signalingException = new \UnexpectedValueException();
+        $prevUnserializeHandler = ini_set('unserialize_callback_func', '');
+        $prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = []) use (&$prevErrorHandler, $signalingException) {
+            if (__FILE__ === $file) {
+                throw $signalingException;
+            }
+
+            return $prevErrorHandler ? $prevErrorHandler($type, $msg, $file, $line, $context) : false;
+        });
+
+        try {
+            $meta = unserialize($content);
+        } catch (\Error $e) {
+        } catch (\Exception $e) {
+        }
+        restore_error_handler();
+        ini_set('unserialize_callback_func', $prevUnserializeHandler);
+        if (null !== $e && $e !== $signalingException) {
+            throw $e;
+        }
+
+        return $meta;
+    }
 }
diff --git a/civicrm/vendor/symfony/config/ResourceCheckerConfigCacheFactory.php b/civicrm/vendor/symfony/config/ResourceCheckerConfigCacheFactory.php
index 586c5bf280c28eb3e38d477acdf55f6cb28a4b4c..c00fa7db1b037c81dcbea16836d63792bb8d98a7 100644
--- a/civicrm/vendor/symfony/config/ResourceCheckerConfigCacheFactory.php
+++ b/civicrm/vendor/symfony/config/ResourceCheckerConfigCacheFactory.php
@@ -19,15 +19,12 @@ namespace Symfony\Component\Config;
  */
 class ResourceCheckerConfigCacheFactory implements ConfigCacheFactoryInterface
 {
-    /**
-     * @var ResourceCheckerInterface[]
-     */
-    private $resourceCheckers = array();
+    private $resourceCheckers = [];
 
     /**
-     * @param ResourceCheckerInterface[] $resourceCheckers
+     * @param iterable|ResourceCheckerInterface[] $resourceCheckers
      */
-    public function __construct(array $resourceCheckers = array())
+    public function __construct($resourceCheckers = [])
     {
         $this->resourceCheckers = $resourceCheckers;
     }
diff --git a/civicrm/vendor/symfony/config/Util/Exception/InvalidXmlException.php b/civicrm/vendor/symfony/config/Util/Exception/InvalidXmlException.php
new file mode 100644
index 0000000000000000000000000000000000000000..a335bbd2eed7c2afcc3f85e75f35eb46c43392cb
--- /dev/null
+++ b/civicrm/vendor/symfony/config/Util/Exception/InvalidXmlException.php
@@ -0,0 +1,21 @@
+<?php
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Config\Util\Exception;
+
+/**
+ * Exception class for when XML parsing with an XSD schema file path or a callable validator produces errors unrelated
+ * to the actual XML parsing.
+ *
+ * @author Ole Rößner <ole@roessner.it>
+ */
+class InvalidXmlException extends XmlParsingException
+{
+}
diff --git a/civicrm/vendor/symfony/config/Util/Exception/XmlParsingException.php b/civicrm/vendor/symfony/config/Util/Exception/XmlParsingException.php
new file mode 100644
index 0000000000000000000000000000000000000000..9bceed660baede3fc8c7c4c121df4c87c8b3d82b
--- /dev/null
+++ b/civicrm/vendor/symfony/config/Util/Exception/XmlParsingException.php
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Config\Util\Exception;
+
+/**
+ * Exception class for when XML cannot be parsed properly.
+ *
+ * @author Ole Rößner <ole@roessner.it>
+ */
+class XmlParsingException extends \InvalidArgumentException
+{
+}
diff --git a/civicrm/vendor/symfony/config/Util/XmlUtils.php b/civicrm/vendor/symfony/config/Util/XmlUtils.php
index 5ecbd2efdc262099c0c6075ffe8f720602cd3a24..c925f315e5ed3954020f2319644217a040b70d4a 100644
--- a/civicrm/vendor/symfony/config/Util/XmlUtils.php
+++ b/civicrm/vendor/symfony/config/Util/XmlUtils.php
@@ -11,6 +11,9 @@
 
 namespace Symfony\Component\Config\Util;
 
+use Symfony\Component\Config\Util\Exception\InvalidXmlException;
+use Symfony\Component\Config\Util\Exception\XmlParsingException;
+
 /**
  * XMLUtils is a bunch of utility methods to XML operations.
  *
@@ -18,6 +21,7 @@ namespace Symfony\Component\Config\Util;
  *
  * @author Fabien Potencier <fabien@symfony.com>
  * @author Martin Hasoň <martin.hason@gmail.com>
+ * @author Ole Rößner <ole@roessner.it>
  */
 class XmlUtils
 {
@@ -29,27 +33,23 @@ class XmlUtils
     }
 
     /**
-     * Loads an XML file.
+     * Parses an XML string.
      *
-     * @param string               $file             An XML file path
+     * @param string               $content          An XML string
      * @param string|callable|null $schemaOrCallable An XSD schema file path, a callable, or null to disable validation
      *
      * @return \DOMDocument
      *
-     * @throws \InvalidArgumentException When loading of XML file returns error
-     * @throws \RuntimeException         When DOM extension is missing
+     * @throws XmlParsingException When parsing of XML file returns error
+     * @throws InvalidXmlException When parsing of XML with schema or callable produces any errors unrelated to the XML parsing itself
+     * @throws \RuntimeException   When DOM extension is missing
      */
-    public static function loadFile($file, $schemaOrCallable = null)
+    public static function parse($content, $schemaOrCallable = null)
     {
         if (!\extension_loaded('dom')) {
             throw new \RuntimeException('Extension DOM is required.');
         }
 
-        $content = @file_get_contents($file);
-        if ('' === trim($content)) {
-            throw new \InvalidArgumentException(sprintf('File %s does not contain valid XML, it is empty.', $file));
-        }
-
         $internalErrors = libxml_use_internal_errors(true);
         $disableEntities = libxml_disable_entity_loader(true);
         libxml_clear_errors();
@@ -59,7 +59,7 @@ class XmlUtils
         if (!$dom->loadXML($content, LIBXML_NONET | (\defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0))) {
             libxml_disable_entity_loader($disableEntities);
 
-            throw new \InvalidArgumentException(implode("\n", static::getXmlErrors($internalErrors)));
+            throw new XmlParsingException(implode("\n", static::getXmlErrors($internalErrors)));
         }
 
         $dom->normalizeDocument();
@@ -69,7 +69,7 @@ class XmlUtils
 
         foreach ($dom->childNodes as $child) {
             if (XML_DOCUMENT_TYPE_NODE === $child->nodeType) {
-                throw new \InvalidArgumentException('Document types are not allowed.');
+                throw new XmlParsingException('Document types are not allowed.');
             }
         }
 
@@ -90,15 +90,15 @@ class XmlUtils
             } else {
                 libxml_use_internal_errors($internalErrors);
 
-                throw new \InvalidArgumentException('The schemaOrCallable argument has to be a valid path to XSD file or callable.');
+                throw new XmlParsingException('The schemaOrCallable argument has to be a valid path to XSD file or callable.');
             }
 
             if (!$valid) {
                 $messages = static::getXmlErrors($internalErrors);
                 if (empty($messages)) {
-                    $messages = array(sprintf('The XML file "%s" is not valid.', $file));
+                    throw new InvalidXmlException('The XML is not valid.', 0, $e);
                 }
-                throw new \InvalidArgumentException(implode("\n", $messages), 0, $e);
+                throw new XmlParsingException(implode("\n", $messages), 0, $e);
             }
         }
 
@@ -108,6 +108,41 @@ class XmlUtils
         return $dom;
     }
 
+    /**
+     * Loads an XML file.
+     *
+     * @param string               $file             An XML file path
+     * @param string|callable|null $schemaOrCallable An XSD schema file path, a callable, or null to disable validation
+     *
+     * @return \DOMDocument
+     *
+     * @throws \InvalidArgumentException When loading of XML file returns error
+     * @throws XmlParsingException       When XML parsing returns any errors
+     * @throws \RuntimeException         When DOM extension is missing
+     */
+    public static function loadFile($file, $schemaOrCallable = null)
+    {
+        if (!is_file($file)) {
+            throw new \InvalidArgumentException(sprintf('Resource "%s" is not a file.', $file));
+        }
+
+        if (!is_readable($file)) {
+            throw new \InvalidArgumentException(sprintf('File "%s" is not readable.', $file));
+        }
+
+        $content = @file_get_contents($file);
+
+        if ('' === trim($content)) {
+            throw new \InvalidArgumentException(sprintf('File "%s" does not contain valid XML, it is empty.', $file));
+        }
+
+        try {
+            return static::parse($content, $schemaOrCallable);
+        } catch (InvalidXmlException $e) {
+            throw new XmlParsingException(sprintf('The XML file "%s" is not valid.', $file), 0, $e->getPrevious());
+        }
+    }
+
     /**
      * Converts a \DOMElement object to a PHP array.
      *
@@ -126,15 +161,15 @@ class XmlUtils
      * @param \DOMElement $element     A \DOMElement instance
      * @param bool        $checkPrefix Check prefix in an element or an attribute name
      *
-     * @return array A PHP array
+     * @return mixed
      */
     public static function convertDomElementToArray(\DOMElement $element, $checkPrefix = true)
     {
         $prefix = (string) $element->prefix;
         $empty = true;
-        $config = array();
+        $config = [];
         foreach ($element->attributes as $name => $node) {
-            if ($checkPrefix && !\in_array((string) $node->prefix, array('', $prefix), true)) {
+            if ($checkPrefix && !\in_array((string) $node->prefix, ['', $prefix], true)) {
                 continue;
             }
             $config[$name] = static::phpize($node->value);
@@ -156,7 +191,7 @@ class XmlUtils
                 $key = $node->localName;
                 if (isset($config[$key])) {
                     if (!\is_array($config[$key]) || !\is_int(key($config[$key]))) {
-                        $config[$key] = array($config[$key]);
+                        $config[$key] = [$config[$key]];
                     }
                     $config[$key][] = $value;
                 } else {
@@ -193,7 +228,7 @@ class XmlUtils
 
         switch (true) {
             case 'null' === $lowercaseValue:
-                return;
+                return null;
             case ctype_digit($value):
                 $raw = $value;
                 $cast = (int) $value;
@@ -208,13 +243,13 @@ class XmlUtils
                 return true;
             case 'false' === $lowercaseValue:
                 return false;
-            case isset($value[1]) && '0b' == $value[0].$value[1]:
+            case isset($value[1]) && '0b' == $value[0].$value[1] && preg_match('/^0b[01]*$/', $value):
                 return bindec($value);
             case is_numeric($value):
                 return '0x' === $value[0].$value[1] ? hexdec($value) : (float) $value;
             case preg_match('/^0x[0-9a-f]++$/i', $value):
                 return hexdec($value);
-            case preg_match('/^(-|\+)?[0-9]+(\.[0-9]+)?$/', $value):
+            case preg_match('/^[+-]?[0-9]+(\.[0-9]+)?$/', $value):
                 return (float) $value;
             default:
                 return $value;
@@ -223,7 +258,7 @@ class XmlUtils
 
     protected static function getXmlErrors($internalErrors)
     {
-        $errors = array();
+        $errors = [];
         foreach (libxml_get_errors() as $error) {
             $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
                 LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
diff --git a/civicrm/vendor/symfony/config/composer.json b/civicrm/vendor/symfony/config/composer.json
index ee1322ccdba1644cb090484d9590df7d337a0be8..c04294dd4cba5646eeb540e49bd3a2a7bcfc9bba 100644
--- a/civicrm/vendor/symfony/config/composer.json
+++ b/civicrm/vendor/symfony/config/composer.json
@@ -16,12 +16,19 @@
         }
     ],
     "require": {
-        "php": ">=5.3.9",
-        "symfony/filesystem": "~2.3|~3.0.0",
+        "php": "^5.5.9|>=7.0.8",
+        "symfony/filesystem": "~2.8|~3.0|~4.0",
         "symfony/polyfill-ctype": "~1.8"
     },
     "require-dev": {
-        "symfony/yaml": "~2.7|~3.0.0"
+        "symfony/finder": "~3.3|~4.0",
+        "symfony/yaml": "~3.0|~4.0",
+        "symfony/dependency-injection": "~3.3|~4.0",
+        "symfony/event-dispatcher": "~3.3|~4.0"
+    },
+    "conflict": {
+        "symfony/finder": "<3.3",
+        "symfony/dependency-injection": "<3.3"
     },
     "suggest": {
         "symfony/yaml": "To use the yaml reference dumper"
@@ -35,7 +42,7 @@
     "minimum-stability": "dev",
     "extra": {
         "branch-alias": {
-            "dev-master": "2.8-dev"
+            "dev-master": "3.4-dev"
         }
     }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Alias.php b/civicrm/vendor/symfony/dependency-injection/Alias.php
index eaf7f00ccd446d4ec4cf5f5979a1b224f9a72d9b..de14c5ea9512bcafe5bf3b49c934aa584a830d08 100644
--- a/civicrm/vendor/symfony/dependency-injection/Alias.php
+++ b/civicrm/vendor/symfony/dependency-injection/Alias.php
@@ -15,6 +15,7 @@ class Alias
 {
     private $id;
     private $public;
+    private $private;
 
     /**
      * @param string $id     Alias identifier
@@ -22,8 +23,9 @@ class Alias
      */
     public function __construct($id, $public = true)
     {
-        $this->id = strtolower($id);
+        $this->id = (string) $id;
         $this->public = $public;
+        $this->private = 2 > \func_num_args();
     }
 
     /**
@@ -40,10 +42,44 @@ class Alias
      * Sets if this Alias is public.
      *
      * @param bool $boolean If this Alias should be public
+     *
+     * @return $this
      */
     public function setPublic($boolean)
     {
         $this->public = (bool) $boolean;
+        $this->private = false;
+
+        return $this;
+    }
+
+    /**
+     * Sets if this Alias is private.
+     *
+     * When set, the "private" state has a higher precedence than "public".
+     * In version 3.4, a "private" alias always remains publicly accessible,
+     * but triggers a deprecation notice when accessed from the container,
+     * so that the alias can be made really private in 4.0.
+     *
+     * @param bool $boolean
+     *
+     * @return $this
+     */
+    public function setPrivate($boolean)
+    {
+        $this->private = (bool) $boolean;
+
+        return $this;
+    }
+
+    /**
+     * Whether this alias is private.
+     *
+     * @return bool
+     */
+    public function isPrivate()
+    {
+        return $this->private;
     }
 
     /**
diff --git a/civicrm/vendor/symfony/dependency-injection/Argument/ArgumentInterface.php b/civicrm/vendor/symfony/dependency-injection/Argument/ArgumentInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..b46eb77be749e2dd85637656beb14087ec9da4ee
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Argument/ArgumentInterface.php
@@ -0,0 +1,27 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Argument;
+
+/**
+ * Represents a complex argument containing nested values.
+ *
+ * @author Titouan Galopin <galopintitouan@gmail.com>
+ */
+interface ArgumentInterface
+{
+    /**
+     * @return array
+     */
+    public function getValues();
+
+    public function setValues(array $values);
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Argument/BoundArgument.php b/civicrm/vendor/symfony/dependency-injection/Argument/BoundArgument.php
new file mode 100644
index 0000000000000000000000000000000000000000..a20698440c3cfb4ae6abd2561395e56daf065cdc
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Argument/BoundArgument.php
@@ -0,0 +1,46 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Argument;
+
+/**
+ * @author Guilhem Niot <guilhem.niot@gmail.com>
+ */
+final class BoundArgument implements ArgumentInterface
+{
+    private static $sequence = 0;
+
+    private $value;
+    private $identifier;
+    private $used;
+
+    public function __construct($value)
+    {
+        $this->value = $value;
+        $this->identifier = ++self::$sequence;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getValues()
+    {
+        return [$this->value, $this->identifier, $this->used];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setValues(array $values)
+    {
+        list($this->value, $this->identifier, $this->used) = $values;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Argument/IteratorArgument.php b/civicrm/vendor/symfony/dependency-injection/Argument/IteratorArgument.php
new file mode 100644
index 0000000000000000000000000000000000000000..2d796d2d8f99292dcd2f87a10c00e2ccb758c434
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Argument/IteratorArgument.php
@@ -0,0 +1,55 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Argument;
+
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * Represents a collection of values to lazily iterate over.
+ *
+ * @author Titouan Galopin <galopintitouan@gmail.com>
+ */
+class IteratorArgument implements ArgumentInterface
+{
+    private $values;
+
+    /**
+     * @param Reference[] $values
+     */
+    public function __construct(array $values)
+    {
+        $this->setValues($values);
+    }
+
+    /**
+     * @return array The values to lazily iterate over
+     */
+    public function getValues()
+    {
+        return $this->values;
+    }
+
+    /**
+     * @param Reference[] $values The service references to lazily iterate over
+     */
+    public function setValues(array $values)
+    {
+        foreach ($values as $k => $v) {
+            if (null !== $v && !$v instanceof Reference) {
+                throw new InvalidArgumentException(sprintf('An IteratorArgument must hold only Reference instances, "%s" given.', \is_object($v) ? \get_class($v) : \gettype($v)));
+            }
+        }
+
+        $this->values = $values;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Argument/RewindableGenerator.php b/civicrm/vendor/symfony/dependency-injection/Argument/RewindableGenerator.php
new file mode 100644
index 0000000000000000000000000000000000000000..b00a36c34f542985c6922e2bd0d549eab342d29b
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Argument/RewindableGenerator.php
@@ -0,0 +1,46 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Argument;
+
+/**
+ * @internal
+ */
+class RewindableGenerator implements \IteratorAggregate, \Countable
+{
+    private $generator;
+    private $count;
+
+    /**
+     * @param int|callable $count
+     */
+    public function __construct(callable $generator, $count)
+    {
+        $this->generator = $generator;
+        $this->count = $count;
+    }
+
+    public function getIterator()
+    {
+        $g = $this->generator;
+
+        return $g();
+    }
+
+    public function count()
+    {
+        if (\is_callable($count = $this->count)) {
+            $this->count = $count();
+        }
+
+        return $this->count;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Argument/ServiceClosureArgument.php b/civicrm/vendor/symfony/dependency-injection/Argument/ServiceClosureArgument.php
new file mode 100644
index 0000000000000000000000000000000000000000..6331affa48387f2fa695df2920c602d7be62c4e1
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Argument/ServiceClosureArgument.php
@@ -0,0 +1,50 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Argument;
+
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * Represents a service wrapped in a memoizing closure.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class ServiceClosureArgument implements ArgumentInterface
+{
+    private $values;
+
+    public function __construct(Reference $reference)
+    {
+        $this->values = [$reference];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getValues()
+    {
+        return $this->values;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setValues(array $values)
+    {
+        if ([0] !== array_keys($values) || !($values[0] instanceof Reference || null === $values[0])) {
+            throw new InvalidArgumentException('A ServiceClosureArgument must hold one and only one Reference.');
+        }
+
+        $this->values = $values;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Argument/TaggedIteratorArgument.php b/civicrm/vendor/symfony/dependency-injection/Argument/TaggedIteratorArgument.php
new file mode 100644
index 0000000000000000000000000000000000000000..f00e533919f9b2be7105764ee993493fdc952e59
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Argument/TaggedIteratorArgument.php
@@ -0,0 +1,37 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Argument;
+
+/**
+ * Represents a collection of services found by tag name to lazily iterate over.
+ *
+ * @author Roland Franssen <franssen.roland@gmail.com>
+ */
+class TaggedIteratorArgument extends IteratorArgument
+{
+    private $tag;
+
+    /**
+     * @param string $tag
+     */
+    public function __construct($tag)
+    {
+        parent::__construct([]);
+
+        $this->tag = (string) $tag;
+    }
+
+    public function getTag()
+    {
+        return $this->tag;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/CHANGELOG.md b/civicrm/vendor/symfony/dependency-injection/CHANGELOG.md
index 27cb2d58a4af1e3b84e0b442afb6255cecfb7aa8..a004161b963d9e303e9ddbbf3da63c56e76cb3be 100644
--- a/civicrm/vendor/symfony/dependency-injection/CHANGELOG.md
+++ b/civicrm/vendor/symfony/dependency-injection/CHANGELOG.md
@@ -1,6 +1,65 @@
 CHANGELOG
 =========
 
+3.4.0
+-----
+
+ * moved the `ExtensionCompilerPass` to before-optimization passes with priority -1000
+ * deprecated "public-by-default" definitions and aliases, the new default will be "private" in 4.0
+ * added `EnvVarProcessorInterface` and corresponding "container.env_var_processor" tag for processing env vars
+ * added support for ignore-on-uninitialized references
+ * deprecated service auto-registration while autowiring
+ * deprecated the ability to check for the initialization of a private service with the `Container::initialized()` method
+ * deprecated support for top-level anonymous services in XML
+ * deprecated case insensitivity of parameter names
+ * deprecated the `ResolveDefinitionTemplatesPass` class in favor of `ResolveChildDefinitionsPass`
+ * added `TaggedIteratorArgument` with YAML (`!tagged foo`) and XML (`<service type="tagged"/>`) support
+ * deprecated `AutowireExceptionPass` and `AutowirePass::getAutowiringExceptions()`, use `Definition::addError()` and the `DefinitionErrorExceptionPass` instead
+
+
+3.3.0
+-----
+
+ * deprecated autowiring services based on the types they implement;
+   rename (or alias) your services to their FQCN id to make them autowirable
+ * added "ServiceSubscriberInterface" - to allow for per-class explicit service-locator definitions
+ * added "container.service_locator" tag for defining service-locator services
+ * added anonymous services support in YAML configuration files using the `!service` tag.
+ * added "TypedReference" and "ServiceClosureArgument" for creating service-locator services
+ * added `ServiceLocator` - a PSR-11 container holding a set of services to be lazily loaded
+ * added "instanceof" section for local interface-defined configs
+ * added prototype services for PSR4-based discovery and registration
+ * added `ContainerBuilder::getReflectionClass()` for retrieving and tracking reflection class info
+ * deprecated `ContainerBuilder::getClassResource()`, use `ContainerBuilder::getReflectionClass()` or `ContainerBuilder::addObjectResource()` instead
+ * added `ContainerBuilder::fileExists()` for checking and tracking file or directory existence
+ * deprecated autowiring-types, use aliases instead
+ * added support for omitting the factory class name in a service definition if the definition class is set
+ * deprecated case insensitivity of service identifiers
+ * added "iterator" argument type for lazy iteration over a set of values and services
+ * added file-wide configurable defaults for service attributes "public", "tags",
+   "autowire" and "autoconfigure"
+ * made the "class" attribute optional, using the "id" as fallback
+ * using the `PhpDumper` with an uncompiled `ContainerBuilder` is deprecated and
+   will not be supported anymore in 4.0
+ * deprecated the `DefinitionDecorator` class in favor of `ChildDefinition`
+ * allow config files to be loaded using a glob pattern
+ * [BC BREAK] the `NullDumper` class is now final
+
+3.2.0
+-----
+
+ * allowed to prioritize compiler passes by introducing a third argument to `PassConfig::addPass()`, to `Compiler::addPass` and to `ContainerBuilder::addCompilerPass()`
+ * added support for PHP constants in YAML configuration files
+ * deprecated the ability to set or unset a private service with the `Container::set()` method
+ * deprecated the ability to check for the existence of a private service with the `Container::has()` method
+ * deprecated the ability to request a private service with the `Container::get()` method
+ * deprecated support for generating a dumped `Container` without populating the method map
+
+3.0.0
+-----
+
+ * removed all deprecated codes from 2.x versions
+
 2.8.0
 -----
 
diff --git a/civicrm/vendor/symfony/dependency-injection/ChildDefinition.php b/civicrm/vendor/symfony/dependency-injection/ChildDefinition.php
new file mode 100644
index 0000000000000000000000000000000000000000..123b387475bda98d09afc548f50f5a024b696516
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/ChildDefinition.php
@@ -0,0 +1,126 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\Exception\BadMethodCallException;
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException;
+
+/**
+ * This definition extends another definition.
+ *
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ */
+class ChildDefinition extends Definition
+{
+    private $parent;
+
+    /**
+     * @param string $parent The id of Definition instance to decorate
+     */
+    public function __construct($parent)
+    {
+        $this->parent = $parent;
+        $this->setPrivate(false);
+    }
+
+    /**
+     * Returns the Definition to inherit from.
+     *
+     * @return string
+     */
+    public function getParent()
+    {
+        return $this->parent;
+    }
+
+    /**
+     * Sets the Definition to inherit from.
+     *
+     * @param string $parent
+     *
+     * @return $this
+     */
+    public function setParent($parent)
+    {
+        $this->parent = $parent;
+
+        return $this;
+    }
+
+    /**
+     * Gets an argument to pass to the service constructor/factory method.
+     *
+     * If replaceArgument() has been used to replace an argument, this method
+     * will return the replacement value.
+     *
+     * @param int|string $index
+     *
+     * @return mixed The argument value
+     *
+     * @throws OutOfBoundsException When the argument does not exist
+     */
+    public function getArgument($index)
+    {
+        if (\array_key_exists('index_'.$index, $this->arguments)) {
+            return $this->arguments['index_'.$index];
+        }
+
+        return parent::getArgument($index);
+    }
+
+    /**
+     * You should always use this method when overwriting existing arguments
+     * of the parent definition.
+     *
+     * If you directly call setArguments() keep in mind that you must follow
+     * certain conventions when you want to overwrite the arguments of the
+     * parent definition, otherwise your arguments will only be appended.
+     *
+     * @param int|string $index
+     * @param mixed      $value
+     *
+     * @return $this
+     *
+     * @throws InvalidArgumentException when $index isn't an integer
+     */
+    public function replaceArgument($index, $value)
+    {
+        if (\is_int($index)) {
+            $this->arguments['index_'.$index] = $value;
+        } elseif (0 === strpos($index, '$')) {
+            $this->arguments[$index] = $value;
+        } else {
+            throw new InvalidArgumentException('The argument must be an existing index or the name of a constructor\'s parameter.');
+        }
+
+        return $this;
+    }
+
+    /**
+     * @internal
+     */
+    public function setAutoconfigured($autoconfigured)
+    {
+        throw new BadMethodCallException('A ChildDefinition cannot be autoconfigured.');
+    }
+
+    /**
+     * @internal
+     */
+    public function setInstanceofConditionals(array $instanceof)
+    {
+        throw new BadMethodCallException('A ChildDefinition cannot have instanceof conditionals set on it.');
+    }
+}
+
+class_alias(ChildDefinition::class, DefinitionDecorator::class);
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/AbstractRecursivePass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/AbstractRecursivePass.php
new file mode 100644
index 0000000000000000000000000000000000000000..27969e70672544153b54f76751d4ecca52ebbaf1
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/AbstractRecursivePass.php
@@ -0,0 +1,174 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+abstract class AbstractRecursivePass implements CompilerPassInterface
+{
+    /**
+     * @var ContainerBuilder
+     */
+    protected $container;
+    protected $currentId;
+
+    /**
+     * {@inheritdoc}
+     */
+    public function process(ContainerBuilder $container)
+    {
+        $this->container = $container;
+
+        try {
+            $this->processValue($container->getDefinitions(), true);
+        } finally {
+            $this->container = null;
+        }
+    }
+
+    /**
+     * Processes a value found in a definition tree.
+     *
+     * @param mixed $value
+     * @param bool  $isRoot
+     *
+     * @return mixed The processed value
+     */
+    protected function processValue($value, $isRoot = false)
+    {
+        if (\is_array($value)) {
+            foreach ($value as $k => $v) {
+                if ($isRoot) {
+                    $this->currentId = $k;
+                }
+                if ($v !== $processedValue = $this->processValue($v, $isRoot)) {
+                    $value[$k] = $processedValue;
+                }
+            }
+        } elseif ($value instanceof ArgumentInterface) {
+            $value->setValues($this->processValue($value->getValues()));
+        } elseif ($value instanceof Definition) {
+            $value->setArguments($this->processValue($value->getArguments()));
+            $value->setProperties($this->processValue($value->getProperties()));
+            $value->setMethodCalls($this->processValue($value->getMethodCalls()));
+
+            $changes = $value->getChanges();
+            if (isset($changes['factory'])) {
+                $value->setFactory($this->processValue($value->getFactory()));
+            }
+            if (isset($changes['configurator'])) {
+                $value->setConfigurator($this->processValue($value->getConfigurator()));
+            }
+        }
+
+        return $value;
+    }
+
+    /**
+     * @param bool $required
+     *
+     * @return \ReflectionFunctionAbstract|null
+     *
+     * @throws RuntimeException
+     */
+    protected function getConstructor(Definition $definition, $required)
+    {
+        if ($definition->isSynthetic()) {
+            return null;
+        }
+
+        if (\is_string($factory = $definition->getFactory())) {
+            if (!\function_exists($factory)) {
+                throw new RuntimeException(sprintf('Invalid service "%s": function "%s" does not exist.', $this->currentId, $factory));
+            }
+            $r = new \ReflectionFunction($factory);
+            if (false !== $r->getFileName() && file_exists($r->getFileName())) {
+                $this->container->fileExists($r->getFileName());
+            }
+
+            return $r;
+        }
+
+        if ($factory) {
+            list($class, $method) = $factory;
+            if ($class instanceof Reference) {
+                $class = $this->container->findDefinition((string) $class)->getClass();
+            } elseif (null === $class) {
+                $class = $definition->getClass();
+            }
+            if ('__construct' === $method) {
+                throw new RuntimeException(sprintf('Invalid service "%s": "__construct()" cannot be used as a factory method.', $this->currentId));
+            }
+
+            return $this->getReflectionMethod(new Definition($class), $method);
+        }
+
+        $class = $definition->getClass();
+
+        try {
+            if (!$r = $this->container->getReflectionClass($class)) {
+                throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.', $this->currentId, $class));
+            }
+        } catch (\ReflectionException $e) {
+            throw new RuntimeException(sprintf('Invalid service "%s": '.lcfirst($e->getMessage()), $this->currentId));
+        }
+        if (!$r = $r->getConstructor()) {
+            if ($required) {
+                throw new RuntimeException(sprintf('Invalid service "%s": class%s has no constructor.', $this->currentId, sprintf($class !== $this->currentId ? ' "%s"' : '', $class)));
+            }
+        } elseif (!$r->isPublic()) {
+            throw new RuntimeException(sprintf('Invalid service "%s": %s must be public.', $this->currentId, sprintf($class !== $this->currentId ? 'constructor of class "%s"' : 'its constructor', $class)));
+        }
+
+        return $r;
+    }
+
+    /**
+     * @param string $method
+     *
+     * @throws RuntimeException
+     *
+     * @return \ReflectionFunctionAbstract
+     */
+    protected function getReflectionMethod(Definition $definition, $method)
+    {
+        if ('__construct' === $method) {
+            return $this->getConstructor($definition, true);
+        }
+
+        if (!$class = $definition->getClass()) {
+            throw new RuntimeException(sprintf('Invalid service "%s": the class is not set.', $this->currentId));
+        }
+
+        if (!$r = $this->container->getReflectionClass($class)) {
+            throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.', $this->currentId, $class));
+        }
+
+        if (!$r->hasMethod($method)) {
+            throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" does not exist.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method));
+        }
+
+        $r = $r->getMethod($method);
+        if (!$r->isPublic()) {
+            throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" must be public.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method));
+        }
+
+        return $r;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/AnalyzeServiceReferencesPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/AnalyzeServiceReferencesPass.php
index ce9d556eaf8e603d9695bd8ee8ab7f019117032f..bff9d42079e121a701ef98769223b5b37888d841 100644
--- a/civicrm/vendor/symfony/dependency-injection/Compiler/AnalyzeServiceReferencesPass.php
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/AnalyzeServiceReferencesPass.php
@@ -11,9 +11,14 @@
 
 namespace Symfony\Component\DependencyInjection\Compiler;
 
+use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Component\DependencyInjection\ExpressionLanguage;
 use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\ExpressionLanguage\Expression;
 
 /**
  * Run this pass before passes that need to know more about the relation of
@@ -24,20 +29,23 @@ use Symfony\Component\DependencyInjection\Reference;
  *
  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  */
-class AnalyzeServiceReferencesPass implements RepeatablePassInterface
+class AnalyzeServiceReferencesPass extends AbstractRecursivePass implements RepeatablePassInterface
 {
     private $graph;
-    private $container;
-    private $currentId;
     private $currentDefinition;
     private $onlyConstructorArguments;
+    private $hasProxyDumper;
+    private $lazy;
+    private $expressionLanguage;
+    private $byConstructor;
 
     /**
      * @param bool $onlyConstructorArguments Sets this Service Reference pass to ignore method calls
      */
-    public function __construct($onlyConstructorArguments = false)
+    public function __construct($onlyConstructorArguments = false, $hasProxyDumper = true)
     {
         $this->onlyConstructorArguments = (bool) $onlyConstructorArguments;
+        $this->hasProxyDumper = (bool) $hasProxyDumper;
     }
 
     /**
@@ -56,68 +64,77 @@ class AnalyzeServiceReferencesPass implements RepeatablePassInterface
         $this->container = $container;
         $this->graph = $container->getCompiler()->getServiceReferenceGraph();
         $this->graph->clear();
+        $this->lazy = false;
+        $this->byConstructor = false;
 
-        foreach ($container->getDefinitions() as $id => $definition) {
-            if ($definition->isSynthetic() || $definition->isAbstract()) {
-                continue;
-            }
+        foreach ($container->getAliases() as $id => $alias) {
+            $targetId = $this->getDefinitionId((string) $alias);
+            $this->graph->connect($id, $alias, $targetId, $this->getDefinition($targetId), null);
+        }
 
-            $this->currentId = $id;
-            $this->currentDefinition = $definition;
+        parent::process($container);
+    }
 
-            $this->processArguments($definition->getArguments());
-            if ($definition->getFactoryService(false)) {
-                $this->processArguments(array(new Reference($definition->getFactoryService(false))));
-            }
-            if (\is_array($definition->getFactory())) {
-                $this->processArguments($definition->getFactory());
-            }
+    protected function processValue($value, $isRoot = false)
+    {
+        $lazy = $this->lazy;
 
-            if (!$this->onlyConstructorArguments) {
-                $this->processArguments($definition->getMethodCalls());
-                $this->processArguments($definition->getProperties());
-                if ($definition->getConfigurator()) {
-                    $this->processArguments(array($definition->getConfigurator()));
-                }
-            }
-        }
+        if ($value instanceof ArgumentInterface) {
+            $this->lazy = true;
+            parent::processValue($value->getValues());
+            $this->lazy = $lazy;
 
-        foreach ($container->getAliases() as $id => $alias) {
-            $this->graph->connect($id, $alias, (string) $alias, $this->getDefinition((string) $alias), null);
+            return $value;
         }
-    }
+        if ($value instanceof Expression) {
+            $this->getExpressionLanguage()->compile((string) $value, ['this' => 'container']);
 
-    /**
-     * Processes service definitions for arguments to find relationships for the service graph.
-     *
-     * @param array $arguments An array of Reference or Definition objects relating to service definitions
-     */
-    private function processArguments(array $arguments)
-    {
-        foreach ($arguments as $argument) {
-            if (\is_array($argument)) {
-                $this->processArguments($argument);
-            } elseif ($argument instanceof Reference) {
-                $this->graph->connect(
-                    $this->currentId,
-                    $this->currentDefinition,
-                    $this->getDefinitionId((string) $argument),
-                    $this->getDefinition((string) $argument),
-                    $argument
-                );
-            } elseif ($argument instanceof Definition) {
-                $this->processArguments($argument->getArguments());
-                $this->processArguments($argument->getMethodCalls());
-                $this->processArguments($argument->getProperties());
-
-                if (\is_array($argument->getFactory())) {
-                    $this->processArguments($argument->getFactory());
-                }
-                if ($argument->getFactoryService(false)) {
-                    $this->processArguments(array(new Reference($argument->getFactoryService(false))));
-                }
+            return $value;
+        }
+        if ($value instanceof Reference) {
+            $targetId = $this->getDefinitionId((string) $value);
+            $targetDefinition = $this->getDefinition($targetId);
+
+            $this->graph->connect(
+                $this->currentId,
+                $this->currentDefinition,
+                $targetId,
+                $targetDefinition,
+                $value,
+                $this->lazy || ($this->hasProxyDumper && $targetDefinition && $targetDefinition->isLazy()),
+                ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior(),
+                $this->byConstructor
+            );
+
+            return $value;
+        }
+        if (!$value instanceof Definition) {
+            return parent::processValue($value, $isRoot);
+        }
+        if ($isRoot) {
+            if ($value->isSynthetic() || $value->isAbstract()) {
+                return $value;
             }
+            $this->currentDefinition = $value;
+        } elseif ($this->currentDefinition === $value) {
+            return $value;
         }
+        $this->lazy = false;
+
+        $byConstructor = $this->byConstructor;
+        $this->byConstructor = $isRoot || $byConstructor;
+        $this->processValue($value->getFactory());
+        $this->processValue($value->getArguments());
+        $this->byConstructor = $byConstructor;
+
+        if (!$this->onlyConstructorArguments) {
+            $this->processValue($value->getProperties());
+            $this->processValue($value->getMethodCalls());
+            $this->processValue($value->getConfigurator());
+        }
+        $this->lazy = $lazy;
+
+        return $value;
     }
 
     /**
@@ -129,8 +146,6 @@ class AnalyzeServiceReferencesPass implements RepeatablePassInterface
      */
     private function getDefinition($id)
     {
-        $id = $this->getDefinitionId($id);
-
         return null === $id ? null : $this->container->getDefinition($id);
     }
 
@@ -141,9 +156,37 @@ class AnalyzeServiceReferencesPass implements RepeatablePassInterface
         }
 
         if (!$this->container->hasDefinition($id)) {
-            return;
+            return null;
+        }
+
+        return $this->container->normalizeId($id);
+    }
+
+    private function getExpressionLanguage()
+    {
+        if (null === $this->expressionLanguage) {
+            if (!class_exists(ExpressionLanguage::class)) {
+                throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
+            }
+
+            $providers = $this->container->getExpressionLanguageProviders();
+            $this->expressionLanguage = new ExpressionLanguage(null, $providers, function ($arg) {
+                if ('""' === substr_replace($arg, '', 1, -1)) {
+                    $id = stripcslashes(substr($arg, 1, -1));
+                    $id = $this->getDefinitionId($id);
+
+                    $this->graph->connect(
+                        $this->currentId,
+                        $this->currentDefinition,
+                        $id,
+                        $this->getDefinition($id)
+                    );
+                }
+
+                return sprintf('$this->get(%s)', $arg);
+            });
         }
 
-        return $id;
+        return $this->expressionLanguage;
     }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/AutoAliasServicePass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/AutoAliasServicePass.php
index c1f05e03ec02c9dd602a89c22b4d7157d2973843..03420683a205a649e4e60b97b05a3c3e82d9f52d 100644
--- a/civicrm/vendor/symfony/dependency-injection/Compiler/AutoAliasServicePass.php
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/AutoAliasServicePass.php
@@ -33,7 +33,7 @@ class AutoAliasServicePass implements CompilerPassInterface
 
                 $aliasId = $container->getParameterBag()->resolveValue($tag['format']);
                 if ($container->hasDefinition($aliasId) || $container->hasAlias($aliasId)) {
-                    $container->setAlias($serviceId, new Alias($aliasId));
+                    $container->setAlias($serviceId, new Alias($aliasId, true));
                 }
             }
         }
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/AutowireExceptionPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/AutowireExceptionPass.php
new file mode 100644
index 0000000000000000000000000000000000000000..9a4c74d3590edf5bb697103b30dda3a1bcd1ac68
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/AutowireExceptionPass.php
@@ -0,0 +1,74 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Compiler;
+
+@trigger_error('The '.__NAMESPACE__.'\AutowireExceptionPass class is deprecated since Symfony 3.4 and will be removed in 4.0. Use the DefinitionErrorExceptionPass class instead.', E_USER_DEPRECATED);
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+/**
+ * Throws autowire exceptions from AutowirePass for definitions that still exist.
+ *
+ * @deprecated since version 3.4, will be removed in 4.0.
+ *
+ * @author Ryan Weaver <ryan@knpuniversity.com>
+ */
+class AutowireExceptionPass implements CompilerPassInterface
+{
+    private $autowirePass;
+    private $inlineServicePass;
+
+    public function __construct(AutowirePass $autowirePass, InlineServiceDefinitionsPass $inlineServicePass)
+    {
+        $this->autowirePass = $autowirePass;
+        $this->inlineServicePass = $inlineServicePass;
+    }
+
+    public function process(ContainerBuilder $container)
+    {
+        // the pass should only be run once
+        if (null === $this->autowirePass || null === $this->inlineServicePass) {
+            return;
+        }
+
+        $inlinedIds = $this->inlineServicePass->getInlinedServiceIds();
+        $exceptions = $this->autowirePass->getAutowiringExceptions();
+
+        // free up references
+        $this->autowirePass = null;
+        $this->inlineServicePass = null;
+
+        foreach ($exceptions as $exception) {
+            if ($this->doesServiceExistInTheContainer($exception->getServiceId(), $container, $inlinedIds)) {
+                throw $exception;
+            }
+        }
+    }
+
+    private function doesServiceExistInTheContainer($serviceId, ContainerBuilder $container, array $inlinedIds)
+    {
+        if ($container->hasDefinition($serviceId)) {
+            return true;
+        }
+
+        // was the service inlined? Of so, does its parent service exist?
+        if (isset($inlinedIds[$serviceId])) {
+            foreach ($inlinedIds[$serviceId] as $parentId) {
+                if ($this->doesServiceExistInTheContainer($parentId, $container, $inlinedIds)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/AutowirePass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/AutowirePass.php
index b2795485531615ff281e0fa7479a4bb5a86e7360..91b279c77a256aa562f2a605288be4797b61a8f8 100644
--- a/civicrm/vendor/symfony/dependency-injection/Compiler/AutowirePass.php
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/AutowirePass.php
@@ -11,143 +11,249 @@
 
 namespace Symfony\Component\DependencyInjection\Compiler;
 
+use Symfony\Component\Config\Resource\ClassExistenceResource;
+use Symfony\Component\DependencyInjection\Config\AutowireServiceResource;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Exception\AutowiringFailedException;
 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
-use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper;
+use Symfony\Component\DependencyInjection\TypedReference;
 
 /**
- * Guesses constructor arguments of services definitions and try to instantiate services if necessary.
+ * Inspects existing service definitions and wires the autowired ones using the type hints of their classes.
  *
  * @author Kévin Dunglas <dunglas@gmail.com>
+ * @author Nicolas Grekas <p@tchwork.com>
  */
-class AutowirePass implements CompilerPassInterface
+class AutowirePass extends AbstractRecursivePass
 {
-    private $container;
-    private $reflectionClasses = array();
-    private $definedTypes = array();
+    private $definedTypes = [];
     private $types;
-    private $notGuessableTypes = array();
-    private $autowired = array();
+    private $ambiguousServiceTypes;
+    private $autowired = [];
+    private $lastFailure;
+    private $throwOnAutowiringException;
+    private $autowiringExceptions = [];
+    private $strictMode;
+
+    /**
+     * @param bool $throwOnAutowireException Errors can be retrieved via Definition::getErrors()
+     */
+    public function __construct($throwOnAutowireException = true)
+    {
+        $this->throwOnAutowiringException = $throwOnAutowireException;
+    }
+
+    /**
+     * @deprecated since version 3.4, to be removed in 4.0.
+     *
+     * @return AutowiringFailedException[]
+     */
+    public function getAutowiringExceptions()
+    {
+        @trigger_error('Calling AutowirePass::getAutowiringExceptions() is deprecated since Symfony 3.4 and will be removed in 4.0. Use Definition::getErrors() instead.', E_USER_DEPRECATED);
+
+        return $this->autowiringExceptions;
+    }
 
     /**
      * {@inheritdoc}
      */
     public function process(ContainerBuilder $container)
     {
-        $throwingAutoloader = function ($class) { throw new \ReflectionException(sprintf('Class %s does not exist', $class)); };
-        spl_autoload_register($throwingAutoloader);
+        // clear out any possibly stored exceptions from before
+        $this->autowiringExceptions = [];
+        $this->strictMode = $container->hasParameter('container.autowiring.strict_mode') && $container->getParameter('container.autowiring.strict_mode');
 
         try {
-            $this->container = $container;
-            foreach ($container->getDefinitions() as $id => $definition) {
-                if ($definition->isAutowired()) {
-                    $this->completeDefinition($id, $definition);
-                }
-            }
-        } catch (\Exception $e) {
-        } catch (\Throwable $e) {
+            parent::process($container);
+        } finally {
+            $this->definedTypes = [];
+            $this->types = null;
+            $this->ambiguousServiceTypes = null;
+            $this->autowired = [];
         }
+    }
 
-        spl_autoload_unregister($throwingAutoloader);
+    /**
+     * Creates a resource to help know if this service has changed.
+     *
+     * @return AutowireServiceResource
+     *
+     * @deprecated since version 3.3, to be removed in 4.0. Use ContainerBuilder::getReflectionClass() instead.
+     */
+    public static function createResourceForClass(\ReflectionClass $reflectionClass)
+    {
+        @trigger_error('The '.__METHOD__.'() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use ContainerBuilder::getReflectionClass() instead.', E_USER_DEPRECATED);
 
-        // Free memory and remove circular reference to container
-        $this->container = null;
-        $this->reflectionClasses = array();
-        $this->definedTypes = array();
-        $this->types = null;
-        $this->notGuessableTypes = array();
-        $this->autowired = array();
+        $metadata = [];
 
-        if (isset($e)) {
-            throw $e;
+        foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) {
+            if (!$reflectionMethod->isStatic()) {
+                $metadata[$reflectionMethod->name] = self::getResourceMetadataForMethod($reflectionMethod);
+            }
         }
+
+        return new AutowireServiceResource($reflectionClass->name, $reflectionClass->getFileName(), $metadata);
     }
 
     /**
-     * Wires the given definition.
-     *
-     * @param string     $id
-     * @param Definition $definition
-     *
-     * @throws RuntimeException
+     * {@inheritdoc}
      */
-    private function completeDefinition($id, Definition $definition)
+    protected function processValue($value, $isRoot = false)
     {
-        if ($definition->getFactory() || $definition->getFactoryClass(false) || $definition->getFactoryService(false)) {
-            throw new RuntimeException(sprintf('Service "%s" can use either autowiring or a factory, not both.', $id));
+        try {
+            return $this->doProcessValue($value, $isRoot);
+        } catch (AutowiringFailedException $e) {
+            if ($this->throwOnAutowiringException) {
+                throw $e;
+            }
+
+            $this->autowiringExceptions[] = $e;
+            $this->container->getDefinition($this->currentId)->addError($e->getMessage());
+
+            return parent::processValue($value, $isRoot);
         }
+    }
 
-        if (!$reflectionClass = $this->getReflectionClass($id, $definition)) {
-            return;
+    private function doProcessValue($value, $isRoot = false)
+    {
+        if ($value instanceof TypedReference) {
+            if ($ref = $this->getAutowiredReference($value, $value->getRequiringClass() ? sprintf('for "%s" in "%s"', $value->getType(), $value->getRequiringClass()) : '')) {
+                return $ref;
+            }
+            $this->container->log($this, $this->createTypeNotFoundMessage($value, 'it'));
         }
+        $value = parent::processValue($value, $isRoot);
 
-        $this->container->addClassResource($reflectionClass);
+        if (!$value instanceof Definition || !$value->isAutowired() || $value->isAbstract() || !$value->getClass()) {
+            return $value;
+        }
+        if (!$reflectionClass = $this->container->getReflectionClass($value->getClass(), false)) {
+            $this->container->log($this, sprintf('Skipping service "%s": Class or interface "%s" cannot be loaded.', $this->currentId, $value->getClass()));
 
-        if (!$constructor = $reflectionClass->getConstructor()) {
-            return;
+            return $value;
         }
-        $parameters = $constructor->getParameters();
-        if (method_exists('ReflectionMethod', 'isVariadic') && $constructor->isVariadic()) {
-            array_pop($parameters);
+
+        $methodCalls = $value->getMethodCalls();
+
+        try {
+            $constructor = $this->getConstructor($value, false);
+        } catch (RuntimeException $e) {
+            throw new AutowiringFailedException($this->currentId, $e->getMessage(), 0, $e);
         }
 
-        $arguments = $definition->getArguments();
-        foreach ($parameters as $index => $parameter) {
-            if (array_key_exists($index, $arguments) && '' !== $arguments[$index]) {
-                continue;
+        if ($constructor) {
+            array_unshift($methodCalls, [$constructor, $value->getArguments()]);
+        }
+
+        $methodCalls = $this->autowireCalls($reflectionClass, $methodCalls);
+
+        if ($constructor) {
+            list(, $arguments) = array_shift($methodCalls);
+
+            if ($arguments !== $value->getArguments()) {
+                $value->setArguments($arguments);
             }
+        }
 
-            try {
-                if (!$typeHint = $parameter->getClass()) {
-                    if (isset($arguments[$index])) {
+        if ($methodCalls !== $value->getMethodCalls()) {
+            $value->setMethodCalls($methodCalls);
+        }
+
+        return $value;
+    }
+
+    /**
+     * @return array
+     */
+    private function autowireCalls(\ReflectionClass $reflectionClass, array $methodCalls)
+    {
+        foreach ($methodCalls as $i => $call) {
+            list($method, $arguments) = $call;
+
+            if ($method instanceof \ReflectionFunctionAbstract) {
+                $reflectionMethod = $method;
+            } else {
+                $definition = new Definition($reflectionClass->name);
+                try {
+                    $reflectionMethod = $this->getReflectionMethod($definition, $method);
+                } catch (RuntimeException $e) {
+                    if ($definition->getFactory()) {
                         continue;
                     }
+                    throw $e;
+                }
+            }
 
-                    // no default value? Then fail
-                    if (!$parameter->isOptional()) {
-                        throw new RuntimeException(sprintf('Unable to autowire argument index %d ($%s) for the service "%s". If this is an object, give it a type-hint. Otherwise, specify this argument\'s value explicitly.', $index, $parameter->name, $id));
-                    }
+            $arguments = $this->autowireMethod($reflectionMethod, $arguments);
 
-                    // specifically pass the default value
-                    $arguments[$index] = $parameter->getDefaultValue();
+            if ($arguments !== $call[1]) {
+                $methodCalls[$i][1] = $arguments;
+            }
+        }
 
-                    continue;
-                }
+        return $methodCalls;
+    }
 
-                if (isset($this->autowired[$typeHint->name])) {
-                    $arguments[$index] = $this->autowired[$typeHint->name] ? new Reference($this->autowired[$typeHint->name]) : null;
-                    continue;
-                }
+    /**
+     * Autowires the constructor or a method.
+     *
+     * @return array The autowired arguments
+     *
+     * @throws AutowiringFailedException
+     */
+    private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, array $arguments)
+    {
+        $class = $reflectionMethod instanceof \ReflectionMethod ? $reflectionMethod->class : $this->currentId;
+        $method = $reflectionMethod->name;
+        $parameters = $reflectionMethod->getParameters();
+        if (method_exists('ReflectionMethod', 'isVariadic') && $reflectionMethod->isVariadic()) {
+            array_pop($parameters);
+        }
 
-                if (null === $this->types) {
-                    $this->populateAvailableTypes();
-                }
+        foreach ($parameters as $index => $parameter) {
+            if (\array_key_exists($index, $arguments) && '' !== $arguments[$index]) {
+                continue;
+            }
 
-                if (isset($this->types[$typeHint->name]) && !isset($this->notGuessableTypes[$typeHint->name])) {
-                    $value = new Reference($this->types[$typeHint->name]);
-                } else {
-                    try {
-                        $value = $this->createAutowiredDefinition($typeHint, $id);
-                    } catch (RuntimeException $e) {
-                        if ($parameter->isDefaultValueAvailable()) {
-                            $value = $parameter->getDefaultValue();
-                        } elseif ($parameter->allowsNull()) {
-                            $value = null;
-                        } else {
-                            throw $e;
-                        }
-                        $this->autowired[$typeHint->name] = false;
-                    }
+            $type = ProxyHelper::getTypeHint($reflectionMethod, $parameter, true);
+
+            if (!$type) {
+                if (isset($arguments[$index])) {
+                    continue;
                 }
-            } catch (\ReflectionException $e) {
-                // Typehint against a non-existing class
 
+                // no default value? Then fail
                 if (!$parameter->isDefaultValueAvailable()) {
-                    throw new RuntimeException(sprintf('Cannot autowire argument %s for %s because the type-hinted class does not exist (%s).', $index + 1, $definition->getClass(), $e->getMessage()), 0, $e);
+                    // For core classes, isDefaultValueAvailable() can
+                    // be false when isOptional() returns true. If the
+                    // argument *is* optional, allow it to be missing
+                    if ($parameter->isOptional()) {
+                        continue;
+                    }
+                    $type = ProxyHelper::getTypeHint($reflectionMethod, $parameter, false);
+                    $type = $type ? sprintf('is type-hinted "%s"', $type) : 'has no type-hint';
+
+                    throw new AutowiringFailedException($this->currentId, sprintf('Cannot autowire service "%s": argument "$%s" of method "%s()" %s, you should configure its value explicitly.', $this->currentId, $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method, $type));
                 }
 
-                $value = $parameter->getDefaultValue();
+                // specifically pass the default value
+                $arguments[$index] = $parameter->getDefaultValue();
+
+                continue;
+            }
+
+            if (!$value = $this->getAutowiredReference($ref = new TypedReference($type, $type, !$parameter->isOptional() ? $class : ''), 'for '.sprintf('argument "$%s" of method "%s()"', $parameter->name, $class.'::'.$method))) {
+                $failureMessage = $this->createTypeNotFoundMessage($ref, sprintf('argument "$%s" of method "%s()"', $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method));
+
+                if ($parameter->isDefaultValueAvailable()) {
+                    $value = $parameter->getDefaultValue();
+                } elseif (!$parameter->allowsNull()) {
+                    throw new AutowiringFailedException($this->currentId, $failureMessage);
+                }
+                $this->container->log($this, $failureMessage);
             }
 
             $arguments[$index] = $value;
@@ -166,41 +272,96 @@ class AutowirePass implements CompilerPassInterface
         // it's possible index 1 was set, then index 0, then 2, etc
         // make sure that we re-order so they're injected as expected
         ksort($arguments);
-        $definition->setArguments($arguments);
+
+        return $arguments;
+    }
+
+    /**
+     * @return TypedReference|null A reference to the service matching the given type, if any
+     */
+    private function getAutowiredReference(TypedReference $reference, $deprecationMessage)
+    {
+        $this->lastFailure = null;
+        $type = $reference->getType();
+
+        if ($type !== $this->container->normalizeId($reference) || ($this->container->has($type) && !$this->container->findDefinition($type)->isAbstract())) {
+            return $reference;
+        }
+
+        if (null === $this->types) {
+            $this->populateAvailableTypes($this->strictMode);
+        }
+
+        if (isset($this->definedTypes[$type])) {
+            return new TypedReference($this->types[$type], $type);
+        }
+
+        if (!$this->strictMode && isset($this->types[$type])) {
+            $message = 'Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won\'t be supported in version 4.0.';
+            if ($aliasSuggestion = $this->getAliasesSuggestionForType($type = $reference->getType(), $deprecationMessage)) {
+                $message .= ' '.$aliasSuggestion;
+            } else {
+                $message .= sprintf(' You should %s the "%s" service to "%s" instead.', isset($this->types[$this->types[$type]]) ? 'alias' : 'rename (or alias)', $this->types[$type], $type);
+            }
+
+            @trigger_error($message, E_USER_DEPRECATED);
+
+            return new TypedReference($this->types[$type], $type);
+        }
+
+        if (!$reference->canBeAutoregistered() || isset($this->types[$type]) || isset($this->ambiguousServiceTypes[$type])) {
+            return null;
+        }
+
+        if (isset($this->autowired[$type])) {
+            return $this->autowired[$type] ? new TypedReference($this->autowired[$type], $type) : null;
+        }
+
+        if (!$this->strictMode) {
+            return $this->createAutowiredDefinition($type);
+        }
+
+        return null;
     }
 
     /**
      * Populates the list of available types.
      */
-    private function populateAvailableTypes()
+    private function populateAvailableTypes($onlyAutowiringTypes = false)
     {
-        $this->types = array();
+        $this->types = [];
+        if (!$onlyAutowiringTypes) {
+            $this->ambiguousServiceTypes = [];
+        }
 
         foreach ($this->container->getDefinitions() as $id => $definition) {
-            $this->populateAvailableType($id, $definition);
+            $this->populateAvailableType($id, $definition, $onlyAutowiringTypes);
         }
     }
 
     /**
      * Populates the list of available types for a given definition.
      *
-     * @param string     $id
-     * @param Definition $definition
+     * @param string $id
      */
-    private function populateAvailableType($id, Definition $definition)
+    private function populateAvailableType($id, Definition $definition, $onlyAutowiringTypes)
     {
         // Never use abstract services
         if ($definition->isAbstract()) {
             return;
         }
 
-        foreach ($definition->getAutowiringTypes() as $type) {
+        foreach ($definition->getAutowiringTypes(false) as $type) {
             $this->definedTypes[$type] = true;
             $this->types[$type] = $id;
-            unset($this->notGuessableTypes[$type]);
+            unset($this->ambiguousServiceTypes[$type]);
         }
 
-        if (!$reflectionClass = $this->getReflectionClass($id, $definition)) {
+        if ($onlyAutowiringTypes) {
+            return;
+        }
+
+        if (preg_match('/^\d+_[^~]++~[._a-zA-Z\d]{7}$/', $id) || $definition->isDeprecated() || !$reflectionClass = $this->container->getReflectionClass($definition->getClass(), false)) {
             return;
         }
 
@@ -225,110 +386,186 @@ class AutowirePass implements CompilerPassInterface
             return;
         }
 
-        if (!isset($this->types[$type])) {
-            $this->types[$type] = $id;
+        // is this already a type/class that is known to match multiple services?
+        if (isset($this->ambiguousServiceTypes[$type])) {
+            $this->ambiguousServiceTypes[$type][] = $id;
 
             return;
         }
 
-        if ($this->types[$type] === $id) {
+        // check to make sure the type doesn't match multiple services
+        if (!isset($this->types[$type]) || $this->types[$type] === $id) {
+            $this->types[$type] = $id;
+
             return;
         }
 
-        if (!isset($this->notGuessableTypes[$type])) {
-            $this->notGuessableTypes[$type] = true;
-            $this->types[$type] = (array) $this->types[$type];
+        // keep an array of all services matching this type
+        if (!isset($this->ambiguousServiceTypes[$type])) {
+            $this->ambiguousServiceTypes[$type] = [$this->types[$type]];
+            unset($this->types[$type]);
         }
-
-        $this->types[$type][] = $id;
+        $this->ambiguousServiceTypes[$type][] = $id;
     }
 
     /**
      * Registers a definition for the type if possible or throws an exception.
      *
-     * @param \ReflectionClass $typeHint
-     * @param string           $id
-     *
-     * @return Reference A reference to the registered definition
+     * @param string $type
      *
-     * @throws RuntimeException
+     * @return TypedReference|null A reference to the registered definition
      */
-    private function createAutowiredDefinition(\ReflectionClass $typeHint, $id)
+    private function createAutowiredDefinition($type)
     {
-        if (isset($this->notGuessableTypes[$typeHint->name])) {
-            $classOrInterface = $typeHint->isInterface() ? 'interface' : 'class';
-            $matchingServices = implode(', ', $this->types[$typeHint->name]);
-
-            throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". Multiple services exist for this %s (%s).', $typeHint->name, $id, $classOrInterface, $matchingServices));
+        if (!($typeHint = $this->container->getReflectionClass($type, false)) || !$typeHint->isInstantiable()) {
+            return null;
         }
 
-        if (!$typeHint->isInstantiable()) {
-            $classOrInterface = $typeHint->isInterface() ? 'interface' : 'class';
-            throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". No services were found matching this %s and it cannot be auto-registered.', $typeHint->name, $id, $classOrInterface));
+        $currentId = $this->currentId;
+        $this->currentId = $type;
+        $this->autowired[$type] = $argumentId = sprintf('autowired.%s', $type);
+        $argumentDefinition = new Definition($type);
+        $argumentDefinition->setPublic(false);
+        $argumentDefinition->setAutowired(true);
+
+        try {
+            $originalThrowSetting = $this->throwOnAutowiringException;
+            $this->throwOnAutowiringException = true;
+            $this->processValue($argumentDefinition, true);
+            $this->container->setDefinition($argumentId, $argumentDefinition);
+        } catch (AutowiringFailedException $e) {
+            $this->autowired[$type] = false;
+            $this->lastFailure = $e->getMessage();
+            $this->container->log($this, $this->lastFailure);
+
+            return null;
+        } finally {
+            $this->throwOnAutowiringException = $originalThrowSetting;
+            $this->currentId = $currentId;
         }
 
-        $this->autowired[$typeHint->name] = $argumentId = sprintf('autowired.%s', $typeHint->name);
+        @trigger_error(sprintf('Relying on service auto-registration for type "%s" is deprecated since Symfony 3.4 and won\'t be supported in 4.0. Create a service named "%s" instead.', $type, $type), E_USER_DEPRECATED);
 
-        $argumentDefinition = $this->container->register($argumentId, $typeHint->name);
-        $argumentDefinition->setPublic(false);
+        $this->container->log($this, sprintf('Type "%s" has been auto-registered for service "%s".', $type, $this->currentId));
 
+        return new TypedReference($argumentId, $type);
+    }
+
+    private function createTypeNotFoundMessage(TypedReference $reference, $label)
+    {
+        $trackResources = $this->container->isTrackingResources();
+        $this->container->setResourceTracking(false);
         try {
-            $this->completeDefinition($argumentId, $argumentDefinition);
-        } catch (RuntimeException $e) {
-            $classOrInterface = $typeHint->isInterface() ? 'interface' : 'class';
-            $message = sprintf('Unable to autowire argument of type "%s" for the service "%s". No services were found matching this %s and it cannot be auto-registered.', $typeHint->name, $id, $classOrInterface);
-            throw new RuntimeException($message, 0, $e);
+            if ($r = $this->container->getReflectionClass($type = $reference->getType(), false)) {
+                $alternatives = $this->createTypeAlternatives($reference);
+            }
+        } finally {
+            $this->container->setResourceTracking($trackResources);
+        }
+
+        if (!$r) {
+            // either $type does not exist or a parent class does not exist
+            try {
+                $resource = new ClassExistenceResource($type, false);
+                // isFresh() will explode ONLY if a parent class/trait does not exist
+                $resource->isFresh(0);
+                $parentMsg = false;
+            } catch (\ReflectionException $e) {
+                $parentMsg = $e->getMessage();
+            }
+
+            $message = sprintf('has type "%s" but this class %s.', $type, $parentMsg ? sprintf('is missing a parent class (%s)', $parentMsg) : 'was not found');
+        } else {
+            $message = $this->container->has($type) ? 'this service is abstract' : 'no such service exists';
+            $message = sprintf('references %s "%s" but %s.%s', $r->isInterface() ? 'interface' : 'class', $type, $message, $alternatives);
+
+            if ($r->isInterface() && !$alternatives) {
+                $message .= ' Did you create a class that implements this interface?';
+            }
+        }
+
+        $message = sprintf('Cannot autowire service "%s": %s %s', $this->currentId, $label, $message);
+
+        if (null !== $this->lastFailure) {
+            $message = $this->lastFailure."\n".$message;
+            $this->lastFailure = null;
         }
 
-        return new Reference($argumentId);
+        return $message;
     }
 
-    /**
-     * Retrieves the reflection class associated with the given service.
-     *
-     * @param string     $id
-     * @param Definition $definition
-     *
-     * @return \ReflectionClass|false
-     */
-    private function getReflectionClass($id, Definition $definition)
+    private function createTypeAlternatives(TypedReference $reference)
     {
-        if (isset($this->reflectionClasses[$id])) {
-            return $this->reflectionClasses[$id];
+        // try suggesting available aliases first
+        if ($message = $this->getAliasesSuggestionForType($type = $reference->getType())) {
+            return ' '.$message;
+        }
+        if (null === $this->ambiguousServiceTypes) {
+            $this->populateAvailableTypes();
         }
 
-        // Cannot use reflection if the class isn't set
-        if (!$class = $definition->getClass()) {
-            return false;
+        if (isset($this->ambiguousServiceTypes[$type])) {
+            $message = sprintf('one of these existing services: "%s"', implode('", "', $this->ambiguousServiceTypes[$type]));
+        } elseif (isset($this->types[$type])) {
+            $message = sprintf('the existing "%s" service', $this->types[$type]);
+        } elseif ($reference->getRequiringClass() && !$reference->canBeAutoregistered() && !$this->strictMode) {
+            return ' It cannot be auto-registered because it is from a different root namespace.';
+        } else {
+            return '';
         }
 
-        $class = $this->container->getParameterBag()->resolveValue($class);
+        return sprintf(' You should maybe alias this %s to %s.', class_exists($type, false) ? 'class' : 'interface', $message);
+    }
 
-        if ($deprecated = $definition->isDeprecated()) {
-            $prevErrorHandler = set_error_handler(function ($level, $message, $file, $line) use (&$prevErrorHandler) {
-                return (E_USER_DEPRECATED === $level || !$prevErrorHandler) ? false : $prevErrorHandler($level, $message, $file, $line);
-            });
+    /**
+     * @deprecated since version 3.3, to be removed in 4.0.
+     */
+    private static function getResourceMetadataForMethod(\ReflectionMethod $method)
+    {
+        $methodArgumentsMetadata = [];
+        foreach ($method->getParameters() as $parameter) {
+            try {
+                $class = $parameter->getClass();
+            } catch (\ReflectionException $e) {
+                // type-hint is against a non-existent class
+                $class = false;
+            }
+
+            $isVariadic = method_exists($parameter, 'isVariadic') && $parameter->isVariadic();
+            $methodArgumentsMetadata[] = [
+                'class' => $class,
+                'isOptional' => $parameter->isOptional(),
+                'defaultValue' => ($parameter->isOptional() && !$isVariadic) ? $parameter->getDefaultValue() : null,
+            ];
         }
 
-        $e = null;
+        return $methodArgumentsMetadata;
+    }
 
-        try {
-            $reflector = new \ReflectionClass($class);
-        } catch (\Exception $e) {
-        } catch (\Throwable $e) {
+    private function getAliasesSuggestionForType($type, $extraContext = null)
+    {
+        $aliases = [];
+        foreach (class_parents($type) + class_implements($type) as $parent) {
+            if ($this->container->has($parent) && !$this->container->findDefinition($parent)->isAbstract()) {
+                $aliases[] = $parent;
+            }
         }
 
-        if ($deprecated) {
-            restore_error_handler();
+        $extraContext = $extraContext ? ' '.$extraContext : '';
+        if (1 < $len = \count($aliases)) {
+            $message = sprintf('Try changing the type-hint%s to one of its parents: ', $extraContext);
+            for ($i = 0, --$len; $i < $len; ++$i) {
+                $message .= sprintf('%s "%s", ', class_exists($aliases[$i], false) ? 'class' : 'interface', $aliases[$i]);
+            }
+            $message .= sprintf('or %s "%s".', class_exists($aliases[$i], false) ? 'class' : 'interface', $aliases[$i]);
+
+            return $message;
         }
 
-        if (null !== $e) {
-            if (!$e instanceof \ReflectionException) {
-                throw $e;
-            }
-            $reflector = false;
+        if ($aliases) {
+            return sprintf('Try changing the type-hint%s to "%s" instead.', $extraContext, $aliases[0]);
         }
 
-        return $this->reflectionClasses[$id] = $reflector;
+        return null;
     }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/AutowireRequiredMethodsPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/AutowireRequiredMethodsPass.php
new file mode 100644
index 0000000000000000000000000000000000000000..efb9df7b94318c0d9a9501eb6a8775571caa3c81
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/AutowireRequiredMethodsPass.php
@@ -0,0 +1,70 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\Definition;
+
+/**
+ * Looks for definitions with autowiring enabled and registers their corresponding "@required" methods as setters.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class AutowireRequiredMethodsPass extends AbstractRecursivePass
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function processValue($value, $isRoot = false)
+    {
+        $value = parent::processValue($value, $isRoot);
+
+        if (!$value instanceof Definition || !$value->isAutowired() || $value->isAbstract() || !$value->getClass()) {
+            return $value;
+        }
+        if (!$reflectionClass = $this->container->getReflectionClass($value->getClass(), false)) {
+            return $value;
+        }
+
+        $alreadyCalledMethods = [];
+
+        foreach ($value->getMethodCalls() as list($method)) {
+            $alreadyCalledMethods[strtolower($method)] = true;
+        }
+
+        foreach ($reflectionClass->getMethods() as $reflectionMethod) {
+            $r = $reflectionMethod;
+
+            if ($r->isConstructor() || isset($alreadyCalledMethods[strtolower($r->name)])) {
+                continue;
+            }
+
+            while (true) {
+                if (false !== $doc = $r->getDocComment()) {
+                    if (false !== stripos($doc, '@required') && preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc)) {
+                        $value->addMethodCall($reflectionMethod->name);
+                        break;
+                    }
+                    if (false === stripos($doc, '@inheritdoc') || !preg_match('#(?:^/\*\*|\n\s*+\*)\s*+(?:\{@inheritdoc\}|@inheritdoc)(?:\s|\*/$)#i', $doc)) {
+                        break;
+                    }
+                }
+                try {
+                    $r = $r->getPrototype();
+                } catch (\ReflectionException $e) {
+                    break; // method has no prototype
+                }
+            }
+        }
+
+        return $value;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/CheckArgumentsValidityPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/CheckArgumentsValidityPass.php
new file mode 100644
index 0000000000000000000000000000000000000000..30a6f524ade46e84e48c13c8831c10ec691c567f
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/CheckArgumentsValidityPass.php
@@ -0,0 +1,87 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+
+/**
+ * Checks if arguments of methods are properly configured.
+ *
+ * @author Kévin Dunglas <dunglas@gmail.com>
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class CheckArgumentsValidityPass extends AbstractRecursivePass
+{
+    private $throwExceptions;
+
+    public function __construct($throwExceptions = true)
+    {
+        $this->throwExceptions = $throwExceptions;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function processValue($value, $isRoot = false)
+    {
+        if (!$value instanceof Definition) {
+            return parent::processValue($value, $isRoot);
+        }
+
+        $i = 0;
+        foreach ($value->getArguments() as $k => $v) {
+            if ($k !== $i++) {
+                if (!\is_int($k)) {
+                    $msg = sprintf('Invalid constructor argument for service "%s": integer expected but found string "%s". Check your service definition.', $this->currentId, $k);
+                    $value->addError($msg);
+                    if ($this->throwExceptions) {
+                        throw new RuntimeException($msg);
+                    }
+
+                    break;
+                }
+
+                $msg = sprintf('Invalid constructor argument %d for service "%s": argument %d must be defined before. Check your service definition.', 1 + $k, $this->currentId, $i);
+                $value->addError($msg);
+                if ($this->throwExceptions) {
+                    throw new RuntimeException($msg);
+                }
+            }
+        }
+
+        foreach ($value->getMethodCalls() as $methodCall) {
+            $i = 0;
+            foreach ($methodCall[1] as $k => $v) {
+                if ($k !== $i++) {
+                    if (!\is_int($k)) {
+                        $msg = sprintf('Invalid argument for method call "%s" of service "%s": integer expected but found string "%s". Check your service definition.', $methodCall[0], $this->currentId, $k);
+                        $value->addError($msg);
+                        if ($this->throwExceptions) {
+                            throw new RuntimeException($msg);
+                        }
+
+                        break;
+                    }
+
+                    $msg = sprintf('Invalid argument %d for method call "%s" of service "%s": argument %d must be defined before. Check your service definition.', 1 + $k, $methodCall[0], $this->currentId, $i);
+                    $value->addError($msg);
+                    if ($this->throwExceptions) {
+                        throw new RuntimeException($msg);
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/CheckCircularReferencesPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/CheckCircularReferencesPass.php
index 6bde1942bae12d935b6325c91e058e37bde55bd7..55d911c4f3e60bc3b9b1f88b626bb7d7e0fcde47 100644
--- a/civicrm/vendor/symfony/dependency-injection/Compiler/CheckCircularReferencesPass.php
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/CheckCircularReferencesPass.php
@@ -36,9 +36,9 @@ class CheckCircularReferencesPass implements CompilerPassInterface
     {
         $graph = $container->getCompiler()->getServiceReferenceGraph();
 
-        $this->checkedNodes = array();
+        $this->checkedNodes = [];
         foreach ($graph->getNodes() as $id => $node) {
-            $this->currentPath = array($id);
+            $this->currentPath = [$id];
 
             $this->checkOutEdges($node->getOutEdges());
         }
@@ -58,8 +58,8 @@ class CheckCircularReferencesPass implements CompilerPassInterface
             $id = $node->getId();
 
             if (empty($this->checkedNodes[$id])) {
-                // don't check circular dependencies for lazy services
-                if (!$node->getValue() || !$node->getValue()->isLazy()) {
+                // Don't check circular references for lazy edges
+                if (!$node->getValue() || (!$edge->isLazy() && !$edge->isWeak())) {
                     $searchKey = array_search($id, $this->currentPath);
                     $this->currentPath[] = $id;
 
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/CheckDefinitionValidityPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/CheckDefinitionValidityPass.php
index ea1e089179a83762606e0a6133c199b23d2b7315..4b6d277fe997974ca2dfd699e32d2db5334eb57f 100644
--- a/civicrm/vendor/symfony/dependency-injection/Compiler/CheckDefinitionValidityPass.php
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/CheckDefinitionValidityPass.php
@@ -12,7 +12,7 @@
 namespace Symfony\Component\DependencyInjection\Compiler;
 
 use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\DependencyInjection\Exception\EnvParameterException;
 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
 
 /**
@@ -24,8 +24,6 @@ use Symfony\Component\DependencyInjection\Exception\RuntimeException;
  *
  * - non synthetic, non abstract services always have a class set
  * - synthetic services are always public
- * - synthetic services are always of non-prototype scope
- * - shared services are always of non-prototype scope
  *
  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  */
@@ -40,29 +38,22 @@ class CheckDefinitionValidityPass implements CompilerPassInterface
     {
         foreach ($container->getDefinitions() as $id => $definition) {
             // synthetic service is public
-            if ($definition->isSynthetic() && !$definition->isPublic()) {
+            if ($definition->isSynthetic() && !($definition->isPublic() || $definition->isPrivate())) {
                 throw new RuntimeException(sprintf('A synthetic service ("%s") must be public.', $id));
             }
 
-            // synthetic service has non-prototype scope
-            if ($definition->isSynthetic() && ContainerInterface::SCOPE_PROTOTYPE === $definition->getScope(false)) {
-                throw new RuntimeException(sprintf('A synthetic service ("%s") cannot be of scope "prototype".', $id));
-            }
-
-            // shared service has non-prototype scope
-            if ($definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE === $definition->getScope(false)) {
-                throw new RuntimeException(sprintf('A shared service ("%s") cannot be of scope "prototype".', $id));
-            }
-
-            if ($definition->getFactory() && ($definition->getFactoryClass(false) || $definition->getFactoryService(false) || $definition->getFactoryMethod(false))) {
-                throw new RuntimeException(sprintf('A service ("%s") can use either the old or the new factory syntax, not both.', $id));
-            }
-
             // non-synthetic, non-abstract service has class
             if (!$definition->isAbstract() && !$definition->isSynthetic() && !$definition->getClass()) {
-                if ($definition->getFactory() || $definition->getFactoryClass(false) || $definition->getFactoryService(false)) {
+                if ($definition->getFactory()) {
                     throw new RuntimeException(sprintf('Please add the class to service "%s" even if it is constructed by a factory since we might need to add method calls based on compile-time checks.', $id));
                 }
+                if (class_exists($id) || interface_exists($id, false)) {
+                    if (0 === strpos($id, '\\') && 1 < substr_count($id, '\\')) {
+                        throw new RuntimeException(sprintf('The definition for "%s" has no class attribute, and appears to reference a class or interface. Please specify the class attribute explicitly or remove the leading backslash by renaming the service to "%s" to get rid of this error.', $id, substr($id, 1)));
+                    }
+
+                    throw new RuntimeException(sprintf('The definition for "%s" has no class attribute, and appears to reference a class or interface in the global namespace. Leaving out the "class" attribute is only allowed for namespaced classes. Please specify the class attribute explicitly to get rid of this error.', $id));
+                }
 
                 throw new RuntimeException(sprintf('The definition for "%s" has no class. If you intend to inject this service dynamically at runtime, please mark it as synthetic=true. If this is an abstract definition solely used by child definitions, please add abstract=true, otherwise specify a class to get rid of this error.', $id));
             }
@@ -77,6 +68,22 @@ class CheckDefinitionValidityPass implements CompilerPassInterface
                     }
                 }
             }
+
+            if ($definition->isPublic() && !$definition->isPrivate()) {
+                $resolvedId = $container->resolveEnvPlaceholders($id, null, $usedEnvs);
+                if (null !== $usedEnvs) {
+                    throw new EnvParameterException([$resolvedId], null, 'A service name ("%s") cannot contain dynamic values.');
+                }
+            }
+        }
+
+        foreach ($container->getAliases() as $id => $alias) {
+            if ($alias->isPublic() && !$alias->isPrivate()) {
+                $resolvedId = $container->resolveEnvPlaceholders($id, null, $usedEnvs);
+                if (null !== $usedEnvs) {
+                    throw new EnvParameterException([$resolvedId], null, 'An alias name ("%s") cannot contain dynamic values.');
+                }
+            }
         }
     }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php
index f17096f8f3598afe6d8aec109446fcd935c84e5d..77b35f1866a9c8e1deac781a6737a6874cd18652 100644
--- a/civicrm/vendor/symfony/dependency-injection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php
@@ -11,9 +11,7 @@
 
 namespace Symfony\Component\DependencyInjection\Compiler;
 
-use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\DependencyInjection\Definition;
 use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
 use Symfony\Component\DependencyInjection\Reference;
 
@@ -22,42 +20,17 @@ use Symfony\Component\DependencyInjection\Reference;
  *
  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  */
-class CheckExceptionOnInvalidReferenceBehaviorPass implements CompilerPassInterface
+class CheckExceptionOnInvalidReferenceBehaviorPass extends AbstractRecursivePass
 {
-    private $container;
-    private $sourceId;
-
-    public function process(ContainerBuilder $container)
+    protected function processValue($value, $isRoot = false)
     {
-        $this->container = $container;
-
-        foreach ($container->getDefinitions() as $id => $definition) {
-            $this->sourceId = $id;
-            $this->processDefinition($definition);
+        if (!$value instanceof Reference) {
+            return parent::processValue($value, $isRoot);
         }
-    }
-
-    private function processDefinition(Definition $definition)
-    {
-        $this->processReferences($definition->getArguments());
-        $this->processReferences($definition->getMethodCalls());
-        $this->processReferences($definition->getProperties());
-    }
-
-    private function processReferences(array $arguments)
-    {
-        foreach ($arguments as $argument) {
-            if (\is_array($argument)) {
-                $this->processReferences($argument);
-            } elseif ($argument instanceof Definition) {
-                $this->processDefinition($argument);
-            } elseif ($argument instanceof Reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $argument->getInvalidBehavior()) {
-                $destId = (string) $argument;
-
-                if (!$this->container->has($destId)) {
-                    throw new ServiceNotFoundException($destId, $this->sourceId);
-                }
-            }
+        if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $value->getInvalidBehavior() && !$this->container->has($id = (string) $value)) {
+            throw new ServiceNotFoundException($id, $this->currentId);
         }
+
+        return $value;
     }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/CheckReferenceValidityPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/CheckReferenceValidityPass.php
index 2b380dd352a851a62dc010c0c36df9df209ddf99..8f2a3bdf706cf8adfcfe2170b9321113a44cc179 100644
--- a/civicrm/vendor/symfony/dependency-injection/Compiler/CheckReferenceValidityPass.php
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/CheckReferenceValidityPass.php
@@ -11,12 +11,8 @@
 
 namespace Symfony\Component\DependencyInjection\Compiler;
 
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\DependencyInjection\Definition;
 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
-use Symfony\Component\DependencyInjection\Exception\ScopeCrossingInjectionException;
-use Symfony\Component\DependencyInjection\Exception\ScopeWideningInjectionException;
 use Symfony\Component\DependencyInjection\Reference;
 
 /**
@@ -24,132 +20,24 @@ use Symfony\Component\DependencyInjection\Reference;
  *
  * The following checks are performed by this pass:
  * - target definitions are not abstract
- * - target definitions are of equal or wider scope
- * - target definitions are in the same scope hierarchy
  *
  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  */
-class CheckReferenceValidityPass implements CompilerPassInterface
+class CheckReferenceValidityPass extends AbstractRecursivePass
 {
-    private $container;
-    private $currentId;
-    private $currentScope;
-    private $currentScopeAncestors;
-    private $currentScopeChildren;
-
-    /**
-     * Processes the ContainerBuilder to validate References.
-     */
-    public function process(ContainerBuilder $container)
+    protected function processValue($value, $isRoot = false)
     {
-        $this->container = $container;
-
-        $children = $this->container->getScopeChildren(false);
-        $ancestors = array();
-
-        $scopes = $this->container->getScopes(false);
-        foreach ($scopes as $name => $parent) {
-            $ancestors[$name] = array($parent);
-
-            while (isset($scopes[$parent])) {
-                $ancestors[$name][] = $parent = $scopes[$parent];
-            }
-        }
-
-        foreach ($container->getDefinitions() as $id => $definition) {
-            if ($definition->isSynthetic() || $definition->isAbstract()) {
-                continue;
-            }
-
-            $this->currentId = $id;
-            $this->currentScope = $scope = $definition->getScope(false);
-
-            if (ContainerInterface::SCOPE_CONTAINER === $scope) {
-                $this->currentScopeChildren = array_keys($scopes);
-                $this->currentScopeAncestors = array();
-            } elseif (ContainerInterface::SCOPE_PROTOTYPE !== $scope) {
-                $this->currentScopeChildren = isset($children[$scope]) ? $children[$scope] : array();
-                $this->currentScopeAncestors = isset($ancestors[$scope]) ? $ancestors[$scope] : array();
-            }
-
-            $this->validateReferences($definition->getArguments());
-            $this->validateReferences($definition->getMethodCalls());
-            $this->validateReferences($definition->getProperties());
+        if ($isRoot && $value instanceof Definition && ($value->isSynthetic() || $value->isAbstract())) {
+            return $value;
         }
-    }
+        if ($value instanceof Reference && $this->container->hasDefinition((string) $value)) {
+            $targetDefinition = $this->container->getDefinition((string) $value);
 
-    /**
-     * Validates an array of References.
-     *
-     * @param array $arguments An array of Reference objects
-     *
-     * @throws RuntimeException when there is a reference to an abstract definition
-     */
-    private function validateReferences(array $arguments)
-    {
-        foreach ($arguments as $argument) {
-            if (\is_array($argument)) {
-                $this->validateReferences($argument);
-            } elseif ($argument instanceof Reference) {
-                $targetDefinition = $this->getDefinition((string) $argument);
-
-                if (null !== $targetDefinition && $targetDefinition->isAbstract()) {
-                    throw new RuntimeException(sprintf('The definition "%s" has a reference to an abstract definition "%s". Abstract definitions cannot be the target of references.', $this->currentId, $argument));
-                }
-
-                $this->validateScope($argument, $targetDefinition);
+            if ($targetDefinition->isAbstract()) {
+                throw new RuntimeException(sprintf('The definition "%s" has a reference to an abstract definition "%s". Abstract definitions cannot be the target of references.', $this->currentId, $value));
             }
         }
-    }
-
-    /**
-     * Validates the scope of a single Reference.
-     *
-     * @throws ScopeWideningInjectionException when the definition references a service of a narrower scope
-     * @throws ScopeCrossingInjectionException when the definition references a service of another scope hierarchy
-     */
-    private function validateScope(Reference $reference, Definition $definition = null)
-    {
-        if (ContainerInterface::SCOPE_PROTOTYPE === $this->currentScope) {
-            return;
-        }
-
-        if (!$reference->isStrict(false)) {
-            return;
-        }
-
-        if (null === $definition) {
-            return;
-        }
-
-        if ($this->currentScope === $scope = $definition->getScope(false)) {
-            return;
-        }
-
-        $id = (string) $reference;
-
-        if (\in_array($scope, $this->currentScopeChildren, true)) {
-            throw new ScopeWideningInjectionException($this->currentId, $this->currentScope, $id, $scope);
-        }
-
-        if (!\in_array($scope, $this->currentScopeAncestors, true)) {
-            throw new ScopeCrossingInjectionException($this->currentId, $this->currentScope, $id, $scope);
-        }
-    }
-
-    /**
-     * Returns the Definition given an id.
-     *
-     * @param string $id Definition identifier
-     *
-     * @return Definition
-     */
-    private function getDefinition($id)
-    {
-        if (!$this->container->hasDefinition($id)) {
-            return;
-        }
 
-        return $this->container->getDefinition($id);
+        return parent::processValue($value, $isRoot);
     }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/Compiler.php b/civicrm/vendor/symfony/dependency-injection/Compiler/Compiler.php
index dd9539eeb7ceaa7c2a1da208caf9870a7c7a5cd4..c5f698b012b899f0fcec118686569db41e4d3951 100644
--- a/civicrm/vendor/symfony/dependency-injection/Compiler/Compiler.php
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/Compiler.php
@@ -12,6 +12,7 @@
 namespace Symfony\Component\DependencyInjection\Compiler;
 
 use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Exception\EnvParameterException;
 
 /**
  * This class is used to remove circular dependencies between individual passes.
@@ -21,7 +22,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
 class Compiler
 {
     private $passConfig;
-    private $log = array();
+    private $log = [];
     private $loggingFormatter;
     private $serviceReferenceGraph;
 
@@ -29,7 +30,6 @@ class Compiler
     {
         $this->passConfig = new PassConfig();
         $this->serviceReferenceGraph = new ServiceReferenceGraph();
-        $this->loggingFormatter = new LoggingFormatter();
     }
 
     /**
@@ -56,9 +56,17 @@ class Compiler
      * Returns the logging formatter which can be used by compilation passes.
      *
      * @return LoggingFormatter
+     *
+     * @deprecated since version 3.3, to be removed in 4.0. Use the ContainerBuilder::log() method instead.
      */
     public function getLoggingFormatter()
     {
+        if (null === $this->loggingFormatter) {
+            @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the ContainerBuilder::log() method instead.', __METHOD__), E_USER_DEPRECATED);
+
+            $this->loggingFormatter = new LoggingFormatter();
+        }
+
         return $this->loggingFormatter;
     }
 
@@ -68,21 +76,50 @@ class Compiler
      * @param CompilerPassInterface $pass A compiler pass
      * @param string                $type The type of the pass
      */
-    public function addPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION)
+    public function addPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/*, int $priority = 0*/)
     {
-        $this->passConfig->addPass($pass, $type);
+        if (\func_num_args() >= 3) {
+            $priority = func_get_arg(2);
+        } else {
+            if (__CLASS__ !== static::class) {
+                $r = new \ReflectionMethod($this, __FUNCTION__);
+                if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
+                    @trigger_error(sprintf('Method %s() will have a third `int $priority = 0` argument in version 4.0. Not defining it is deprecated since Symfony 3.2.', __METHOD__), E_USER_DEPRECATED);
+                }
+            }
+
+            $priority = 0;
+        }
+
+        $this->passConfig->addPass($pass, $type, $priority);
     }
 
     /**
      * Adds a log message.
      *
      * @param string $string The log message
+     *
+     * @deprecated since version 3.3, to be removed in 4.0. Use the ContainerBuilder::log() method instead.
      */
     public function addLogMessage($string)
     {
+        @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the ContainerBuilder::log() method instead.', __METHOD__), E_USER_DEPRECATED);
+
         $this->log[] = $string;
     }
 
+    /**
+     * @final
+     */
+    public function log(CompilerPassInterface $pass, $message)
+    {
+        if (false !== strpos($message, "\n")) {
+            $message = str_replace("\n", "\n".\get_class($pass).': ', trim($message));
+        }
+
+        $this->log[] = \get_class($pass).': '.$message;
+    }
+
     /**
      * Returns the log.
      *
@@ -98,8 +135,31 @@ class Compiler
      */
     public function compile(ContainerBuilder $container)
     {
-        foreach ($this->passConfig->getPasses() as $pass) {
-            $pass->process($container);
+        try {
+            foreach ($this->passConfig->getPasses() as $pass) {
+                $pass->process($container);
+            }
+        } catch (\Exception $e) {
+            $usedEnvs = [];
+            $prev = $e;
+
+            do {
+                $msg = $prev->getMessage();
+
+                if ($msg !== $resolvedMsg = $container->resolveEnvPlaceholders($msg, null, $usedEnvs)) {
+                    $r = new \ReflectionProperty($prev, 'message');
+                    $r->setAccessible(true);
+                    $r->setValue($prev, $resolvedMsg);
+                }
+            } while ($prev = $prev->getPrevious());
+
+            if ($usedEnvs) {
+                $e = new EnvParameterException($usedEnvs, $e);
+            }
+
+            throw $e;
+        } finally {
+            $this->getServiceReferenceGraph()->clear();
         }
     }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/DecoratorServicePass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/DecoratorServicePass.php
index 80eb67a6323473b3184a49f47fa5d919e48c8bfc..bbd857e154e51163f83ce14c4bdf230aec9140bc 100644
--- a/civicrm/vendor/symfony/dependency-injection/Compiler/DecoratorServicePass.php
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/DecoratorServicePass.php
@@ -32,11 +32,11 @@ class DecoratorServicePass implements CompilerPassInterface
             if (!$decorated = $definition->getDecoratedService()) {
                 continue;
             }
-            $definitions->insert(array($id, $definition), array($decorated[2], --$order));
+            $definitions->insert([$id, $definition], [$decorated[2], --$order]);
         }
+        $decoratingDefinitions = [];
 
-        foreach ($definitions as $arr) {
-            list($id, $definition) = $arr;
+        foreach ($definitions as list($id, $definition)) {
             list($inner, $renamedId) = $definition->getDecoratedService();
 
             $definition->setDecoratedService(null);
@@ -50,19 +50,32 @@ class DecoratorServicePass implements CompilerPassInterface
             if ($container->hasAlias($inner)) {
                 $alias = $container->getAlias($inner);
                 $public = $alias->isPublic();
-                $container->setAlias($renamedId, new Alias((string) $alias, false));
+                $private = $alias->isPrivate();
+                $container->setAlias($renamedId, new Alias($container->normalizeId($alias), false));
             } else {
                 $decoratedDefinition = $container->getDefinition($inner);
-                $definition->setTags(array_merge($decoratedDefinition->getTags(), $definition->getTags()));
-                $definition->setAutowiringTypes(array_merge($decoratedDefinition->getAutowiringTypes(), $definition->getAutowiringTypes()));
                 $public = $decoratedDefinition->isPublic();
+                $private = $decoratedDefinition->isPrivate();
                 $decoratedDefinition->setPublic(false);
-                $decoratedDefinition->setTags(array());
-                $decoratedDefinition->setAutowiringTypes(array());
                 $container->setDefinition($renamedId, $decoratedDefinition);
+                $decoratingDefinitions[$inner] = $decoratedDefinition;
             }
 
-            $container->setAlias($inner, new Alias($id, $public));
+            if (isset($decoratingDefinitions[$inner])) {
+                $decoratingDefinition = $decoratingDefinitions[$inner];
+                $definition->setTags(array_merge($decoratingDefinition->getTags(), $definition->getTags()));
+                $autowiringTypes = $decoratingDefinition->getAutowiringTypes(false);
+                if ($types = array_merge($autowiringTypes, $definition->getAutowiringTypes(false))) {
+                    $definition->setAutowiringTypes($types);
+                }
+                $decoratingDefinition->setTags([]);
+                if ($autowiringTypes) {
+                    $decoratingDefinition->setAutowiringTypes([]);
+                }
+                $decoratingDefinitions[$inner] = $definition;
+            }
+
+            $container->setAlias($inner, $id)->setPublic($public)->setPrivate($private);
         }
     }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/DefinitionErrorExceptionPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/DefinitionErrorExceptionPass.php
new file mode 100644
index 0000000000000000000000000000000000000000..73b5d1d57d5825fa2168ac7bf67f14b88d55f2a9
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/DefinitionErrorExceptionPass.php
@@ -0,0 +1,39 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+
+/**
+ * Throws an exception for any Definitions that have errors and still exist.
+ *
+ * @author Ryan Weaver <ryan@knpuniversity.com>
+ */
+class DefinitionErrorExceptionPass extends AbstractRecursivePass
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function processValue($value, $isRoot = false)
+    {
+        if (!$value instanceof Definition || empty($value->getErrors())) {
+            return parent::processValue($value, $isRoot);
+        }
+
+        // only show the first error so the user can focus on it
+        $errors = $value->getErrors();
+        $message = reset($errors);
+
+        throw new RuntimeException($message);
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/FactoryReturnTypePass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/FactoryReturnTypePass.php
new file mode 100644
index 0000000000000000000000000000000000000000..d688fb59fd1fd2592d28151c6e83d6c5b4e0c429
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/FactoryReturnTypePass.php
@@ -0,0 +1,112 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * @author Guilhem N. <egetick@gmail.com>
+ *
+ * @deprecated since version 3.3, to be removed in 4.0.
+ */
+class FactoryReturnTypePass implements CompilerPassInterface
+{
+    private $resolveClassPass;
+
+    public function __construct(ResolveClassPass $resolveClassPass = null)
+    {
+        if (null === $resolveClassPass) {
+            @trigger_error('The '.__CLASS__.' class is deprecated since Symfony 3.3 and will be removed in 4.0.', E_USER_DEPRECATED);
+        }
+        $this->resolveClassPass = $resolveClassPass;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function process(ContainerBuilder $container)
+    {
+        // works only since php 7.0 and hhvm 3.11
+        if (!method_exists(\ReflectionMethod::class, 'getReturnType')) {
+            return;
+        }
+        $resolveClassPassChanges = null !== $this->resolveClassPass ? $this->resolveClassPass->getChanges() : [];
+
+        foreach ($container->getDefinitions() as $id => $definition) {
+            $this->updateDefinition($container, $id, $definition, $resolveClassPassChanges);
+        }
+    }
+
+    private function updateDefinition(ContainerBuilder $container, $id, Definition $definition, array $resolveClassPassChanges, array $previous = [])
+    {
+        // circular reference
+        if (isset($previous[$id])) {
+            return;
+        }
+
+        $factory = $definition->getFactory();
+        if (null === $factory || (!isset($resolveClassPassChanges[$id]) && null !== $definition->getClass())) {
+            return;
+        }
+
+        $class = null;
+        if (\is_string($factory)) {
+            try {
+                $m = new \ReflectionFunction($factory);
+                if (false !== $m->getFileName() && file_exists($m->getFileName())) {
+                    $container->fileExists($m->getFileName());
+                }
+            } catch (\ReflectionException $e) {
+                return;
+            }
+        } else {
+            if ($factory[0] instanceof Reference) {
+                $previous[$id] = true;
+                $factoryId = $container->normalizeId($factory[0]);
+                $factoryDefinition = $container->findDefinition($factoryId);
+                $this->updateDefinition($container, $factoryId, $factoryDefinition, $resolveClassPassChanges, $previous);
+                $class = $factoryDefinition->getClass();
+            } else {
+                $class = $factory[0];
+            }
+
+            if (!$m = $container->getReflectionClass($class, false)) {
+                return;
+            }
+            try {
+                $m = $m->getMethod($factory[1]);
+            } catch (\ReflectionException $e) {
+                return;
+            }
+        }
+
+        $returnType = $m->getReturnType();
+        if (null !== $returnType && !$returnType->isBuiltin()) {
+            $returnType = $returnType instanceof \ReflectionNamedType ? $returnType->getName() : $returnType->__toString();
+            if (null !== $class) {
+                $declaringClass = $m->getDeclaringClass()->getName();
+                if ('self' === strtolower($returnType)) {
+                    $returnType = $declaringClass;
+                } elseif ('parent' === strtolower($returnType)) {
+                    $returnType = get_parent_class($declaringClass) ?: null;
+                }
+            }
+
+            if (null !== $returnType && (!isset($resolveClassPassChanges[$id]) || $returnType !== $resolveClassPassChanges[$id])) {
+                @trigger_error(sprintf('Relying on its factory\'s return-type to define the class of service "%s" is deprecated since Symfony 3.3 and won\'t work in 4.0. Set the "class" attribute to "%s" on the service definition instead.', $id, $returnType), E_USER_DEPRECATED);
+            }
+            $definition->setClass($returnType);
+        }
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/InlineServiceDefinitionsPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/InlineServiceDefinitionsPass.php
index b57f48840e7895e9cd7ad75b92cda5199d666644..326ee19323ca43a1ba67d6f3589db0a5973b4abf 100644
--- a/civicrm/vendor/symfony/dependency-injection/Compiler/InlineServiceDefinitionsPass.php
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/InlineServiceDefinitionsPass.php
@@ -11,9 +11,9 @@
 
 namespace Symfony\Component\DependencyInjection\Compiler;
 
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
 use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
 use Symfony\Component\DependencyInjection\Reference;
 
 /**
@@ -21,12 +21,10 @@ use Symfony\Component\DependencyInjection\Reference;
  *
  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  */
-class InlineServiceDefinitionsPass implements RepeatablePassInterface
+class InlineServiceDefinitionsPass extends AbstractRecursivePass implements RepeatablePassInterface
 {
-    private $graph;
-    private $compiler;
-    private $formatter;
-    private $currentId;
+    private $cloningIds = [];
+    private $inlinedServiceIds = [];
 
     /**
      * {@inheritdoc}
@@ -37,88 +35,90 @@ class InlineServiceDefinitionsPass implements RepeatablePassInterface
     }
 
     /**
-     * Processes the ContainerBuilder for inline service definitions.
+     * Returns an array of all services inlined by this pass.
+     *
+     * The key is the inlined service id and its value is the list of services it was inlined into.
+     *
+     * @deprecated since version 3.4, to be removed in 4.0.
+     *
+     * @return array
      */
-    public function process(ContainerBuilder $container)
+    public function getInlinedServiceIds()
     {
-        $this->compiler = $container->getCompiler();
-        $this->formatter = $this->compiler->getLoggingFormatter();
-        $this->graph = $this->compiler->getServiceReferenceGraph();
+        @trigger_error('Calling InlineServiceDefinitionsPass::getInlinedServiceIds() is deprecated since Symfony 3.4 and will be removed in 4.0.', E_USER_DEPRECATED);
 
-        $container->setDefinitions($this->inlineArguments($container, $container->getDefinitions(), true));
+        return $this->inlinedServiceIds;
     }
 
     /**
-     * Processes inline arguments.
-     *
-     * @param ContainerBuilder $container The ContainerBuilder
-     * @param array            $arguments An array of arguments
-     * @param bool             $isRoot    If we are processing the root definitions or not
-     *
-     * @return array
+     * {@inheritdoc}
      */
-    private function inlineArguments(ContainerBuilder $container, array $arguments, $isRoot = false)
+    protected function processValue($value, $isRoot = false)
     {
-        foreach ($arguments as $k => $argument) {
-            if ($isRoot) {
-                $this->currentId = $k;
-            }
-            if (\is_array($argument)) {
-                $arguments[$k] = $this->inlineArguments($container, $argument);
-            } elseif ($argument instanceof Reference) {
-                if (!$container->hasDefinition($id = (string) $argument)) {
-                    continue;
-                }
-
-                if ($this->isInlineableDefinition($container, $id, $definition = $container->getDefinition($id))) {
-                    $this->compiler->addLogMessage($this->formatter->formatInlineService($this, $id, $this->currentId));
-
-                    if ($definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE !== $definition->getScope(false)) {
-                        $arguments[$k] = $definition;
-                    } else {
-                        $arguments[$k] = clone $definition;
-                    }
-                }
-            } elseif ($argument instanceof Definition) {
-                $argument->setArguments($this->inlineArguments($container, $argument->getArguments()));
-                $argument->setMethodCalls($this->inlineArguments($container, $argument->getMethodCalls()));
-                $argument->setProperties($this->inlineArguments($container, $argument->getProperties()));
-
-                $configurator = $this->inlineArguments($container, array($argument->getConfigurator()));
-                $argument->setConfigurator($configurator[0]);
-
-                $factory = $this->inlineArguments($container, array($argument->getFactory()));
-                $argument->setFactory($factory[0]);
+        if ($value instanceof ArgumentInterface) {
+            // Reference found in ArgumentInterface::getValues() are not inlineable
+            return $value;
+        }
+
+        if ($value instanceof Definition && $this->cloningIds) {
+            if ($value->isShared()) {
+                return $value;
             }
+            $value = clone $value;
+        }
+
+        if (!$value instanceof Reference || !$this->container->hasDefinition($id = $this->container->normalizeId($value))) {
+            return parent::processValue($value, $isRoot);
+        }
+
+        $definition = $this->container->getDefinition($id);
+
+        if (!$this->isInlineableDefinition($id, $definition, $this->container->getCompiler()->getServiceReferenceGraph())) {
+            return $value;
         }
 
-        return $arguments;
+        $this->container->log($this, sprintf('Inlined service "%s" to "%s".', $id, $this->currentId));
+        $this->inlinedServiceIds[$id][] = $this->currentId;
+
+        if ($definition->isShared()) {
+            return $definition;
+        }
+
+        if (isset($this->cloningIds[$id])) {
+            $ids = array_keys($this->cloningIds);
+            $ids[] = $id;
+
+            throw new ServiceCircularReferenceException($id, \array_slice($ids, array_search($id, $ids)));
+        }
+
+        $this->cloningIds[$id] = true;
+        try {
+            return $this->processValue($definition);
+        } finally {
+            unset($this->cloningIds[$id]);
+        }
     }
 
     /**
      * Checks if the definition is inlineable.
      *
-     * @param ContainerBuilder $container
-     * @param string           $id
-     * @param Definition       $definition
-     *
      * @return bool If the definition is inlineable
      */
-    private function isInlineableDefinition(ContainerBuilder $container, $id, Definition $definition)
+    private function isInlineableDefinition($id, Definition $definition, ServiceReferenceGraph $graph)
     {
-        if ($definition->isDeprecated() || $definition->isLazy() || $definition->isSynthetic()) {
+        if ($definition->getErrors() || $definition->isDeprecated() || $definition->isLazy() || $definition->isSynthetic()) {
             return false;
         }
 
-        if (!$definition->isShared() || ContainerInterface::SCOPE_PROTOTYPE === $definition->getScope(false)) {
+        if (!$definition->isShared()) {
             return true;
         }
 
-        if ($definition->isPublic()) {
+        if ($definition->isPublic() || $definition->isPrivate()) {
             return false;
         }
 
-        if (!$this->graph->hasNode($id)) {
+        if (!$graph->hasNode($id)) {
             return true;
         }
 
@@ -126,23 +126,28 @@ class InlineServiceDefinitionsPass implements RepeatablePassInterface
             return false;
         }
 
-        $ids = array();
-        foreach ($this->graph->getNode($id)->getInEdges() as $edge) {
+        $ids = [];
+        $isReferencedByConstructor = false;
+        foreach ($graph->getNode($id)->getInEdges() as $edge) {
+            $isReferencedByConstructor = $isReferencedByConstructor || $edge->isReferencedByConstructor();
+            if ($edge->isWeak() || $edge->isLazy()) {
+                return false;
+            }
             $ids[] = $edge->getSourceNode()->getId();
         }
 
-        if (\count(array_unique($ids)) > 1) {
-            return false;
+        if (!$ids) {
+            return true;
         }
 
-        if (\count($ids) > 1 && \is_array($factory = $definition->getFactory()) && ($factory[0] instanceof Reference || $factory[0] instanceof Definition)) {
+        if (\count(array_unique($ids)) > 1) {
             return false;
         }
 
-        if (\count($ids) > 1 && $definition->getFactoryService(false)) {
+        if (\count($ids) > 1 && \is_array($factory = $definition->getFactory()) && ($factory[0] instanceof Reference || $factory[0] instanceof Definition)) {
             return false;
         }
 
-        return $container->getDefinition(reset($ids))->getScope(false) === $definition->getScope(false);
+        return $this->container->getDefinition($ids[0])->isShared();
     }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/LoggingFormatter.php b/civicrm/vendor/symfony/dependency-injection/Compiler/LoggingFormatter.php
index 64ffc9e478337587d1afcf1fa2b738583a0059bb..b058d26238c3fe267e79794b7ffee46a65741293 100644
--- a/civicrm/vendor/symfony/dependency-injection/Compiler/LoggingFormatter.php
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/LoggingFormatter.php
@@ -11,10 +11,14 @@
 
 namespace Symfony\Component\DependencyInjection\Compiler;
 
+@trigger_error('The '.__NAMESPACE__.'\LoggingFormatter class is deprecated since Symfony 3.3 and will be removed in 4.0. Use the ContainerBuilder::log() method instead.', E_USER_DEPRECATED);
+
 /**
  * Used to format logging messages during the compilation.
  *
  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ *
+ * @deprecated since version 3.3, to be removed in 4.0. Use the ContainerBuilder::log() method instead.
  */
 class LoggingFormatter
 {
@@ -38,6 +42,11 @@ class LoggingFormatter
         return $this->format($pass, sprintf('Resolving inheritance for "%s" (parent: %s).', $childId, $parentId));
     }
 
+    public function formatUnusedAutowiringPatterns(CompilerPassInterface $pass, $id, array $patterns)
+    {
+        return $this->format($pass, sprintf('Autowiring\'s patterns "%s" for service "%s" don\'t match any method.', implode('", "', $patterns), $id));
+    }
+
     public function format(CompilerPassInterface $pass, $message)
     {
         return sprintf('%s: %s', \get_class($pass), $message);
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php
index 9434ac70b543bef7b360ef8a71b610e8dd0338ac..caa1fd225167294715a96b0d27c50185be738094 100644
--- a/civicrm/vendor/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php
@@ -12,8 +12,14 @@
 namespace Symfony\Component\DependencyInjection\Compiler;
 
 use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Exception\LogicException;
+use Symfony\Component\DependencyInjection\Exception\RuntimeException;
 use Symfony\Component\DependencyInjection\Extension\ConfigurationExtensionInterface;
+use Symfony\Component\DependencyInjection\Extension\Extension;
+use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
 use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
+use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
+use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
 
 /**
  * Merges extension configs into the container builder.
@@ -43,20 +49,38 @@ class MergeExtensionConfigurationPass implements CompilerPassInterface
                 // this extension was not called
                 continue;
             }
-            $config = $container->getParameterBag()->resolveValue($config);
-
-            $tmpContainer = new ContainerBuilder($container->getParameterBag());
-            $tmpContainer->setResourceTracking($container->isTrackingResources());
-            $tmpContainer->addObjectResource($extension);
-            if ($extension instanceof ConfigurationExtensionInterface && null !== $configuration = $extension->getConfiguration($config, $tmpContainer)) {
-                $tmpContainer->addObjectResource($configuration);
+            $resolvingBag = $container->getParameterBag();
+            if ($resolvingBag instanceof EnvPlaceholderParameterBag && $extension instanceof Extension) {
+                // create a dedicated bag so that we can track env vars per-extension
+                $resolvingBag = new MergeExtensionConfigurationParameterBag($resolvingBag);
             }
+            $config = $resolvingBag->resolveValue($config);
+
+            try {
+                $tmpContainer = new MergeExtensionConfigurationContainerBuilder($extension, $resolvingBag);
+                $tmpContainer->setResourceTracking($container->isTrackingResources());
+                $tmpContainer->addObjectResource($extension);
+                if ($extension instanceof ConfigurationExtensionInterface && null !== $configuration = $extension->getConfiguration($config, $tmpContainer)) {
+                    $tmpContainer->addObjectResource($configuration);
+                }
+
+                foreach ($exprLangProviders as $provider) {
+                    $tmpContainer->addExpressionLanguageProvider($provider);
+                }
 
-            foreach ($exprLangProviders as $provider) {
-                $tmpContainer->addExpressionLanguageProvider($provider);
+                $extension->load($config, $tmpContainer);
+            } catch (\Exception $e) {
+                if ($resolvingBag instanceof MergeExtensionConfigurationParameterBag) {
+                    $container->getParameterBag()->mergeEnvPlaceholders($resolvingBag);
+                }
+
+                throw $e;
             }
 
-            $extension->load($config, $tmpContainer);
+            if ($resolvingBag instanceof MergeExtensionConfigurationParameterBag) {
+                // don't keep track of env vars that are *overridden* when configs are merged
+                $resolvingBag->freezeAfterProcessing($extension, $tmpContainer);
+            }
 
             $container->merge($tmpContainer);
             $container->getParameterBag()->add($parameters);
@@ -66,3 +90,117 @@ class MergeExtensionConfigurationPass implements CompilerPassInterface
         $container->addAliases($aliases);
     }
 }
+
+/**
+ * @internal
+ */
+class MergeExtensionConfigurationParameterBag extends EnvPlaceholderParameterBag
+{
+    private $processedEnvPlaceholders;
+
+    public function __construct(parent $parameterBag)
+    {
+        parent::__construct($parameterBag->all());
+        $this->mergeEnvPlaceholders($parameterBag);
+    }
+
+    public function freezeAfterProcessing(Extension $extension, ContainerBuilder $container)
+    {
+        if (!$config = $extension->getProcessedConfigs()) {
+            // Extension::processConfiguration() wasn't called, we cannot know how configs were merged
+            return;
+        }
+        $this->processedEnvPlaceholders = [];
+
+        // serialize config and container to catch env vars nested in object graphs
+        $config = serialize($config).serialize($container->getDefinitions()).serialize($container->getAliases()).serialize($container->getParameterBag()->all());
+
+        foreach (parent::getEnvPlaceholders() as $env => $placeholders) {
+            foreach ($placeholders as $placeholder) {
+                if (false !== stripos($config, $placeholder)) {
+                    $this->processedEnvPlaceholders[$env] = $placeholders;
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getEnvPlaceholders()
+    {
+        return null !== $this->processedEnvPlaceholders ? $this->processedEnvPlaceholders : parent::getEnvPlaceholders();
+    }
+}
+
+/**
+ * A container builder preventing using methods that wouldn't have any effect from extensions.
+ *
+ * @internal
+ */
+class MergeExtensionConfigurationContainerBuilder extends ContainerBuilder
+{
+    private $extensionClass;
+
+    public function __construct(ExtensionInterface $extension, ParameterBagInterface $parameterBag = null)
+    {
+        parent::__construct($parameterBag);
+
+        $this->extensionClass = \get_class($extension);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/*, int $priority = 0*/)
+    {
+        throw new LogicException(sprintf('You cannot add compiler pass "%s" from extension "%s". Compiler passes must be registered before the container is compiled.', \get_class($pass), $this->extensionClass));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function registerExtension(ExtensionInterface $extension)
+    {
+        throw new LogicException(sprintf('You cannot register extension "%s" from "%s". Extensions must be registered before the container is compiled.', \get_class($extension), $this->extensionClass));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function compile($resolveEnvPlaceholders = false)
+    {
+        throw new LogicException(sprintf('Cannot compile the container in extension "%s".', $this->extensionClass));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function resolveEnvPlaceholders($value, $format = null, array &$usedEnvs = null)
+    {
+        if (true !== $format || !\is_string($value)) {
+            return parent::resolveEnvPlaceholders($value, $format, $usedEnvs);
+        }
+
+        $bag = $this->getParameterBag();
+        $value = $bag->resolveValue($value);
+
+        if (!$bag instanceof EnvPlaceholderParameterBag) {
+            return parent::resolveEnvPlaceholders($value, $format, $usedEnvs);
+        }
+
+        foreach ($bag->getEnvPlaceholders() as $env => $placeholders) {
+            if (false === strpos($env, ':')) {
+                continue;
+            }
+            foreach ($placeholders as $placeholder) {
+                if (false !== stripos($value, $placeholder)) {
+                    throw new RuntimeException(sprintf('Using a cast in "env(%s)" is incompatible with resolution at compile time in "%s". The logic in the extension should be moved to a compiler pass, or an env parameter with no cast should be used instead.', $env, $this->extensionClass));
+                }
+            }
+        }
+
+        return parent::resolveEnvPlaceholders($value, $format, $usedEnvs);
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/PassConfig.php b/civicrm/vendor/symfony/dependency-injection/Compiler/PassConfig.php
index 8eabf3529df7ec6f7c820944d2af1ee7f257f5fb..e470cdec2dc9836e77376315741b79bfb5bd5fd2 100644
--- a/civicrm/vendor/symfony/dependency-injection/Compiler/PassConfig.php
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/PassConfig.php
@@ -29,9 +29,9 @@ class PassConfig
     const TYPE_REMOVE = 'removing';
 
     private $mergePass;
-    private $afterRemovingPasses = array();
-    private $beforeOptimizationPasses = array();
-    private $beforeRemovingPasses = array();
+    private $afterRemovingPasses = [];
+    private $beforeOptimizationPasses = [];
+    private $beforeRemovingPasses = [];
     private $optimizationPasses;
     private $removingPasses;
 
@@ -39,48 +39,74 @@ class PassConfig
     {
         $this->mergePass = new MergeExtensionConfigurationPass();
 
-        $this->optimizationPasses = array(
-            new ExtensionCompilerPass(),
-            new ResolveDefinitionTemplatesPass(),
+        $this->beforeOptimizationPasses = [
+            100 => [
+                $resolveClassPass = new ResolveClassPass(),
+                new ResolveInstanceofConditionalsPass(),
+                new RegisterEnvVarProcessorsPass(),
+            ],
+            -1000 => [new ExtensionCompilerPass()],
+        ];
+
+        $this->optimizationPasses = [[
+            new ResolveChildDefinitionsPass(),
+            new ServiceLocatorTagPass(),
+            new RegisterServiceSubscribersPass(),
             new DecoratorServicePass(),
-            new ResolveParameterPlaceHoldersPass(),
+            new ResolveParameterPlaceHoldersPass(false, false),
+            new ResolveFactoryClassPass(),
+            new FactoryReturnTypePass($resolveClassPass),
             new CheckDefinitionValidityPass(),
+            new ResolveNamedArgumentsPass(),
+            new AutowireRequiredMethodsPass(),
+            new ResolveBindingsPass(),
+            new AutowirePass(false),
+            new ResolveTaggedIteratorArgumentPass(),
+            new ResolveServiceSubscribersPass(),
             new ResolveReferencesToAliasesPass(),
             new ResolveInvalidReferencesPass(),
-            new AutowirePass(),
             new AnalyzeServiceReferencesPass(true),
             new CheckCircularReferencesPass(),
             new CheckReferenceValidityPass(),
-        );
+            new CheckArgumentsValidityPass(false),
+        ]];
+
+        $this->beforeRemovingPasses = [
+            -100 => [
+                new ResolvePrivatesPass(),
+            ],
+        ];
 
-        $this->removingPasses = array(
+        $this->removingPasses = [[
             new RemovePrivateAliasesPass(),
             new ReplaceAliasByActualDefinitionPass(),
             new RemoveAbstractDefinitionsPass(),
-            new RepeatedPass(array(
+            new RepeatedPass([
                 new AnalyzeServiceReferencesPass(),
                 new InlineServiceDefinitionsPass(),
                 new AnalyzeServiceReferencesPass(),
                 new RemoveUnusedDefinitionsPass(),
-            )),
+            ]),
+            new DefinitionErrorExceptionPass(),
             new CheckExceptionOnInvalidReferenceBehaviorPass(),
-        );
+            new ResolveHotPathPass(),
+        ]];
     }
 
     /**
      * Returns all passes in order to be processed.
      *
-     * @return array An array of all passes to process
+     * @return CompilerPassInterface[]
      */
     public function getPasses()
     {
         return array_merge(
-            array($this->mergePass),
-            $this->beforeOptimizationPasses,
-            $this->optimizationPasses,
-            $this->beforeRemovingPasses,
-            $this->removingPasses,
-            $this->afterRemovingPasses
+            [$this->mergePass],
+            $this->getBeforeOptimizationPasses(),
+            $this->getOptimizationPasses(),
+            $this->getBeforeRemovingPasses(),
+            $this->getRemovingPasses(),
+            $this->getAfterRemovingPasses()
         );
     }
 
@@ -92,70 +118,88 @@ class PassConfig
      *
      * @throws InvalidArgumentException when a pass type doesn't exist
      */
-    public function addPass(CompilerPassInterface $pass, $type = self::TYPE_BEFORE_OPTIMIZATION)
+    public function addPass(CompilerPassInterface $pass, $type = self::TYPE_BEFORE_OPTIMIZATION/*, int $priority = 0*/)
     {
+        if (\func_num_args() >= 3) {
+            $priority = func_get_arg(2);
+        } else {
+            if (__CLASS__ !== static::class) {
+                $r = new \ReflectionMethod($this, __FUNCTION__);
+                if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
+                    @trigger_error(sprintf('Method %s() will have a third `int $priority = 0` argument in version 4.0. Not defining it is deprecated since Symfony 3.2.', __METHOD__), E_USER_DEPRECATED);
+                }
+            }
+
+            $priority = 0;
+        }
+
         $property = $type.'Passes';
         if (!isset($this->$property)) {
             throw new InvalidArgumentException(sprintf('Invalid type "%s".', $type));
         }
 
-        $this->{$property}[] = $pass;
+        $passes = &$this->$property;
+
+        if (!isset($passes[$priority])) {
+            $passes[$priority] = [];
+        }
+        $passes[$priority][] = $pass;
     }
 
     /**
      * Gets all passes for the AfterRemoving pass.
      *
-     * @return array An array of passes
+     * @return CompilerPassInterface[]
      */
     public function getAfterRemovingPasses()
     {
-        return $this->afterRemovingPasses;
+        return $this->sortPasses($this->afterRemovingPasses);
     }
 
     /**
      * Gets all passes for the BeforeOptimization pass.
      *
-     * @return array An array of passes
+     * @return CompilerPassInterface[]
      */
     public function getBeforeOptimizationPasses()
     {
-        return $this->beforeOptimizationPasses;
+        return $this->sortPasses($this->beforeOptimizationPasses);
     }
 
     /**
      * Gets all passes for the BeforeRemoving pass.
      *
-     * @return array An array of passes
+     * @return CompilerPassInterface[]
      */
     public function getBeforeRemovingPasses()
     {
-        return $this->beforeRemovingPasses;
+        return $this->sortPasses($this->beforeRemovingPasses);
     }
 
     /**
      * Gets all passes for the Optimization pass.
      *
-     * @return array An array of passes
+     * @return CompilerPassInterface[]
      */
     public function getOptimizationPasses()
     {
-        return $this->optimizationPasses;
+        return $this->sortPasses($this->optimizationPasses);
     }
 
     /**
      * Gets all passes for the Removing pass.
      *
-     * @return array An array of passes
+     * @return CompilerPassInterface[]
      */
     public function getRemovingPasses()
     {
-        return $this->removingPasses;
+        return $this->sortPasses($this->removingPasses);
     }
 
     /**
      * Gets the Merge pass.
      *
-     * @return CompilerPassInterface The merge pass
+     * @return CompilerPassInterface
      */
     public function getMergePass()
     {
@@ -170,50 +214,69 @@ class PassConfig
     /**
      * Sets the AfterRemoving passes.
      *
-     * @param array $passes An array of passes
+     * @param CompilerPassInterface[] $passes
      */
     public function setAfterRemovingPasses(array $passes)
     {
-        $this->afterRemovingPasses = $passes;
+        $this->afterRemovingPasses = [$passes];
     }
 
     /**
      * Sets the BeforeOptimization passes.
      *
-     * @param array $passes An array of passes
+     * @param CompilerPassInterface[] $passes
      */
     public function setBeforeOptimizationPasses(array $passes)
     {
-        $this->beforeOptimizationPasses = $passes;
+        $this->beforeOptimizationPasses = [$passes];
     }
 
     /**
      * Sets the BeforeRemoving passes.
      *
-     * @param array $passes An array of passes
+     * @param CompilerPassInterface[] $passes
      */
     public function setBeforeRemovingPasses(array $passes)
     {
-        $this->beforeRemovingPasses = $passes;
+        $this->beforeRemovingPasses = [$passes];
     }
 
     /**
      * Sets the Optimization passes.
      *
-     * @param array $passes An array of passes
+     * @param CompilerPassInterface[] $passes
      */
     public function setOptimizationPasses(array $passes)
     {
-        $this->optimizationPasses = $passes;
+        $this->optimizationPasses = [$passes];
     }
 
     /**
      * Sets the Removing passes.
      *
-     * @param array $passes An array of passes
+     * @param CompilerPassInterface[] $passes
      */
     public function setRemovingPasses(array $passes)
     {
-        $this->removingPasses = $passes;
+        $this->removingPasses = [$passes];
+    }
+
+    /**
+     * Sort passes by priority.
+     *
+     * @param array $passes CompilerPassInterface instances with their priority as key
+     *
+     * @return CompilerPassInterface[]
+     */
+    private function sortPasses(array $passes)
+    {
+        if (0 === \count($passes)) {
+            return [];
+        }
+
+        krsort($passes);
+
+        // Flatten the array
+        return \call_user_func_array('array_merge', $passes);
     }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/PriorityTaggedServiceTrait.php b/civicrm/vendor/symfony/dependency-injection/Compiler/PriorityTaggedServiceTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..c7e12536eade6888fca9f74848d820e1ca4f87ef
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/PriorityTaggedServiceTrait.php
@@ -0,0 +1,54 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * Trait that allows a generic method to find and sort service by priority option in the tag.
+ *
+ * @author Iltar van der Berg <kjarli@gmail.com>
+ */
+trait PriorityTaggedServiceTrait
+{
+    /**
+     * Finds all services with the given tag name and order them by their priority.
+     *
+     * The order of additions must be respected for services having the same priority,
+     * and knowing that the \SplPriorityQueue class does not respect the FIFO method,
+     * we should not use that class.
+     *
+     * @see https://bugs.php.net/53710
+     * @see https://bugs.php.net/60926
+     *
+     * @param string $tagName
+     *
+     * @return Reference[]
+     */
+    private function findAndSortTaggedServices($tagName, ContainerBuilder $container)
+    {
+        $services = [];
+
+        foreach ($container->findTaggedServiceIds($tagName, true) as $serviceId => $attributes) {
+            $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
+            $services[$priority][] = new Reference($serviceId);
+        }
+
+        if ($services) {
+            krsort($services);
+            $services = \call_user_func_array('array_merge', $services);
+        }
+
+        return $services;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/RegisterEnvVarProcessorsPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/RegisterEnvVarProcessorsPass.php
new file mode 100644
index 0000000000000000000000000000000000000000..b4d0d0550613130530af2f0cc4afdb2c2bb788d3
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/RegisterEnvVarProcessorsPass.php
@@ -0,0 +1,78 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\EnvVarProcessor;
+use Symfony\Component\DependencyInjection\EnvVarProcessorInterface;
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\ServiceLocator;
+
+/**
+ * Creates the container.env_var_processors_locator service.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class RegisterEnvVarProcessorsPass implements CompilerPassInterface
+{
+    private static $allowedTypes = ['array', 'bool', 'float', 'int', 'string'];
+
+    public function process(ContainerBuilder $container)
+    {
+        $bag = $container->getParameterBag();
+        $types = [];
+        $processors = [];
+        foreach ($container->findTaggedServiceIds('container.env_var_processor') as $id => $tags) {
+            if (!$r = $container->getReflectionClass($class = $container->getDefinition($id)->getClass())) {
+                throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
+            } elseif (!$r->isSubclassOf(EnvVarProcessorInterface::class)) {
+                throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, EnvVarProcessorInterface::class));
+            }
+            foreach ($class::getProvidedTypes() as $prefix => $type) {
+                $processors[$prefix] = new ServiceClosureArgument(new Reference($id));
+                $types[$prefix] = self::validateProvidedTypes($type, $class);
+            }
+        }
+
+        if ($bag instanceof EnvPlaceholderParameterBag) {
+            foreach (EnvVarProcessor::getProvidedTypes() as $prefix => $type) {
+                if (!isset($types[$prefix])) {
+                    $types[$prefix] = self::validateProvidedTypes($type, EnvVarProcessor::class);
+                }
+            }
+            $bag->setProvidedTypes($types);
+        }
+
+        if ($processors) {
+            $container->register('container.env_var_processors_locator', ServiceLocator::class)
+                ->setPublic(true)
+                ->setArguments([$processors])
+            ;
+        }
+    }
+
+    private static function validateProvidedTypes($types, $class)
+    {
+        $types = explode('|', $types);
+
+        foreach ($types as $type) {
+            if (!\in_array($type, self::$allowedTypes)) {
+                throw new InvalidArgumentException(sprintf('Invalid type "%s" returned by "%s::getProvidedTypes()", expected one of "%s".', $type, $class, implode('", "', self::$allowedTypes)));
+            }
+        }
+
+        return $types;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/RegisterServiceSubscribersPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/RegisterServiceSubscribersPass.php
new file mode 100644
index 0000000000000000000000000000000000000000..bf1387c04ed171b62fe6f9f7f23912cb26ff961e
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/RegisterServiceSubscribersPass.php
@@ -0,0 +1,101 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
+use Symfony\Component\DependencyInjection\TypedReference;
+
+/**
+ * Compiler pass to register tagged services that require a service locator.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class RegisterServiceSubscribersPass extends AbstractRecursivePass
+{
+    protected function processValue($value, $isRoot = false)
+    {
+        if (!$value instanceof Definition || $value->isAbstract() || $value->isSynthetic() || !$value->hasTag('container.service_subscriber')) {
+            return parent::processValue($value, $isRoot);
+        }
+
+        $serviceMap = [];
+        $autowire = $value->isAutowired();
+
+        foreach ($value->getTag('container.service_subscriber') as $attributes) {
+            if (!$attributes) {
+                $autowire = true;
+                continue;
+            }
+            ksort($attributes);
+            if ([] !== array_diff(array_keys($attributes), ['id', 'key'])) {
+                throw new InvalidArgumentException(sprintf('The "container.service_subscriber" tag accepts only the "key" and "id" attributes, "%s" given for service "%s".', implode('", "', array_keys($attributes)), $this->currentId));
+            }
+            if (!\array_key_exists('id', $attributes)) {
+                throw new InvalidArgumentException(sprintf('Missing "id" attribute on "container.service_subscriber" tag with key="%s" for service "%s".', $attributes['key'], $this->currentId));
+            }
+            if (!\array_key_exists('key', $attributes)) {
+                $attributes['key'] = $attributes['id'];
+            }
+            if (isset($serviceMap[$attributes['key']])) {
+                continue;
+            }
+            $serviceMap[$attributes['key']] = new Reference($attributes['id']);
+        }
+        $class = $value->getClass();
+
+        if (!$r = $this->container->getReflectionClass($class)) {
+            throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $this->currentId));
+        }
+        if (!$r->isSubclassOf(ServiceSubscriberInterface::class)) {
+            throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $this->currentId, ServiceSubscriberInterface::class));
+        }
+        $class = $r->name;
+
+        $subscriberMap = [];
+        $declaringClass = (new \ReflectionMethod($class, 'getSubscribedServices'))->class;
+
+        foreach ($class::getSubscribedServices() as $key => $type) {
+            if (!\is_string($type) || !preg_match('/^\??[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+$/', $type)) {
+                throw new InvalidArgumentException(sprintf('"%s::getSubscribedServices()" must return valid PHP types for service "%s" key "%s", "%s" returned.', $class, $this->currentId, $key, \is_string($type) ? $type : \gettype($type)));
+            }
+            if ($optionalBehavior = '?' === $type[0]) {
+                $type = substr($type, 1);
+                $optionalBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
+            }
+            if (\is_int($key)) {
+                $key = $type;
+            }
+            if (!isset($serviceMap[$key])) {
+                if (!$autowire) {
+                    throw new InvalidArgumentException(sprintf('Service "%s" misses a "container.service_subscriber" tag with "key"/"id" attributes corresponding to entry "%s" as returned by "%s::getSubscribedServices()".', $this->currentId, $key, $class));
+                }
+                $serviceMap[$key] = new Reference($type);
+            }
+
+            $subscriberMap[$key] = new TypedReference($this->container->normalizeId($serviceMap[$key]), $type, $declaringClass, $optionalBehavior ?: ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE);
+            unset($serviceMap[$key]);
+        }
+
+        if ($serviceMap = array_keys($serviceMap)) {
+            $message = sprintf(1 < \count($serviceMap) ? 'keys "%s" do' : 'key "%s" does', str_replace('%', '%%', implode('", "', $serviceMap)));
+            throw new InvalidArgumentException(sprintf('Service %s not exist in the map returned by "%s::getSubscribedServices()" for service "%s".', $message, $class, $this->currentId));
+        }
+
+        $value->addTag('container.service_subscriber.locator', ['id' => (string) ServiceLocatorTagPass::register($this->container, $subscriberMap, $this->currentId)]);
+
+        return parent::processValue($value);
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/RemoveAbstractDefinitionsPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/RemoveAbstractDefinitionsPass.php
index 9999214c8b763b19319ae3d1405c008f988c83ed..04b6852fab345cac238772e7e639c2d302527b9d 100644
--- a/civicrm/vendor/symfony/dependency-injection/Compiler/RemoveAbstractDefinitionsPass.php
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/RemoveAbstractDefinitionsPass.php
@@ -23,13 +23,10 @@ class RemoveAbstractDefinitionsPass implements CompilerPassInterface
      */
     public function process(ContainerBuilder $container)
     {
-        $compiler = $container->getCompiler();
-        $formatter = $compiler->getLoggingFormatter();
-
         foreach ($container->getDefinitions() as $id => $definition) {
             if ($definition->isAbstract()) {
                 $container->removeDefinition($id);
-                $compiler->addLogMessage($formatter->formatRemoveService($this, $id, 'abstract'));
+                $container->log($this, sprintf('Removed service "%s"; reason: abstract.', $id));
             }
         }
     }
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/RemovePrivateAliasesPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/RemovePrivateAliasesPass.php
index 36abc6159ee3cc0351fd4c36f736827ac3da51fe..03d9e1d8a59abcd8dce9db354051b38de1becd08 100644
--- a/civicrm/vendor/symfony/dependency-injection/Compiler/RemovePrivateAliasesPass.php
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/RemovePrivateAliasesPass.php
@@ -27,16 +27,13 @@ class RemovePrivateAliasesPass implements CompilerPassInterface
      */
     public function process(ContainerBuilder $container)
     {
-        $compiler = $container->getCompiler();
-        $formatter = $compiler->getLoggingFormatter();
-
         foreach ($container->getAliases() as $id => $alias) {
-            if ($alias->isPublic()) {
+            if ($alias->isPublic() || $alias->isPrivate()) {
                 continue;
             }
 
             $container->removeAlias($id);
-            $compiler->addLogMessage($formatter->formatRemoveService($this, $id, 'private alias'));
+            $container->log($this, sprintf('Removed service "%s"; reason: private alias.', $id));
         }
     }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/RemoveUnusedDefinitionsPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/RemoveUnusedDefinitionsPass.php
index 911875cf51d59e319f743d11d9eaf30ac472bfb0..a1013f66c00b0f73af8d33e83e94ecef57679235 100644
--- a/civicrm/vendor/symfony/dependency-injection/Compiler/RemoveUnusedDefinitionsPass.php
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/RemoveUnusedDefinitionsPass.php
@@ -35,21 +35,22 @@ class RemoveUnusedDefinitionsPass implements RepeatablePassInterface
      */
     public function process(ContainerBuilder $container)
     {
-        $compiler = $container->getCompiler();
-        $formatter = $compiler->getLoggingFormatter();
-        $graph = $compiler->getServiceReferenceGraph();
+        $graph = $container->getCompiler()->getServiceReferenceGraph();
 
         $hasChanged = false;
         foreach ($container->getDefinitions() as $id => $definition) {
-            if ($definition->isPublic()) {
+            if ($definition->isPublic() || $definition->isPrivate()) {
                 continue;
             }
 
             if ($graph->hasNode($id)) {
                 $edges = $graph->getNode($id)->getInEdges();
-                $referencingAliases = array();
-                $sourceIds = array();
+                $referencingAliases = [];
+                $sourceIds = [];
                 foreach ($edges as $edge) {
+                    if ($edge->isWeak()) {
+                        continue;
+                    }
                     $node = $edge->getSourceNode();
                     $sourceIds[] = $node->getId();
 
@@ -59,18 +60,20 @@ class RemoveUnusedDefinitionsPass implements RepeatablePassInterface
                 }
                 $isReferenced = (\count(array_unique($sourceIds)) - \count($referencingAliases)) > 0;
             } else {
-                $referencingAliases = array();
+                $referencingAliases = [];
                 $isReferenced = false;
             }
 
             if (1 === \count($referencingAliases) && false === $isReferenced) {
                 $container->setDefinition((string) reset($referencingAliases), $definition);
-                $definition->setPublic(true);
+                $definition->setPublic(!$definition->isPrivate());
+                $definition->setPrivate(reset($referencingAliases)->isPrivate());
                 $container->removeDefinition($id);
-                $compiler->addLogMessage($formatter->formatRemoveService($this, $id, 'replaces alias '.reset($referencingAliases)));
+                $container->log($this, sprintf('Removed service "%s"; reason: replaces alias %s.', $id, reset($referencingAliases)));
             } elseif (0 === \count($referencingAliases) && false === $isReferenced) {
                 $container->removeDefinition($id);
-                $compiler->addLogMessage($formatter->formatRemoveService($this, $id, 'unused'));
+                $container->resolveEnvPlaceholders(serialize($definition));
+                $container->log($this, sprintf('Removed service "%s"; reason: unused.', $id));
                 $hasChanged = true;
             }
         }
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/ReplaceAliasByActualDefinitionPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/ReplaceAliasByActualDefinitionPass.php
index cf48c53da8190816f8a1c1281c4f43e06a2ae466..472bf9415ad69cc63b48b97681dc1ebe2b81d8c4 100644
--- a/civicrm/vendor/symfony/dependency-injection/Compiler/ReplaceAliasByActualDefinitionPass.php
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/ReplaceAliasByActualDefinitionPass.php
@@ -21,10 +21,9 @@ use Symfony\Component\DependencyInjection\Reference;
  *
  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  */
-class ReplaceAliasByActualDefinitionPass implements CompilerPassInterface
+class ReplaceAliasByActualDefinitionPass extends AbstractRecursivePass
 {
-    private $compiler;
-    private $formatter;
+    private $replacements;
 
     /**
      * Process the Container to replace aliases with service definitions.
@@ -33,21 +32,18 @@ class ReplaceAliasByActualDefinitionPass implements CompilerPassInterface
      */
     public function process(ContainerBuilder $container)
     {
-        // Setup
-        $this->compiler = $container->getCompiler();
-        $this->formatter = $this->compiler->getLoggingFormatter();
         // First collect all alias targets that need to be replaced
-        $seenAliasTargets = array();
-        $replacements = array();
+        $seenAliasTargets = [];
+        $replacements = [];
         foreach ($container->getAliases() as $definitionId => $target) {
-            $targetId = (string) $target;
+            $targetId = $container->normalizeId($target);
             // Special case: leave this target alone
             if ('service_container' === $targetId) {
                 continue;
             }
             // Check if target needs to be replaces
             if (isset($replacements[$targetId])) {
-                $container->setAlias($definitionId, $replacements[$targetId]);
+                $container->setAlias($definitionId, $replacements[$targetId])->setPublic($target->isPublic())->setPrivate($target->isPrivate());
             }
             // No need to process the same target twice
             if (isset($seenAliasTargets[$targetId])) {
@@ -60,83 +56,34 @@ class ReplaceAliasByActualDefinitionPass implements CompilerPassInterface
             } catch (InvalidArgumentException $e) {
                 throw new InvalidArgumentException(sprintf('Unable to replace alias "%s" with actual definition "%s".', $definitionId, $targetId), null, $e);
             }
-            if ($definition->isPublic()) {
+            if ($definition->isPublic() || $definition->isPrivate()) {
                 continue;
             }
             // Remove private definition and schedule for replacement
-            $definition->setPublic(true);
+            $definition->setPublic(!$target->isPrivate());
+            $definition->setPrivate($target->isPrivate());
             $container->setDefinition($definitionId, $definition);
             $container->removeDefinition($targetId);
             $replacements[$targetId] = $definitionId;
         }
+        $this->replacements = $replacements;
 
-        // Now replace target instances in all definitions
-        foreach ($container->getDefinitions() as $definitionId => $definition) {
-            $definition->setArguments($this->updateArgumentReferences($replacements, $definitionId, $definition->getArguments()));
-            $definition->setMethodCalls($this->updateArgumentReferences($replacements, $definitionId, $definition->getMethodCalls()));
-            $definition->setProperties($this->updateArgumentReferences($replacements, $definitionId, $definition->getProperties()));
-            $definition->setFactoryService($this->updateFactoryReferenceId($replacements, $definition->getFactoryService(false)), false);
-            $definition->setFactory($this->updateFactoryReference($replacements, $definition->getFactory()));
-        }
+        parent::process($container);
+        $this->replacements = [];
     }
 
     /**
-     * Recursively updates references in an array.
-     *
-     * @param array  $replacements Table of aliases to replace
-     * @param string $definitionId Identifier of this definition
-     * @param array  $arguments    Where to replace the aliases
-     *
-     * @return array
+     * {@inheritdoc}
      */
-    private function updateArgumentReferences(array $replacements, $definitionId, array $arguments)
+    protected function processValue($value, $isRoot = false)
     {
-        foreach ($arguments as $k => $argument) {
-            // Handle recursion step
-            if (\is_array($argument)) {
-                $arguments[$k] = $this->updateArgumentReferences($replacements, $definitionId, $argument);
-                continue;
-            }
-            // Skip arguments that don't need replacement
-            if (!$argument instanceof Reference) {
-                continue;
-            }
-            $referenceId = (string) $argument;
-            if (!isset($replacements[$referenceId])) {
-                continue;
-            }
+        if ($value instanceof Reference && isset($this->replacements[$referenceId = $this->container->normalizeId($value)])) {
             // Perform the replacement
-            $newId = $replacements[$referenceId];
-            $arguments[$k] = new Reference($newId, $argument->getInvalidBehavior());
-            $this->compiler->addLogMessage($this->formatter->formatUpdateReference($this, $definitionId, $referenceId, $newId));
-        }
-
-        return $arguments;
-    }
-
-    /**
-     * Returns the updated reference for the factory service.
-     *
-     * @param array       $replacements Table of aliases to replace
-     * @param string|null $referenceId  Factory service reference identifier
-     *
-     * @return string|null
-     */
-    private function updateFactoryReferenceId(array $replacements, $referenceId)
-    {
-        if (null === $referenceId) {
-            return;
-        }
-
-        return isset($replacements[$referenceId]) ? $replacements[$referenceId] : $referenceId;
-    }
-
-    private function updateFactoryReference(array $replacements, $factory)
-    {
-        if (\is_array($factory) && $factory[0] instanceof Reference && isset($replacements[$referenceId = (string) $factory[0]])) {
-            $factory[0] = new Reference($replacements[$referenceId], $factory[0]->getInvalidBehavior());
+            $newId = $this->replacements[$referenceId];
+            $value = new Reference($newId, $value->getInvalidBehavior());
+            $this->container->log($this, sprintf('Changed reference of service "%s" previously pointing to "%s" to "%s".', $this->currentId, $referenceId, $newId));
         }
 
-        return $factory;
+        return parent::processValue($value, $isRoot);
     }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveBindingsPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveBindingsPass.php
new file mode 100644
index 0000000000000000000000000000000000000000..065dbb4b40a1be35a95fe5ff1a9d02b2493b6024
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveBindingsPass.php
@@ -0,0 +1,180 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\Argument\BoundArgument;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\TypedReference;
+
+/**
+ * @author Guilhem Niot <guilhem.niot@gmail.com>
+ */
+class ResolveBindingsPass extends AbstractRecursivePass
+{
+    private $usedBindings = [];
+    private $unusedBindings = [];
+    private $errorMessages = [];
+
+    /**
+     * {@inheritdoc}
+     */
+    public function process(ContainerBuilder $container)
+    {
+        $this->usedBindings = $container->getRemovedBindingIds();
+
+        try {
+            parent::process($container);
+
+            foreach ($this->unusedBindings as list($key, $serviceId)) {
+                $message = sprintf('Unused binding "%s" in service "%s".', $key, $serviceId);
+                if ($this->errorMessages) {
+                    $message .= sprintf("\nCould be related to%s:", 1 < \count($this->errorMessages) ? ' one of' : '');
+                }
+                foreach ($this->errorMessages as $m) {
+                    $message .= "\n - ".$m;
+                }
+                throw new InvalidArgumentException($message);
+            }
+        } finally {
+            $this->usedBindings = [];
+            $this->unusedBindings = [];
+            $this->errorMessages = [];
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function processValue($value, $isRoot = false)
+    {
+        if ($value instanceof TypedReference && $value->getType() === $this->container->normalizeId($value)) {
+            // Already checked
+            $bindings = $this->container->getDefinition($this->currentId)->getBindings();
+
+            if (isset($bindings[$value->getType()])) {
+                return $this->getBindingValue($bindings[$value->getType()]);
+            }
+
+            return parent::processValue($value, $isRoot);
+        }
+
+        if (!$value instanceof Definition || !$bindings = $value->getBindings()) {
+            return parent::processValue($value, $isRoot);
+        }
+
+        foreach ($bindings as $key => $binding) {
+            list($bindingValue, $bindingId, $used) = $binding->getValues();
+            if ($used) {
+                $this->usedBindings[$bindingId] = true;
+                unset($this->unusedBindings[$bindingId]);
+            } elseif (!isset($this->usedBindings[$bindingId])) {
+                $this->unusedBindings[$bindingId] = [$key, $this->currentId];
+            }
+
+            if (isset($key[0]) && '$' === $key[0]) {
+                continue;
+            }
+
+            if (null !== $bindingValue && !$bindingValue instanceof Reference && !$bindingValue instanceof Definition) {
+                throw new InvalidArgumentException(sprintf('Invalid value for binding key "%s" for service "%s": expected null, an instance of "%s" or an instance of "%s", "%s" given.', $key, $this->currentId, Reference::class, Definition::class, \gettype($bindingValue)));
+            }
+        }
+
+        if ($value->isAbstract()) {
+            return parent::processValue($value, $isRoot);
+        }
+
+        $calls = $value->getMethodCalls();
+
+        try {
+            if ($constructor = $this->getConstructor($value, false)) {
+                $calls[] = [$constructor, $value->getArguments()];
+            }
+        } catch (RuntimeException $e) {
+            $this->errorMessages[] = $e->getMessage();
+            $this->container->getDefinition($this->currentId)->addError($e->getMessage());
+
+            return parent::processValue($value, $isRoot);
+        }
+
+        foreach ($calls as $i => $call) {
+            list($method, $arguments) = $call;
+
+            if ($method instanceof \ReflectionFunctionAbstract) {
+                $reflectionMethod = $method;
+            } else {
+                try {
+                    $reflectionMethod = $this->getReflectionMethod($value, $method);
+                } catch (RuntimeException $e) {
+                    if ($value->getFactory()) {
+                        continue;
+                    }
+                    throw $e;
+                }
+            }
+
+            foreach ($reflectionMethod->getParameters() as $key => $parameter) {
+                if (\array_key_exists($key, $arguments) && '' !== $arguments[$key]) {
+                    continue;
+                }
+
+                if (\array_key_exists('$'.$parameter->name, $bindings)) {
+                    $arguments[$key] = $this->getBindingValue($bindings['$'.$parameter->name]);
+
+                    continue;
+                }
+
+                $typeHint = ProxyHelper::getTypeHint($reflectionMethod, $parameter, true);
+
+                if (!isset($bindings[$typeHint])) {
+                    continue;
+                }
+
+                $arguments[$key] = $this->getBindingValue($bindings[$typeHint]);
+            }
+
+            if ($arguments !== $call[1]) {
+                ksort($arguments);
+                $calls[$i][1] = $arguments;
+            }
+        }
+
+        if ($constructor) {
+            list(, $arguments) = array_pop($calls);
+
+            if ($arguments !== $value->getArguments()) {
+                $value->setArguments($arguments);
+            }
+        }
+
+        if ($calls !== $value->getMethodCalls()) {
+            $value->setMethodCalls($calls);
+        }
+
+        return parent::processValue($value, $isRoot);
+    }
+
+    private function getBindingValue(BoundArgument $binding)
+    {
+        list($bindingValue, $bindingId) = $binding->getValues();
+
+        $this->usedBindings[$bindingId] = true;
+        unset($this->unusedBindings[$bindingId]);
+
+        return $bindingValue;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveChildDefinitionsPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveChildDefinitionsPass.php
new file mode 100644
index 0000000000000000000000000000000000000000..539395a43742ec1bcadeb35d130f2e6bf05b7a04
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveChildDefinitionsPass.php
@@ -0,0 +1,198 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\ChildDefinition;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Exception\ExceptionInterface;
+use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
+
+/**
+ * This replaces all ChildDefinition instances with their equivalent fully
+ * merged Definition instance.
+ *
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class ResolveChildDefinitionsPass extends AbstractRecursivePass
+{
+    private $currentPath;
+
+    protected function processValue($value, $isRoot = false)
+    {
+        if (!$value instanceof Definition) {
+            return parent::processValue($value, $isRoot);
+        }
+        if ($isRoot) {
+            // yes, we are specifically fetching the definition from the
+            // container to ensure we are not operating on stale data
+            $value = $this->container->getDefinition($this->currentId);
+        }
+        if ($value instanceof ChildDefinition) {
+            $this->currentPath = [];
+            $value = $this->resolveDefinition($value);
+            if ($isRoot) {
+                $this->container->setDefinition($this->currentId, $value);
+            }
+        }
+
+        return parent::processValue($value, $isRoot);
+    }
+
+    /**
+     * Resolves the definition.
+     *
+     * @return Definition
+     *
+     * @throws RuntimeException When the definition is invalid
+     */
+    private function resolveDefinition(ChildDefinition $definition)
+    {
+        try {
+            return $this->doResolveDefinition($definition);
+        } catch (ServiceCircularReferenceException $e) {
+            throw $e;
+        } catch (ExceptionInterface $e) {
+            $r = new \ReflectionProperty($e, 'message');
+            $r->setAccessible(true);
+            $r->setValue($e, sprintf('Service "%s": %s', $this->currentId, $e->getMessage()));
+
+            throw $e;
+        }
+    }
+
+    private function doResolveDefinition(ChildDefinition $definition)
+    {
+        if (!$this->container->has($parent = $definition->getParent())) {
+            throw new RuntimeException(sprintf('Parent definition "%s" does not exist.', $parent));
+        }
+
+        $searchKey = array_search($parent, $this->currentPath);
+        $this->currentPath[] = $parent;
+
+        if (false !== $searchKey) {
+            throw new ServiceCircularReferenceException($parent, \array_slice($this->currentPath, $searchKey));
+        }
+
+        $parentDef = $this->container->findDefinition($parent);
+        if ($parentDef instanceof ChildDefinition) {
+            $id = $this->currentId;
+            $this->currentId = $parent;
+            $parentDef = $this->resolveDefinition($parentDef);
+            $this->container->setDefinition($parent, $parentDef);
+            $this->currentId = $id;
+        }
+
+        $this->container->log($this, sprintf('Resolving inheritance for "%s" (parent: %s).', $this->currentId, $parent));
+        $def = new Definition();
+
+        // merge in parent definition
+        // purposely ignored attributes: abstract, shared, tags, autoconfigured
+        $def->setClass($parentDef->getClass());
+        $def->setArguments($parentDef->getArguments());
+        $def->setMethodCalls($parentDef->getMethodCalls());
+        $def->setProperties($parentDef->getProperties());
+        if ($parentDef->getAutowiringTypes(false)) {
+            $def->setAutowiringTypes($parentDef->getAutowiringTypes(false));
+        }
+        if ($parentDef->isDeprecated()) {
+            $def->setDeprecated(true, $parentDef->getDeprecationMessage('%service_id%'));
+        }
+        $def->setFactory($parentDef->getFactory());
+        $def->setConfigurator($parentDef->getConfigurator());
+        $def->setFile($parentDef->getFile());
+        $def->setPublic($parentDef->isPublic());
+        $def->setLazy($parentDef->isLazy());
+        $def->setAutowired($parentDef->isAutowired());
+        $def->setChanges($parentDef->getChanges());
+
+        $def->setBindings($definition->getBindings() + $parentDef->getBindings());
+
+        // overwrite with values specified in the decorator
+        $changes = $definition->getChanges();
+        if (isset($changes['class'])) {
+            $def->setClass($definition->getClass());
+        }
+        if (isset($changes['factory'])) {
+            $def->setFactory($definition->getFactory());
+        }
+        if (isset($changes['configurator'])) {
+            $def->setConfigurator($definition->getConfigurator());
+        }
+        if (isset($changes['file'])) {
+            $def->setFile($definition->getFile());
+        }
+        if (isset($changes['public'])) {
+            $def->setPublic($definition->isPublic());
+        } else {
+            $def->setPrivate($definition->isPrivate() || $parentDef->isPrivate());
+        }
+        if (isset($changes['lazy'])) {
+            $def->setLazy($definition->isLazy());
+        }
+        if (isset($changes['deprecated'])) {
+            $def->setDeprecated($definition->isDeprecated(), $definition->getDeprecationMessage('%service_id%'));
+        }
+        if (isset($changes['autowired'])) {
+            $def->setAutowired($definition->isAutowired());
+        }
+        if (isset($changes['shared'])) {
+            $def->setShared($definition->isShared());
+        }
+        if (isset($changes['decorated_service'])) {
+            $decoratedService = $definition->getDecoratedService();
+            if (null === $decoratedService) {
+                $def->setDecoratedService($decoratedService);
+            } else {
+                $def->setDecoratedService($decoratedService[0], $decoratedService[1], $decoratedService[2]);
+            }
+        }
+
+        // merge arguments
+        foreach ($definition->getArguments() as $k => $v) {
+            if (is_numeric($k)) {
+                $def->addArgument($v);
+            } elseif (0 === strpos($k, 'index_')) {
+                $def->replaceArgument((int) substr($k, \strlen('index_')), $v);
+            } else {
+                $def->setArgument($k, $v);
+            }
+        }
+
+        // merge properties
+        foreach ($definition->getProperties() as $k => $v) {
+            $def->setProperty($k, $v);
+        }
+
+        // append method calls
+        if ($calls = $definition->getMethodCalls()) {
+            $def->setMethodCalls(array_merge($def->getMethodCalls(), $calls));
+        }
+
+        // merge autowiring types
+        foreach ($definition->getAutowiringTypes(false) as $autowiringType) {
+            $def->addAutowiringType($autowiringType);
+        }
+
+        // these attributes are always taken from the child
+        $def->setAbstract($definition->isAbstract());
+        $def->setTags($definition->getTags());
+        // autoconfigure is never taken from parent (on purpose)
+        // and it's not legal on an instanceof
+        $def->setAutoconfigured($definition->isAutoconfigured());
+
+        return $def;
+    }
+}
+
+class_alias(ResolveChildDefinitionsPass::class, ResolveDefinitionTemplatesPass::class);
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveClassPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveClassPass.php
new file mode 100644
index 0000000000000000000000000000000000000000..b1c1b4f884e01431bdebb08fb6d3475d1f7f2150
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveClassPass.php
@@ -0,0 +1,56 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\ChildDefinition;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class ResolveClassPass implements CompilerPassInterface
+{
+    private $changes = [];
+
+    /**
+     * {@inheritdoc}
+     */
+    public function process(ContainerBuilder $container)
+    {
+        foreach ($container->getDefinitions() as $id => $definition) {
+            if ($definition->isSynthetic() || null !== $definition->getClass()) {
+                continue;
+            }
+            if (preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)++$/', $id)) {
+                if ($definition instanceof ChildDefinition && !class_exists($id)) {
+                    throw new InvalidArgumentException(sprintf('Service definition "%s" has a parent but no class, and its name looks like a FQCN. Either the class is missing or you want to inherit it from the parent service. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class.', $id));
+                }
+                $this->changes[strtolower($id)] = $id;
+                $definition->setClass($id);
+            }
+        }
+    }
+
+    /**
+     * @internal
+     *
+     * @deprecated since 3.3, to be removed in 4.0.
+     */
+    public function getChanges()
+    {
+        $changes = $this->changes;
+        $this->changes = [];
+
+        return $changes;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveDefinitionTemplatesPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveDefinitionTemplatesPass.php
index db1864ec01fbd22e018fb1fbc78974a56bd3684a..d48eff221463504d41faf5c162936d939d30f373 100644
--- a/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveDefinitionTemplatesPass.php
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveDefinitionTemplatesPass.php
@@ -11,211 +11,19 @@
 
 namespace Symfony\Component\DependencyInjection\Compiler;
 
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\DependencyInjection\Definition;
-use Symfony\Component\DependencyInjection\DefinitionDecorator;
-use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+@trigger_error('The '.__NAMESPACE__.'\ResolveDefinitionTemplatesPass class is deprecated since Symfony 3.4 and will be removed in 4.0. Use the ResolveChildDefinitionsPass class instead.', E_USER_DEPRECATED);
 
-/**
- * This replaces all DefinitionDecorator instances with their equivalent fully
- * merged Definition instance.
- *
- * @author Johannes M. Schmitt <schmittjoh@gmail.com>
- * @author Nicolas Grekas <p@tchwork.com>
- */
-class ResolveDefinitionTemplatesPass implements CompilerPassInterface
-{
-    private $compiler;
-    private $formatter;
-    private $currentId;
-
-    /**
-     * Process the ContainerBuilder to replace DefinitionDecorator instances with their real Definition instances.
-     */
-    public function process(ContainerBuilder $container)
-    {
-        $this->compiler = $container->getCompiler();
-        $this->formatter = $this->compiler->getLoggingFormatter();
-
-        $container->setDefinitions($this->resolveArguments($container, $container->getDefinitions(), true));
-    }
+class_exists(ResolveChildDefinitionsPass::class);
 
+if (false) {
     /**
-     * Resolves definition decorator arguments.
+     * This definition decorates another definition.
      *
-     * @param ContainerBuilder $container The ContainerBuilder
-     * @param array            $arguments An array of arguments
-     * @param bool             $isRoot    If we are processing the root definitions or not
+     * @author Johannes M. Schmitt <schmittjoh@gmail.com>
      *
-     * @return array
+     * @deprecated The ResolveDefinitionTemplatesPass class is deprecated since version 3.4 and will be removed in 4.0. Use the ResolveChildDefinitionsPass class instead.
      */
-    private function resolveArguments(ContainerBuilder $container, array $arguments, $isRoot = false)
+    class ResolveDefinitionTemplatesPass extends AbstractRecursivePass
     {
-        foreach ($arguments as $k => $argument) {
-            if ($isRoot) {
-                // yes, we are specifically fetching the definition from the
-                // container to ensure we are not operating on stale data
-                $arguments[$k] = $argument = $container->getDefinition($k);
-                $this->currentId = $k;
-            }
-            if (\is_array($argument)) {
-                $arguments[$k] = $this->resolveArguments($container, $argument);
-            } elseif ($argument instanceof Definition) {
-                if ($argument instanceof DefinitionDecorator) {
-                    $arguments[$k] = $argument = $this->resolveDefinition($container, $argument);
-                    if ($isRoot) {
-                        $container->setDefinition($k, $argument);
-                    }
-                }
-                $argument->setArguments($this->resolveArguments($container, $argument->getArguments()));
-                $argument->setMethodCalls($this->resolveArguments($container, $argument->getMethodCalls()));
-                $argument->setProperties($this->resolveArguments($container, $argument->getProperties()));
-
-                $configurator = $this->resolveArguments($container, array($argument->getConfigurator()));
-                $argument->setConfigurator($configurator[0]);
-
-                $factory = $this->resolveArguments($container, array($argument->getFactory()));
-                $argument->setFactory($factory[0]);
-            }
-        }
-
-        return $arguments;
-    }
-
-    /**
-     * Resolves the definition.
-     *
-     * @param ContainerBuilder    $container  The ContainerBuilder
-     * @param DefinitionDecorator $definition
-     *
-     * @return Definition
-     *
-     * @throws \RuntimeException When the definition is invalid
-     */
-    private function resolveDefinition(ContainerBuilder $container, DefinitionDecorator $definition)
-    {
-        if (!$container->hasDefinition($parent = $definition->getParent())) {
-            throw new RuntimeException(sprintf('The parent definition "%s" defined for definition "%s" does not exist.', $parent, $this->currentId));
-        }
-
-        $parentDef = $container->getDefinition($parent);
-        if ($parentDef instanceof DefinitionDecorator) {
-            $id = $this->currentId;
-            $this->currentId = $parent;
-            $parentDef = $this->resolveDefinition($container, $parentDef);
-            $container->setDefinition($parent, $parentDef);
-            $this->currentId = $id;
-        }
-
-        $this->compiler->addLogMessage($this->formatter->formatResolveInheritance($this, $this->currentId, $parent));
-        $def = new Definition();
-
-        // merge in parent definition
-        // purposely ignored attributes: scope, abstract, tags
-        $def->setClass($parentDef->getClass());
-        $def->setArguments($parentDef->getArguments());
-        $def->setMethodCalls($parentDef->getMethodCalls());
-        $def->setProperties($parentDef->getProperties());
-        $def->setAutowiringTypes($parentDef->getAutowiringTypes());
-        if ($parentDef->getFactoryClass(false)) {
-            $def->setFactoryClass($parentDef->getFactoryClass(false));
-        }
-        if ($parentDef->getFactoryMethod(false)) {
-            $def->setFactoryMethod($parentDef->getFactoryMethod(false));
-        }
-        if ($parentDef->getFactoryService(false)) {
-            $def->setFactoryService($parentDef->getFactoryService(false));
-        }
-        if ($parentDef->isDeprecated()) {
-            $def->setDeprecated(true, $parentDef->getDeprecationMessage('%service_id%'));
-        }
-        $def->setFactory($parentDef->getFactory());
-        $def->setConfigurator($parentDef->getConfigurator());
-        $def->setFile($parentDef->getFile());
-        $def->setPublic($parentDef->isPublic());
-        $def->setLazy($parentDef->isLazy());
-        $def->setAutowired($parentDef->isAutowired());
-
-        // overwrite with values specified in the decorator
-        $changes = $definition->getChanges();
-        if (isset($changes['class'])) {
-            $def->setClass($definition->getClass());
-        }
-        if (isset($changes['factory_class'])) {
-            $def->setFactoryClass($definition->getFactoryClass(false));
-        }
-        if (isset($changes['factory_method'])) {
-            $def->setFactoryMethod($definition->getFactoryMethod(false));
-        }
-        if (isset($changes['factory_service'])) {
-            $def->setFactoryService($definition->getFactoryService(false));
-        }
-        if (isset($changes['factory'])) {
-            $def->setFactory($definition->getFactory());
-        }
-        if (isset($changes['configurator'])) {
-            $def->setConfigurator($definition->getConfigurator());
-        }
-        if (isset($changes['file'])) {
-            $def->setFile($definition->getFile());
-        }
-        if (isset($changes['public'])) {
-            $def->setPublic($definition->isPublic());
-        }
-        if (isset($changes['lazy'])) {
-            $def->setLazy($definition->isLazy());
-        }
-        if (isset($changes['deprecated'])) {
-            $def->setDeprecated($definition->isDeprecated(), $definition->getDeprecationMessage('%service_id%'));
-        }
-        if (isset($changes['autowire'])) {
-            $def->setAutowired($definition->isAutowired());
-        }
-        if (isset($changes['decorated_service'])) {
-            $decoratedService = $definition->getDecoratedService();
-            if (null === $decoratedService) {
-                $def->setDecoratedService($decoratedService);
-            } else {
-                $def->setDecoratedService($decoratedService[0], $decoratedService[1], $decoratedService[2]);
-            }
-        }
-
-        // merge arguments
-        foreach ($definition->getArguments() as $k => $v) {
-            if (is_numeric($k)) {
-                $def->addArgument($v);
-                continue;
-            }
-
-            if (0 !== strpos($k, 'index_')) {
-                throw new RuntimeException(sprintf('Invalid argument key "%s" found.', $k));
-            }
-
-            $index = (int) substr($k, \strlen('index_'));
-            $def->replaceArgument($index, $v);
-        }
-
-        // merge properties
-        foreach ($definition->getProperties() as $k => $v) {
-            $def->setProperty($k, $v);
-        }
-
-        // append method calls
-        if (\count($calls = $definition->getMethodCalls()) > 0) {
-            $def->setMethodCalls(array_merge($def->getMethodCalls(), $calls));
-        }
-
-        // merge autowiring types
-        foreach ($definition->getAutowiringTypes() as $autowiringType) {
-            $def->addAutowiringType($autowiringType);
-        }
-
-        // these attributes are always taken from the child
-        $def->setAbstract($definition->isAbstract());
-        $def->setScope($definition->getScope(false), false);
-        $def->setShared($definition->isShared());
-        $def->setTags($definition->getTags());
-
-        return $def;
     }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveEnvPlaceholdersPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveEnvPlaceholdersPass.php
new file mode 100644
index 0000000000000000000000000000000000000000..9e1edd4d3156885de631a460d12f512a5ba20635
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveEnvPlaceholdersPass.php
@@ -0,0 +1,44 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\Definition;
+
+/**
+ * Replaces env var placeholders by their current values.
+ */
+class ResolveEnvPlaceholdersPass extends AbstractRecursivePass
+{
+    protected function processValue($value, $isRoot = false)
+    {
+        if (\is_string($value)) {
+            return $this->container->resolveEnvPlaceholders($value, true);
+        }
+        if ($value instanceof Definition) {
+            $changes = $value->getChanges();
+            if (isset($changes['class'])) {
+                $value->setClass($this->container->resolveEnvPlaceholders($value->getClass(), true));
+            }
+            if (isset($changes['file'])) {
+                $value->setFile($this->container->resolveEnvPlaceholders($value->getFile(), true));
+            }
+        }
+
+        $value = parent::processValue($value, $isRoot);
+
+        if ($value && \is_array($value) && !$isRoot) {
+            $value = array_combine($this->container->resolveEnvPlaceholders(array_keys($value), true), $value);
+        }
+
+        return $value;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveFactoryClassPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveFactoryClassPass.php
new file mode 100644
index 0000000000000000000000000000000000000000..848da7f2bd24b64c0153d090ba9c93a4bb43abc8
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveFactoryClassPass.php
@@ -0,0 +1,38 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+
+/**
+ * @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
+ */
+class ResolveFactoryClassPass extends AbstractRecursivePass
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function processValue($value, $isRoot = false)
+    {
+        if ($value instanceof Definition && \is_array($factory = $value->getFactory()) && null === $factory[0]) {
+            if (null === $class = $value->getClass()) {
+                throw new RuntimeException(sprintf('The "%s" service is defined to be created by a factory, but is missing the factory class. Did you forget to define the factory or service class?', $this->currentId));
+            }
+
+            $factory[0] = $class;
+            $value->setFactory($factory);
+        }
+
+        return parent::processValue($value, $isRoot);
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveHotPathPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveHotPathPass.php
new file mode 100644
index 0000000000000000000000000000000000000000..4e025113acdb8326aee95a8b9536fec25e7685c5
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveHotPathPass.php
@@ -0,0 +1,71 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * Propagate "container.hot_path" tags to referenced services.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class ResolveHotPathPass extends AbstractRecursivePass
+{
+    private $tagName;
+    private $resolvedIds = [];
+
+    public function __construct($tagName = 'container.hot_path')
+    {
+        $this->tagName = $tagName;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function process(ContainerBuilder $container)
+    {
+        try {
+            parent::process($container);
+            $container->getDefinition('service_container')->clearTag($this->tagName);
+        } finally {
+            $this->resolvedIds = [];
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function processValue($value, $isRoot = false)
+    {
+        if ($value instanceof ArgumentInterface) {
+            return $value;
+        }
+        if ($value instanceof Definition && $isRoot && (isset($this->resolvedIds[$this->currentId]) || !$value->hasTag($this->tagName) || $value->isDeprecated())) {
+            return $value->isDeprecated() ? $value->clearTag($this->tagName) : $value;
+        }
+        if ($value instanceof Reference && ContainerBuilder::IGNORE_ON_UNINITIALIZED_REFERENCE !== $value->getInvalidBehavior() && $this->container->has($id = $this->container->normalizeId($value))) {
+            $definition = $this->container->findDefinition($id);
+            if (!$definition->hasTag($this->tagName) && !$definition->isDeprecated()) {
+                $this->resolvedIds[$id] = true;
+                $definition->addTag($this->tagName);
+                parent::processValue($definition, false);
+            }
+
+            return $value;
+        }
+
+        return parent::processValue($value, $isRoot);
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveInstanceofConditionalsPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveInstanceofConditionalsPass.php
new file mode 100644
index 0000000000000000000000000000000000000000..6268ed9ed048cba4d3faec66ce37c80a66a1c61f
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveInstanceofConditionalsPass.php
@@ -0,0 +1,156 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\ChildDefinition;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+
+/**
+ * Applies instanceof conditionals to definitions.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class ResolveInstanceofConditionalsPass implements CompilerPassInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function process(ContainerBuilder $container)
+    {
+        foreach ($container->getAutoconfiguredInstanceof() as $interface => $definition) {
+            if ($definition->getArguments()) {
+                throw new InvalidArgumentException(sprintf('Autoconfigured instanceof for type "%s" defines arguments but these are not supported and should be removed.', $interface));
+            }
+            if ($definition->getMethodCalls()) {
+                throw new InvalidArgumentException(sprintf('Autoconfigured instanceof for type "%s" defines method calls but these are not supported and should be removed.', $interface));
+            }
+        }
+
+        foreach ($container->getDefinitions() as $id => $definition) {
+            if ($definition instanceof ChildDefinition) {
+                // don't apply "instanceof" to children: it will be applied to their parent
+                continue;
+            }
+            $container->setDefinition($id, $this->processDefinition($container, $id, $definition));
+        }
+    }
+
+    private function processDefinition(ContainerBuilder $container, $id, Definition $definition)
+    {
+        $instanceofConditionals = $definition->getInstanceofConditionals();
+        $autoconfiguredInstanceof = $definition->isAutoconfigured() ? $container->getAutoconfiguredInstanceof() : [];
+        if (!$instanceofConditionals && !$autoconfiguredInstanceof) {
+            return $definition;
+        }
+
+        if (!$class = $container->getParameterBag()->resolveValue($definition->getClass())) {
+            return $definition;
+        }
+
+        $conditionals = $this->mergeConditionals($autoconfiguredInstanceof, $instanceofConditionals, $container);
+
+        $definition->setInstanceofConditionals([]);
+        $parent = $shared = null;
+        $instanceofTags = [];
+        $reflectionClass = null;
+
+        foreach ($conditionals as $interface => $instanceofDefs) {
+            if ($interface !== $class && !(null === $reflectionClass ? $reflectionClass = ($container->getReflectionClass($class, false) ?: false) : $reflectionClass)) {
+                continue;
+            }
+
+            if ($interface !== $class && !is_subclass_of($class, $interface)) {
+                continue;
+            }
+
+            foreach ($instanceofDefs as $key => $instanceofDef) {
+                /** @var ChildDefinition $instanceofDef */
+                $instanceofDef = clone $instanceofDef;
+                $instanceofDef->setAbstract(true)->setParent($parent ?: 'abstract.instanceof.'.$id);
+                $parent = 'instanceof.'.$interface.'.'.$key.'.'.$id;
+                $container->setDefinition($parent, $instanceofDef);
+                $instanceofTags[] = $instanceofDef->getTags();
+                $instanceofDef->setTags([]);
+
+                if (isset($instanceofDef->getChanges()['shared'])) {
+                    $shared = $instanceofDef->isShared();
+                }
+            }
+        }
+
+        if ($parent) {
+            $bindings = $definition->getBindings();
+            $abstract = $container->setDefinition('abstract.instanceof.'.$id, $definition);
+
+            // cast Definition to ChildDefinition
+            $definition->setBindings([]);
+            $definition = serialize($definition);
+            $definition = substr_replace($definition, '53', 2, 2);
+            $definition = substr_replace($definition, 'Child', 44, 0);
+            $definition = unserialize($definition);
+            $definition->setParent($parent);
+
+            if (null !== $shared && !isset($definition->getChanges()['shared'])) {
+                $definition->setShared($shared);
+            }
+
+            $i = \count($instanceofTags);
+            while (0 <= --$i) {
+                foreach ($instanceofTags[$i] as $k => $v) {
+                    foreach ($v as $v) {
+                        if ($definition->hasTag($k) && \in_array($v, $definition->getTag($k))) {
+                            continue;
+                        }
+                        $definition->addTag($k, $v);
+                    }
+                }
+            }
+
+            $definition->setBindings($bindings);
+
+            // reset fields with "merge" behavior
+            $abstract
+                ->setBindings([])
+                ->setArguments([])
+                ->setMethodCalls([])
+                ->setDecoratedService(null)
+                ->setTags([])
+                ->setAbstract(true);
+        }
+
+        return $definition;
+    }
+
+    private function mergeConditionals(array $autoconfiguredInstanceof, array $instanceofConditionals, ContainerBuilder $container)
+    {
+        // make each value an array of ChildDefinition
+        $conditionals = array_map(function ($childDef) { return [$childDef]; }, $autoconfiguredInstanceof);
+
+        foreach ($instanceofConditionals as $interface => $instanceofDef) {
+            // make sure the interface/class exists (but don't validate automaticInstanceofConditionals)
+            if (!$container->getReflectionClass($interface)) {
+                throw new RuntimeException(sprintf('"%s" is set as an "instanceof" conditional, but it does not exist.', $interface));
+            }
+
+            if (!isset($autoconfiguredInstanceof[$interface])) {
+                $conditionals[$interface] = [];
+            }
+
+            $conditionals[$interface][] = $instanceofDef;
+        }
+
+        return $conditionals;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveInvalidReferencesPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveInvalidReferencesPass.php
index 181c85fa8f9e8f01a9bb3d7e919eab3f29cbbbbe..f1a1475aeb4ab6b9211e43a17d872473b1566e50 100644
--- a/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveInvalidReferencesPass.php
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveInvalidReferencesPass.php
@@ -11,8 +11,11 @@
 
 namespace Symfony\Component\DependencyInjection\Compiler;
 
+use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
+use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\DependencyInjection\Definition;
 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
 use Symfony\Component\DependencyInjection\Reference;
 
@@ -25,6 +28,7 @@ use Symfony\Component\DependencyInjection\Reference;
 class ResolveInvalidReferencesPass implements CompilerPassInterface
 {
     private $container;
+    private $signalingException;
 
     /**
      * Process the ContainerBuilder to resolve invalid references.
@@ -32,72 +36,76 @@ class ResolveInvalidReferencesPass implements CompilerPassInterface
     public function process(ContainerBuilder $container)
     {
         $this->container = $container;
-        foreach ($container->getDefinitions() as $definition) {
-            if ($definition->isSynthetic() || $definition->isAbstract()) {
-                continue;
-            }
-
-            $definition->setArguments(
-                $this->processArguments($definition->getArguments())
-            );
-
-            $calls = array();
-            foreach ($definition->getMethodCalls() as $call) {
-                try {
-                    $calls[] = array($call[0], $this->processArguments($call[1], true));
-                } catch (RuntimeException $e) {
-                    // this call is simply removed
-                }
-            }
-            $definition->setMethodCalls($calls);
+        $this->signalingException = new RuntimeException('Invalid reference.');
 
-            $properties = array();
-            foreach ($definition->getProperties() as $name => $value) {
-                try {
-                    $value = $this->processArguments(array($value), true);
-                    $properties[$name] = reset($value);
-                } catch (RuntimeException $e) {
-                    // ignore property
-                }
-            }
-            $definition->setProperties($properties);
+        try {
+            $this->processValue($container->getDefinitions(), 1);
+        } finally {
+            $this->container = $this->signalingException = null;
         }
     }
 
     /**
      * Processes arguments to determine invalid references.
      *
-     * @param array $arguments    An array of Reference objects
-     * @param bool  $inMethodCall
-     *
-     * @return array
-     *
-     * @throws RuntimeException When the config is invalid
+     * @throws RuntimeException When an invalid reference is found
      */
-    private function processArguments(array $arguments, $inMethodCall = false)
+    private function processValue($value, $rootLevel = 0, $level = 0)
     {
-        foreach ($arguments as $k => $argument) {
-            if (\is_array($argument)) {
-                $arguments[$k] = $this->processArguments($argument, $inMethodCall);
-            } elseif ($argument instanceof Reference) {
-                $id = (string) $argument;
-
-                $invalidBehavior = $argument->getInvalidBehavior();
-                $exists = $this->container->has($id);
+        if ($value instanceof ServiceClosureArgument) {
+            $value->setValues($this->processValue($value->getValues(), 1, 1));
+        } elseif ($value instanceof ArgumentInterface) {
+            $value->setValues($this->processValue($value->getValues(), $rootLevel, 1 + $level));
+        } elseif ($value instanceof Definition) {
+            if ($value->isSynthetic() || $value->isAbstract()) {
+                return $value;
+            }
+            $value->setArguments($this->processValue($value->getArguments(), 0));
+            $value->setProperties($this->processValue($value->getProperties(), 1));
+            $value->setMethodCalls($this->processValue($value->getMethodCalls(), 2));
+        } elseif (\is_array($value)) {
+            $i = 0;
 
-                // resolve invalid behavior
-                if (!$exists && ContainerInterface::NULL_ON_INVALID_REFERENCE === $invalidBehavior) {
-                    $arguments[$k] = null;
-                } elseif (!$exists && ContainerInterface::IGNORE_ON_INVALID_REFERENCE === $invalidBehavior) {
-                    if ($inMethodCall) {
-                        throw new RuntimeException('Method shouldn\'t be called.');
+            foreach ($value as $k => $v) {
+                try {
+                    if (false !== $i && $k !== $i++) {
+                        $i = false;
+                    }
+                    if ($v !== $processedValue = $this->processValue($v, $rootLevel, 1 + $level)) {
+                        $value[$k] = $processedValue;
+                    }
+                } catch (RuntimeException $e) {
+                    if ($rootLevel < $level || ($rootLevel && !$level)) {
+                        unset($value[$k]);
+                    } elseif ($rootLevel) {
+                        throw $e;
+                    } else {
+                        $value[$k] = null;
                     }
+                }
+            }
+
+            // Ensure numerically indexed arguments have sequential numeric keys.
+            if (false !== $i) {
+                $value = array_values($value);
+            }
+        } elseif ($value instanceof Reference) {
+            if ($this->container->has($value)) {
+                return $value;
+            }
+            $invalidBehavior = $value->getInvalidBehavior();
 
-                    $arguments[$k] = null;
+            // resolve invalid behavior
+            if (ContainerInterface::NULL_ON_INVALID_REFERENCE === $invalidBehavior) {
+                $value = null;
+            } elseif (ContainerInterface::IGNORE_ON_INVALID_REFERENCE === $invalidBehavior) {
+                if (0 < $level || $rootLevel) {
+                    throw $this->signalingException;
                 }
+                $value = null;
             }
         }
 
-        return $arguments;
+        return $value;
     }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveNamedArgumentsPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveNamedArgumentsPass.php
new file mode 100644
index 0000000000000000000000000000000000000000..225014f1e4224d795da2abfe85d1f0503231b766
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveNamedArgumentsPass.php
@@ -0,0 +1,102 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * Resolves named arguments to their corresponding numeric index.
+ *
+ * @author Kévin Dunglas <dunglas@gmail.com>
+ */
+class ResolveNamedArgumentsPass extends AbstractRecursivePass
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function processValue($value, $isRoot = false)
+    {
+        if (!$value instanceof Definition) {
+            return parent::processValue($value, $isRoot);
+        }
+
+        $calls = $value->getMethodCalls();
+        $calls[] = ['__construct', $value->getArguments()];
+
+        foreach ($calls as $i => $call) {
+            list($method, $arguments) = $call;
+            $parameters = null;
+            $resolvedArguments = [];
+
+            foreach ($arguments as $key => $argument) {
+                if (\is_int($key)) {
+                    $resolvedArguments[$key] = $argument;
+                    continue;
+                }
+
+                if (null === $parameters) {
+                    $r = $this->getReflectionMethod($value, $method);
+                    $class = $r instanceof \ReflectionMethod ? $r->class : $this->currentId;
+                    $method = $r->getName();
+                    $parameters = $r->getParameters();
+                }
+
+                if (isset($key[0]) && '$' === $key[0]) {
+                    foreach ($parameters as $j => $p) {
+                        if ($key === '$'.$p->name) {
+                            $resolvedArguments[$j] = $argument;
+
+                            continue 2;
+                        }
+                    }
+
+                    throw new InvalidArgumentException(sprintf('Invalid service "%s": method "%s()" has no argument named "%s". Check your service definition.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method, $key));
+                }
+
+                if (null !== $argument && !$argument instanceof Reference && !$argument instanceof Definition) {
+                    throw new InvalidArgumentException(sprintf('Invalid service "%s": the value of argument "%s" of method "%s()" must be null, an instance of "%s" or an instance of "%s", "%s" given.', $this->currentId, $key, $class !== $this->currentId ? $class.'::'.$method : $method, Reference::class, Definition::class, \gettype($argument)));
+                }
+
+                $typeFound = false;
+                foreach ($parameters as $j => $p) {
+                    if (!\array_key_exists($j, $resolvedArguments) && ProxyHelper::getTypeHint($r, $p, true) === $key) {
+                        $resolvedArguments[$j] = $argument;
+                        $typeFound = true;
+                    }
+                }
+
+                if (!$typeFound) {
+                    throw new InvalidArgumentException(sprintf('Invalid service "%s": method "%s()" has no argument type-hinted as "%s". Check your service definition.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method, $key));
+                }
+            }
+
+            if ($resolvedArguments !== $call[1]) {
+                ksort($resolvedArguments);
+                $calls[$i][1] = $resolvedArguments;
+            }
+        }
+
+        list(, $arguments) = array_pop($calls);
+
+        if ($arguments !== $value->getArguments()) {
+            $value->setArguments($arguments);
+        }
+        if ($calls !== $value->getMethodCalls()) {
+            $value->setMethodCalls($calls);
+        }
+
+        return parent::processValue($value, $isRoot);
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveParameterPlaceHoldersPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveParameterPlaceHoldersPass.php
index 3a31dd55f6751c2b3b4c822945ecf77727dac9eb..32eb6a3a76f3e37341d15cc31ede0cf8b022fdd3 100644
--- a/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveParameterPlaceHoldersPass.php
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveParameterPlaceHoldersPass.php
@@ -12,6 +12,7 @@
 namespace Symfony\Component\DependencyInjection\Compiler;
 
 use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
 use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
 
 /**
@@ -19,53 +20,79 @@ use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
  *
  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  */
-class ResolveParameterPlaceHoldersPass implements CompilerPassInterface
+class ResolveParameterPlaceHoldersPass extends AbstractRecursivePass
 {
+    private $bag;
+    private $resolveArrays;
+    private $throwOnResolveException;
+
+    public function __construct($resolveArrays = true, $throwOnResolveException = true)
+    {
+        $this->resolveArrays = $resolveArrays;
+        $this->throwOnResolveException = $throwOnResolveException;
+    }
+
     /**
-     * Processes the ContainerBuilder to resolve parameter placeholders.
+     * {@inheritdoc}
      *
      * @throws ParameterNotFoundException
      */
     public function process(ContainerBuilder $container)
     {
-        $parameterBag = $container->getParameterBag();
+        $this->bag = $container->getParameterBag();
 
-        foreach ($container->getDefinitions() as $id => $definition) {
-            try {
-                $definition->setClass($parameterBag->resolveValue($definition->getClass()));
-                $definition->setFile($parameterBag->resolveValue($definition->getFile()));
-                $definition->setArguments($parameterBag->resolveValue($definition->getArguments()));
-                if ($definition->getFactoryClass(false)) {
-                    $definition->setFactoryClass($parameterBag->resolveValue($definition->getFactoryClass(false)));
-                }
+        try {
+            parent::process($container);
 
-                $factory = $definition->getFactory();
+            $aliases = [];
+            foreach ($container->getAliases() as $name => $target) {
+                $this->currentId = $name;
+                $aliases[$this->bag->resolveValue($name)] = $target;
+            }
+            $container->setAliases($aliases);
+        } catch (ParameterNotFoundException $e) {
+            $e->setSourceId($this->currentId);
 
-                if (\is_array($factory) && isset($factory[0])) {
-                    $factory[0] = $parameterBag->resolveValue($factory[0]);
-                    $definition->setFactory($factory);
-                }
+            throw $e;
+        }
 
-                $calls = array();
-                foreach ($definition->getMethodCalls() as $name => $arguments) {
-                    $calls[$parameterBag->resolveValue($name)] = $parameterBag->resolveValue($arguments);
-                }
-                $definition->setMethodCalls($calls);
+        $this->bag->resolve();
+        $this->bag = null;
+    }
 
-                $definition->setProperties($parameterBag->resolveValue($definition->getProperties()));
+    protected function processValue($value, $isRoot = false)
+    {
+        if (\is_string($value)) {
+            try {
+                $v = $this->bag->resolveValue($value);
             } catch (ParameterNotFoundException $e) {
-                $e->setSourceId($id);
+                if ($this->throwOnResolveException) {
+                    throw $e;
+                }
 
-                throw $e;
+                $v = null;
+                $this->container->getDefinition($this->currentId)->addError($e->getMessage());
             }
+
+            return $this->resolveArrays || !$v || !\is_array($v) ? $v : $value;
         }
+        if ($value instanceof Definition) {
+            $value->setBindings($this->processValue($value->getBindings()));
+            $changes = $value->getChanges();
+            if (isset($changes['class'])) {
+                $value->setClass($this->bag->resolveValue($value->getClass()));
+            }
+            if (isset($changes['file'])) {
+                $value->setFile($this->bag->resolveValue($value->getFile()));
+            }
+        }
+
+        $value = parent::processValue($value, $isRoot);
 
-        $aliases = array();
-        foreach ($container->getAliases() as $name => $target) {
-            $aliases[$parameterBag->resolveValue($name)] = $target;
+        if ($value && \is_array($value)) {
+            $value = array_combine($this->bag->resolveValue(array_keys($value)), $value);
         }
-        $container->setAliases($aliases);
 
-        $parameterBag->resolve();
+        return $value;
     }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/ResolvePrivatesPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolvePrivatesPass.php
new file mode 100644
index 0000000000000000000000000000000000000000..1bd993458a40e494a94464996d86a5c672169109
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolvePrivatesPass.php
@@ -0,0 +1,40 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class ResolvePrivatesPass implements CompilerPassInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function process(ContainerBuilder $container)
+    {
+        foreach ($container->getDefinitions() as $id => $definition) {
+            if ($definition->isPrivate()) {
+                $definition->setPublic(false);
+                $definition->setPrivate(true);
+            }
+        }
+
+        foreach ($container->getAliases() as $id => $alias) {
+            if ($alias->isPrivate()) {
+                $alias->setPublic(false);
+                $alias->setPrivate(true);
+            }
+        }
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveReferencesToAliasesPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveReferencesToAliasesPass.php
index 8a1113751735eb76d67e0b5419b4c6e4d66cf3d4..2559dcf10c00e638a4a957a731cecfc536da288f 100644
--- a/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveReferencesToAliasesPass.php
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveReferencesToAliasesPass.php
@@ -11,7 +11,6 @@
 
 namespace Symfony\Component\DependencyInjection\Compiler;
 
-use Symfony\Component\DependencyInjection\Alias;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
 use Symfony\Component\DependencyInjection\Reference;
@@ -21,83 +20,37 @@ use Symfony\Component\DependencyInjection\Reference;
  *
  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  */
-class ResolveReferencesToAliasesPass implements CompilerPassInterface
+class ResolveReferencesToAliasesPass extends AbstractRecursivePass
 {
-    private $container;
-
     /**
-     * Processes the ContainerBuilder to replace references to aliases with actual service references.
+     * {@inheritdoc}
      */
     public function process(ContainerBuilder $container)
     {
-        $this->container = $container;
-
-        foreach ($container->getDefinitions() as $definition) {
-            if ($definition->isSynthetic() || $definition->isAbstract()) {
-                continue;
-            }
-
-            $definition->setArguments($this->processArguments($definition->getArguments()));
-            $definition->setMethodCalls($this->processArguments($definition->getMethodCalls()));
-            $definition->setProperties($this->processArguments($definition->getProperties()));
-            $definition->setFactory($this->processFactory($definition->getFactory()));
-            $definition->setFactoryService($this->processFactoryService($definition->getFactoryService(false)), false);
-        }
+        parent::process($container);
 
         foreach ($container->getAliases() as $id => $alias) {
-            $aliasId = (string) $alias;
-            if ($aliasId !== $defId = $this->getDefinitionId($aliasId)) {
-                $container->setAlias($id, new Alias($defId, $alias->isPublic()));
+            $aliasId = $container->normalizeId($alias);
+            if ($aliasId !== $defId = $this->getDefinitionId($aliasId, $container)) {
+                $container->setAlias($id, $defId)->setPublic($alias->isPublic())->setPrivate($alias->isPrivate());
             }
         }
     }
 
     /**
-     * Processes the arguments to replace aliases.
-     *
-     * @param array $arguments An array of References
-     *
-     * @return array An array of References
+     * {@inheritdoc}
      */
-    private function processArguments(array $arguments)
+    protected function processValue($value, $isRoot = false)
     {
-        foreach ($arguments as $k => $argument) {
-            if (\is_array($argument)) {
-                $arguments[$k] = $this->processArguments($argument);
-            } elseif ($argument instanceof Reference) {
-                $defId = $this->getDefinitionId($id = (string) $argument);
+        if ($value instanceof Reference) {
+            $defId = $this->getDefinitionId($id = $this->container->normalizeId($value), $this->container);
 
-                if ($defId !== $id) {
-                    $arguments[$k] = new Reference($defId, $argument->getInvalidBehavior(), $argument->isStrict(false));
-                }
+            if ($defId !== $id) {
+                return new Reference($defId, $value->getInvalidBehavior());
             }
         }
 
-        return $arguments;
-    }
-
-    private function processFactoryService($factoryService)
-    {
-        if (null === $factoryService) {
-            return;
-        }
-
-        return $this->getDefinitionId($factoryService);
-    }
-
-    private function processFactory($factory)
-    {
-        if (null === $factory || !\is_array($factory) || !$factory[0] instanceof Reference) {
-            return $factory;
-        }
-
-        $defId = $this->getDefinitionId($id = (string) $factory[0]);
-
-        if ($defId !== $id) {
-            $factory[0] = new Reference($defId, $factory[0]->getInvalidBehavior(), $factory[0]->isStrict(false));
-        }
-
-        return $factory;
+        return parent::processValue($value);
     }
 
     /**
@@ -107,15 +60,15 @@ class ResolveReferencesToAliasesPass implements CompilerPassInterface
      *
      * @return string The definition id with aliases resolved
      */
-    private function getDefinitionId($id)
+    private function getDefinitionId($id, ContainerBuilder $container)
     {
-        $seen = array();
-        while ($this->container->hasAlias($id)) {
+        $seen = [];
+        while ($container->hasAlias($id)) {
             if (isset($seen[$id])) {
-                throw new ServiceCircularReferenceException($id, array_keys($seen));
+                throw new ServiceCircularReferenceException($id, array_merge(array_keys($seen), [$id]));
             }
             $seen[$id] = true;
-            $id = (string) $this->container->getAlias($id);
+            $id = $container->normalizeId($container->getAlias($id));
         }
 
         return $id;
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveServiceSubscribersPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveServiceSubscribersPass.php
new file mode 100644
index 0000000000000000000000000000000000000000..ccc80a443e420ca550ff199d587b3afaaea9dce6
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveServiceSubscribersPass.php
@@ -0,0 +1,51 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Compiler;
+
+use Psr\Container\ContainerInterface;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * Compiler pass to inject their service locator to service subscribers.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class ResolveServiceSubscribersPass extends AbstractRecursivePass
+{
+    private $serviceLocator;
+
+    protected function processValue($value, $isRoot = false)
+    {
+        if ($value instanceof Reference && $this->serviceLocator && ContainerInterface::class === $this->container->normalizeId($value)) {
+            return new Reference($this->serviceLocator);
+        }
+
+        if (!$value instanceof Definition) {
+            return parent::processValue($value, $isRoot);
+        }
+
+        $serviceLocator = $this->serviceLocator;
+        $this->serviceLocator = null;
+
+        if ($value->hasTag('container.service_subscriber.locator')) {
+            $this->serviceLocator = $value->getTag('container.service_subscriber.locator')[0]['id'];
+            $value->clearTag('container.service_subscriber.locator');
+        }
+
+        try {
+            return parent::processValue($value);
+        } finally {
+            $this->serviceLocator = $serviceLocator;
+        }
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveTaggedIteratorArgumentPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveTaggedIteratorArgumentPass.php
new file mode 100644
index 0000000000000000000000000000000000000000..009cee9bf5c1dda8424bca6e00f74d54839916cf
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/ResolveTaggedIteratorArgumentPass.php
@@ -0,0 +1,38 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
+
+/**
+ * Resolves all TaggedIteratorArgument arguments.
+ *
+ * @author Roland Franssen <franssen.roland@gmail.com>
+ */
+class ResolveTaggedIteratorArgumentPass extends AbstractRecursivePass
+{
+    use PriorityTaggedServiceTrait;
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function processValue($value, $isRoot = false)
+    {
+        if (!$value instanceof TaggedIteratorArgument) {
+            return parent::processValue($value, $isRoot);
+        }
+
+        $value->setValues($this->findAndSortTaggedServices($value->getTag(), $this->container));
+
+        return $value;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/ServiceLocatorTagPass.php b/civicrm/vendor/symfony/dependency-injection/Compiler/ServiceLocatorTagPass.php
new file mode 100644
index 0000000000000000000000000000000000000000..a7427c5a5bd664c60b949df2158ee93919721408
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/ServiceLocatorTagPass.php
@@ -0,0 +1,126 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\Alias;
+use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\ServiceLocator;
+
+/**
+ * Applies the "container.service_locator" tag by wrapping references into ServiceClosureArgument instances.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+final class ServiceLocatorTagPass extends AbstractRecursivePass
+{
+    protected function processValue($value, $isRoot = false)
+    {
+        if (!$value instanceof Definition || !$value->hasTag('container.service_locator')) {
+            return parent::processValue($value, $isRoot);
+        }
+
+        if (!$value->getClass()) {
+            $value->setClass(ServiceLocator::class);
+        }
+
+        $arguments = $value->getArguments();
+        if (!isset($arguments[0]) || !\is_array($arguments[0])) {
+            throw new InvalidArgumentException(sprintf('Invalid definition for service "%s": an array of references is expected as first argument when the "container.service_locator" tag is set.', $this->currentId));
+        }
+
+        $i = 0;
+
+        foreach ($arguments[0] as $k => $v) {
+            if ($v instanceof ServiceClosureArgument) {
+                continue;
+            }
+            if (!$v instanceof Reference) {
+                throw new InvalidArgumentException(sprintf('Invalid definition for service "%s": an array of references is expected as first argument when the "container.service_locator" tag is set, "%s" found for key "%s".', $this->currentId, \is_object($v) ? \get_class($v) : \gettype($v), $k));
+            }
+
+            if ($i === $k) {
+                unset($arguments[0][$k]);
+
+                $k = (string) $v;
+                ++$i;
+            } elseif (\is_int($k)) {
+                $i = null;
+            }
+            $arguments[0][$k] = new ServiceClosureArgument($v);
+        }
+        ksort($arguments[0]);
+
+        $value->setArguments($arguments);
+
+        $id = 'service_locator.'.ContainerBuilder::hash($value);
+
+        if ($isRoot) {
+            if ($id !== $this->currentId) {
+                $this->container->setAlias($id, new Alias($this->currentId, false));
+            }
+
+            return $value;
+        }
+
+        $this->container->setDefinition($id, $value->setPublic(false));
+
+        return new Reference($id);
+    }
+
+    /**
+     * @param Reference[] $refMap
+     * @param string|null $callerId
+     *
+     * @return Reference
+     */
+    public static function register(ContainerBuilder $container, array $refMap, $callerId = null)
+    {
+        foreach ($refMap as $id => $ref) {
+            if (!$ref instanceof Reference) {
+                throw new InvalidArgumentException(sprintf('Invalid service locator definition: only services can be referenced, "%s" found for key "%s". Inject parameter values using constructors instead.', \is_object($ref) ? \get_class($ref) : \gettype($ref), $id));
+            }
+            $refMap[$id] = new ServiceClosureArgument($ref);
+        }
+        ksort($refMap);
+
+        $locator = (new Definition(ServiceLocator::class))
+            ->addArgument($refMap)
+            ->setPublic(false)
+            ->addTag('container.service_locator');
+
+        if (null !== $callerId && $container->hasDefinition($callerId)) {
+            $locator->setBindings($container->getDefinition($callerId)->getBindings());
+        }
+
+        if (!$container->hasDefinition($id = 'service_locator.'.ContainerBuilder::hash($locator))) {
+            $container->setDefinition($id, $locator);
+        }
+
+        if (null !== $callerId) {
+            $locatorId = $id;
+            // Locators are shared when they hold the exact same list of factories;
+            // to have them specialized per consumer service, we use a cloning factory
+            // to derivate customized instances from the prototype one.
+            $container->register($id .= '.'.$callerId, ServiceLocator::class)
+                ->setPublic(false)
+                ->setFactory([new Reference($locatorId), 'withContext'])
+                ->addArgument($callerId)
+                ->addArgument(new Reference('service_container'));
+        }
+
+        return new Reference($id);
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraph.php b/civicrm/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraph.php
index 43bac7120bcb606d5a6eb764ef1cfbec19deac3f..e419e297e8f1ef66f25582b20e33d0bf9bf41864 100644
--- a/civicrm/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraph.php
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraph.php
@@ -20,13 +20,15 @@ use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
  * it themselves which improves performance quite a lot.
  *
  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ *
+ * @final since version 3.4
  */
 class ServiceReferenceGraph
 {
     /**
      * @var ServiceReferenceGraphNode[]
      */
-    private $nodes = array();
+    private $nodes = [];
 
     /**
      * Checks if the graph has a specific node.
@@ -73,7 +75,10 @@ class ServiceReferenceGraph
      */
     public function clear()
     {
-        $this->nodes = array();
+        foreach ($this->nodes as $node) {
+            $node->clear();
+        }
+        $this->nodes = [];
     }
 
     /**
@@ -85,14 +90,19 @@ class ServiceReferenceGraph
      * @param mixed  $destValue
      * @param string $reference
      */
-    public function connect($sourceId, $sourceValue, $destId, $destValue = null, $reference = null)
+    public function connect($sourceId, $sourceValue, $destId, $destValue = null, $reference = null/*, bool $lazy = false, bool $weak = false, bool $byConstructor = false*/)
     {
+        $lazy = \func_num_args() >= 6 ? func_get_arg(5) : false;
+        $weak = \func_num_args() >= 7 ? func_get_arg(6) : false;
+        $byConstructor = \func_num_args() >= 8 ? func_get_arg(7) : false;
+
         if (null === $sourceId || null === $destId) {
             return;
         }
+
         $sourceNode = $this->createNode($sourceId, $sourceValue);
         $destNode = $this->createNode($destId, $destValue);
-        $edge = new ServiceReferenceGraphEdge($sourceNode, $destNode, $reference);
+        $edge = new ServiceReferenceGraphEdge($sourceNode, $destNode, $reference, $lazy, $weak, $byConstructor);
 
         $sourceNode->addOutEdge($edge);
         $destNode->addInEdge($edge);
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraphEdge.php b/civicrm/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraphEdge.php
index 7e8cf812f7082b499a57fa8a5fc8719d4aab4e7a..911e7a5f5facf19c992d8066ac799cfb447e2be8 100644
--- a/civicrm/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraphEdge.php
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraphEdge.php
@@ -23,17 +23,24 @@ class ServiceReferenceGraphEdge
     private $sourceNode;
     private $destNode;
     private $value;
+    private $lazy;
+    private $weak;
+    private $byConstructor;
 
     /**
-     * @param ServiceReferenceGraphNode $sourceNode
-     * @param ServiceReferenceGraphNode $destNode
-     * @param mixed                     $value
+     * @param mixed $value
+     * @param bool  $lazy
+     * @param bool  $weak
+     * @param bool  $byConstructor
      */
-    public function __construct(ServiceReferenceGraphNode $sourceNode, ServiceReferenceGraphNode $destNode, $value = null)
+    public function __construct(ServiceReferenceGraphNode $sourceNode, ServiceReferenceGraphNode $destNode, $value = null, $lazy = false, $weak = false, $byConstructor = false)
     {
         $this->sourceNode = $sourceNode;
         $this->destNode = $destNode;
         $this->value = $value;
+        $this->lazy = $lazy;
+        $this->weak = $weak;
+        $this->byConstructor = $byConstructor;
     }
 
     /**
@@ -65,4 +72,34 @@ class ServiceReferenceGraphEdge
     {
         return $this->destNode;
     }
+
+    /**
+     * Returns true if the edge is lazy, meaning it's a dependency not requiring direct instantiation.
+     *
+     * @return bool
+     */
+    public function isLazy()
+    {
+        return $this->lazy;
+    }
+
+    /**
+     * Returns true if the edge is weak, meaning it shouldn't prevent removing the target service.
+     *
+     * @return bool
+     */
+    public function isWeak()
+    {
+        return $this->weak;
+    }
+
+    /**
+     * Returns true if the edge links with a constructor argument.
+     *
+     * @return bool
+     */
+    public function isReferencedByConstructor()
+    {
+        return $this->byConstructor;
+    }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraphNode.php b/civicrm/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraphNode.php
index 6a6d42139087a76b5642198ed7d0997fb69d1dab..50140b5fffd678d11377793ec23be63a5f5173f9 100644
--- a/civicrm/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraphNode.php
+++ b/civicrm/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraphNode.php
@@ -24,8 +24,8 @@ use Symfony\Component\DependencyInjection\Definition;
 class ServiceReferenceGraphNode
 {
     private $id;
-    private $inEdges = array();
-    private $outEdges = array();
+    private $inEdges = [];
+    private $outEdges = [];
     private $value;
 
     /**
@@ -81,7 +81,7 @@ class ServiceReferenceGraphNode
     /**
      * Returns the in edges.
      *
-     * @return array The in ServiceReferenceGraphEdge array
+     * @return ServiceReferenceGraphEdge[]
      */
     public function getInEdges()
     {
@@ -91,7 +91,7 @@ class ServiceReferenceGraphNode
     /**
      * Returns the out edges.
      *
-     * @return array The out ServiceReferenceGraphEdge array
+     * @return ServiceReferenceGraphEdge[]
      */
     public function getOutEdges()
     {
@@ -107,4 +107,12 @@ class ServiceReferenceGraphNode
     {
         return $this->value;
     }
+
+    /**
+     * Clears all edges.
+     */
+    public function clear()
+    {
+        $this->inEdges = $this->outEdges = [];
+    }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Config/AutowireServiceResource.php b/civicrm/vendor/symfony/dependency-injection/Config/AutowireServiceResource.php
new file mode 100644
index 0000000000000000000000000000000000000000..0c3d8f5758e46e034c833ef4eb5b127045a3fe44
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Config/AutowireServiceResource.php
@@ -0,0 +1,88 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Config;
+
+@trigger_error('The '.__NAMESPACE__.'\AutowireServiceResource class is deprecated since Symfony 3.3 and will be removed in 4.0. Use ContainerBuilder::getReflectionClass() instead.', E_USER_DEPRECATED);
+
+use Symfony\Component\Config\Resource\SelfCheckingResourceInterface;
+use Symfony\Component\DependencyInjection\Compiler\AutowirePass;
+
+/**
+ * @deprecated since version 3.3, to be removed in 4.0. Use ContainerBuilder::getReflectionClass() instead.
+ */
+class AutowireServiceResource implements SelfCheckingResourceInterface, \Serializable
+{
+    private $class;
+    private $filePath;
+    private $autowiringMetadata = [];
+
+    public function __construct($class, $path, array $autowiringMetadata)
+    {
+        $this->class = $class;
+        $this->filePath = $path;
+        $this->autowiringMetadata = $autowiringMetadata;
+    }
+
+    public function isFresh($timestamp)
+    {
+        if (!file_exists($this->filePath)) {
+            return false;
+        }
+
+        // has the file *not* been modified? Definitely fresh
+        if (@filemtime($this->filePath) <= $timestamp) {
+            return true;
+        }
+
+        try {
+            $reflectionClass = new \ReflectionClass($this->class);
+        } catch (\ReflectionException $e) {
+            // the class does not exist anymore!
+            return false;
+        }
+
+        return (array) $this === (array) AutowirePass::createResourceForClass($reflectionClass);
+    }
+
+    public function __toString()
+    {
+        return 'service.autowire.'.$this->class;
+    }
+
+    /**
+     * @internal
+     */
+    public function serialize()
+    {
+        return serialize([$this->class, $this->filePath, $this->autowiringMetadata]);
+    }
+
+    /**
+     * @internal
+     */
+    public function unserialize($serialized)
+    {
+        if (\PHP_VERSION_ID >= 70000) {
+            list($this->class, $this->filePath, $this->autowiringMetadata) = unserialize($serialized, ['allowed_classes' => false]);
+        } else {
+            list($this->class, $this->filePath, $this->autowiringMetadata) = unserialize($serialized);
+        }
+    }
+
+    /**
+     * @deprecated Implemented for compatibility with Symfony 2.8
+     */
+    public function getResource()
+    {
+        return $this->filePath;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Config/ContainerParametersResource.php b/civicrm/vendor/symfony/dependency-injection/Config/ContainerParametersResource.php
new file mode 100644
index 0000000000000000000000000000000000000000..7560c3356d978ebead02b361ea412f6b1da92c11
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Config/ContainerParametersResource.php
@@ -0,0 +1,64 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Config;
+
+use Symfony\Component\Config\Resource\ResourceInterface;
+
+/**
+ * Tracks container parameters.
+ *
+ * @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
+ */
+class ContainerParametersResource implements ResourceInterface, \Serializable
+{
+    private $parameters;
+
+    /**
+     * @param array $parameters The container parameters to track
+     */
+    public function __construct(array $parameters)
+    {
+        $this->parameters = $parameters;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __toString()
+    {
+        return 'container_parameters_'.md5(serialize($this->parameters));
+    }
+
+    /**
+     * @internal
+     */
+    public function serialize()
+    {
+        return serialize($this->parameters);
+    }
+
+    /**
+     * @internal
+     */
+    public function unserialize($serialized)
+    {
+        $this->parameters = unserialize($serialized);
+    }
+
+    /**
+     * @return array Tracked parameters
+     */
+    public function getParameters()
+    {
+        return $this->parameters;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Config/ContainerParametersResourceChecker.php b/civicrm/vendor/symfony/dependency-injection/Config/ContainerParametersResourceChecker.php
new file mode 100644
index 0000000000000000000000000000000000000000..6ed77e3fd2c48e7e524faa4851c110b6d7dad587
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Config/ContainerParametersResourceChecker.php
@@ -0,0 +1,52 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Config;
+
+use Symfony\Component\Config\Resource\ResourceInterface;
+use Symfony\Component\Config\ResourceCheckerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
+ */
+class ContainerParametersResourceChecker implements ResourceCheckerInterface
+{
+    /** @var ContainerInterface */
+    private $container;
+
+    public function __construct(ContainerInterface $container)
+    {
+        $this->container = $container;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function supports(ResourceInterface $metadata)
+    {
+        return $metadata instanceof ContainerParametersResource;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isFresh(ResourceInterface $resource, $timestamp)
+    {
+        foreach ($resource->getParameters() as $key => $value) {
+            if (!$this->container->hasParameter($key) || $this->container->getParameter($key) !== $value) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Container.php b/civicrm/vendor/symfony/dependency-injection/Container.php
index 25532ead233d5b954cf9c1e9a31a8b157a672fc7..b9f44b0bf9d5b6cb6822d96c6c62e2c2deb2d779 100644
--- a/civicrm/vendor/symfony/dependency-injection/Container.php
+++ b/civicrm/vendor/symfony/dependency-injection/Container.php
@@ -11,59 +11,61 @@
 
 namespace Symfony\Component\DependencyInjection;
 
-use Symfony\Component\DependencyInjection\Exception\InactiveScopeException;
+use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException;
 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
-use Symfony\Component\DependencyInjection\Exception\LogicException;
-use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException;
 use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
 use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
+use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
 use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
-use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
 use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
 
 /**
  * Container is a dependency injection container.
  *
  * It gives access to object instances (services).
- *
  * Services and parameters are simple key/pair stores.
- *
- * Parameter and service keys are case insensitive.
- *
- * A service can also be defined by creating a method named
- * getXXXService(), where XXX is the camelized version of the id:
- *
- *  * request -> getRequestService()
- *  * mysql_session_storage -> getMysqlSessionStorageService()
- *  * symfony.mysql_session_storage -> getSymfony_MysqlSessionStorageService()
- *
- * The container can have three possible behaviors when a service does not exist:
+ * The container can have four possible behaviors when a service
+ * does not exist (or is not initialized for the last case):
  *
  *  * EXCEPTION_ON_INVALID_REFERENCE: Throws an exception (the default)
  *  * NULL_ON_INVALID_REFERENCE:      Returns null
  *  * IGNORE_ON_INVALID_REFERENCE:    Ignores the wrapping command asking for the reference
  *                                    (for instance, ignore a setter if the service does not exist)
+ *  * IGNORE_ON_UNINITIALIZED_REFERENCE: Ignores/returns null for uninitialized services or invalid references
  *
  * @author Fabien Potencier <fabien@symfony.com>
  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  */
-class Container implements IntrospectableContainerInterface, ResettableContainerInterface
+class Container implements ResettableContainerInterface
 {
     protected $parameterBag;
-    protected $services = array();
-    protected $methodMap = array();
-    protected $aliases = array();
-    protected $scopes = array();
-    protected $scopeChildren = array();
-    protected $scopedServices = array();
-    protected $scopeStacks = array();
-    protected $loading = array();
+    protected $services = [];
+    protected $fileMap = [];
+    protected $methodMap = [];
+    protected $aliases = [];
+    protected $loading = [];
+    protected $resolving = [];
+    protected $syntheticIds = [];
+
+    /**
+     * @internal
+     */
+    protected $privates = [];
+
+    /**
+     * @internal
+     */
+    protected $normalizedIds = [];
 
-    private $underscoreMap = array('_' => '', '.' => '_', '\\' => '_');
+    private $underscoreMap = ['_' => '', '.' => '_', '\\' => '_'];
+    private $envCache = [];
+    private $compiled = false;
+    private $getEnv;
 
     public function __construct(ParameterBagInterface $parameterBag = null)
     {
-        $this->parameterBag = $parameterBag ?: new ParameterBag();
+        $this->parameterBag = $parameterBag ?: new EnvPlaceholderParameterBag();
     }
 
     /**
@@ -79,15 +81,31 @@ class Container implements IntrospectableContainerInterface, ResettableContainer
         $this->parameterBag->resolve();
 
         $this->parameterBag = new FrozenParameterBag($this->parameterBag->all());
+
+        $this->compiled = true;
+    }
+
+    /**
+     * Returns true if the container is compiled.
+     *
+     * @return bool
+     */
+    public function isCompiled()
+    {
+        return $this->compiled;
     }
 
     /**
      * Returns true if the container parameter bag are frozen.
      *
+     * @deprecated since version 3.3, to be removed in 4.0.
+     *
      * @return bool true if the container parameter bag are frozen, false otherwise
      */
     public function isFrozen()
     {
+        @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED);
+
         return $this->parameterBag instanceof FrozenParameterBag;
     }
 
@@ -141,61 +159,55 @@ class Container implements IntrospectableContainerInterface, ResettableContainer
     /**
      * Sets a service.
      *
-     * Setting a service to null resets the service: has() returns false and get()
+     * Setting a synthetic service to null resets it: has() returns false and get()
      * behaves in the same way as if the service was never created.
      *
-     * Note: The $scope parameter is deprecated since version 2.8 and will be removed in 3.0.
-     *
-     * @param string $id      The service identifier
-     * @param object $service The service instance
-     * @param string $scope   The scope of the service
-     *
-     * @throws RuntimeException         When trying to set a service in an inactive scope
-     * @throws InvalidArgumentException When trying to set a service in the prototype scope
+     * @param string      $id      The service identifier
+     * @param object|null $service The service instance
      */
-    public function set($id, $service, $scope = self::SCOPE_CONTAINER)
+    public function set($id, $service)
     {
-        if (!\in_array($scope, array('container', 'request')) || ('request' === $scope && 'request' !== $id)) {
-            @trigger_error('The concept of container scopes is deprecated since Symfony 2.8 and will be removed in 3.0. Omit the third parameter.', E_USER_DEPRECATED);
-        }
-
-        if (self::SCOPE_PROTOTYPE === $scope) {
-            throw new InvalidArgumentException(sprintf('You cannot set service "%s" of scope "prototype".', $id));
+        // Runs the internal initializer; used by the dumped container to include always-needed files
+        if (isset($this->privates['service_container']) && $this->privates['service_container'] instanceof \Closure) {
+            $initialize = $this->privates['service_container'];
+            unset($this->privates['service_container']);
+            $initialize();
         }
 
-        $id = strtolower($id);
+        $id = $this->normalizeId($id);
 
         if ('service_container' === $id) {
-            // BC: 'service_container' is no longer a self-reference but always
-            // $this, so ignore this call.
-            // @todo Throw InvalidArgumentException in next major release.
-            return;
+            throw new InvalidArgumentException('You cannot set service "service_container".');
         }
-        if (self::SCOPE_CONTAINER !== $scope) {
-            if (!isset($this->scopedServices[$scope])) {
-                throw new RuntimeException(sprintf('You cannot set service "%s" of inactive scope.', $id));
-            }
 
-            $this->scopedServices[$scope][$id] = $service;
+        if (isset($this->privates[$id]) || !(isset($this->fileMap[$id]) || isset($this->methodMap[$id]))) {
+            if (!isset($this->privates[$id]) && !isset($this->getRemovedIds()[$id])) {
+                // no-op
+            } elseif (null === $service) {
+                @trigger_error(sprintf('The "%s" service is private, unsetting it is deprecated since Symfony 3.2 and will fail in 4.0.', $id), E_USER_DEPRECATED);
+                unset($this->privates[$id]);
+            } else {
+                @trigger_error(sprintf('The "%s" service is private, replacing it is deprecated since Symfony 3.2 and will fail in 4.0.', $id), E_USER_DEPRECATED);
+            }
+        } elseif (isset($this->services[$id])) {
+            if (null === $service) {
+                @trigger_error(sprintf('The "%s" service is already initialized, unsetting it is deprecated since Symfony 3.3 and will fail in 4.0.', $id), E_USER_DEPRECATED);
+            } else {
+                @trigger_error(sprintf('The "%s" service is already initialized, replacing it is deprecated since Symfony 3.3 and will fail in 4.0.', $id), E_USER_DEPRECATED);
+            }
         }
 
         if (isset($this->aliases[$id])) {
             unset($this->aliases[$id]);
         }
 
-        $this->services[$id] = $service;
-
-        if (method_exists($this, $method = 'synchronize'.strtr($id, $this->underscoreMap).'Service')) {
-            $this->$method();
-        }
-
         if (null === $service) {
-            if (self::SCOPE_CONTAINER !== $scope) {
-                unset($this->scopedServices[$scope][$id]);
-            }
-
             unset($this->services[$id]);
+
+            return;
         }
+
+        $this->services[$id] = $service;
     }
 
     /**
@@ -208,18 +220,37 @@ class Container implements IntrospectableContainerInterface, ResettableContainer
     public function has($id)
     {
         for ($i = 2;;) {
-            if ('service_container' === $id
-                || isset($this->aliases[$id])
-                || isset($this->services[$id])
-                || array_key_exists($id, $this->services)
-            ) {
+            if (isset($this->privates[$id])) {
+                @trigger_error(sprintf('The "%s" service is private, checking for its existence is deprecated since Symfony 3.2 and will fail in 4.0.', $id), E_USER_DEPRECATED);
+            }
+            if (isset($this->aliases[$id])) {
+                $id = $this->aliases[$id];
+            }
+            if (isset($this->services[$id])) {
                 return true;
             }
-            if (--$i && $id !== $lcId = strtolower($id)) {
-                $id = $lcId;
-            } else {
-                return method_exists($this, 'get'.strtr($id, $this->underscoreMap).'Service');
+            if ('service_container' === $id) {
+                return true;
             }
+
+            if (isset($this->fileMap[$id]) || isset($this->methodMap[$id])) {
+                return true;
+            }
+
+            if (--$i && $id !== $normalizedId = $this->normalizeId($id)) {
+                $id = $normalizedId;
+                continue;
+            }
+
+            // We only check the convention-based factory in a compiled container (i.e. a child class other than a ContainerBuilder,
+            // and only when the dumper has not generated the method map (otherwise the method map is considered to be fully populated by the dumper)
+            if (!$this->methodMap && !$this instanceof ContainerBuilder && __CLASS__ !== static::class && method_exists($this, 'get'.strtr($id, $this->underscoreMap).'Service')) {
+                @trigger_error('Generating a dumped container without populating the method map is deprecated since Symfony 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.', E_USER_DEPRECATED);
+
+                return true;
+            }
+
+            return false;
         }
     }
 
@@ -232,7 +263,7 @@ class Container implements IntrospectableContainerInterface, ResettableContainer
      * @param string $id              The service identifier
      * @param int    $invalidBehavior The behavior when the service does not exist
      *
-     * @return object The associated service
+     * @return object|null The associated service
      *
      * @throws ServiceCircularReferenceException When a circular reference is detected
      * @throws ServiceNotFoundException          When the service is not defined
@@ -240,18 +271,22 @@ class Container implements IntrospectableContainerInterface, ResettableContainer
      *
      * @see Reference
      */
-    public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE)
+    public function get($id, $invalidBehavior = /* self::EXCEPTION_ON_INVALID_REFERENCE */ 1)
     {
         // Attempt to retrieve the service by checking first aliases then
         // available services. Service IDs are case insensitive, however since
         // this method can be called thousands of times during a request, avoid
-        // calling strtolower() unless necessary.
+        // calling $this->normalizeId($id) unless necessary.
         for ($i = 2;;) {
+            if (isset($this->privates[$id])) {
+                @trigger_error(sprintf('The "%s" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead.', $id), E_USER_DEPRECATED);
+            }
             if (isset($this->aliases[$id])) {
                 $id = $this->aliases[$id];
             }
+
             // Re-use shared service instance if it exists.
-            if (isset($this->services[$id]) || array_key_exists($id, $this->services)) {
+            if (isset($this->services[$id])) {
                 return $this->services[$id];
             }
             if ('service_container' === $id) {
@@ -259,59 +294,58 @@ class Container implements IntrospectableContainerInterface, ResettableContainer
             }
 
             if (isset($this->loading[$id])) {
-                throw new ServiceCircularReferenceException($id, array_keys($this->loading));
-            }
-
-            if (isset($this->methodMap[$id])) {
-                $method = $this->methodMap[$id];
-            } elseif (--$i && $id !== $lcId = strtolower($id)) {
-                $id = $lcId;
-                continue;
-            } elseif (method_exists($this, $method = 'get'.strtr($id, $this->underscoreMap).'Service')) {
-                // $method is set to the right value, proceed
-            } else {
-                if (self::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) {
-                    if (!$id) {
-                        throw new ServiceNotFoundException($id);
-                    }
-
-                    $alternatives = array();
-                    foreach ($this->getServiceIds() as $knownId) {
-                        $lev = levenshtein($id, $knownId);
-                        if ($lev <= \strlen($id) / 3 || false !== strpos($knownId, $id)) {
-                            $alternatives[] = $knownId;
-                        }
-                    }
-
-                    throw new ServiceNotFoundException($id, null, null, $alternatives);
-                }
-
-                return;
+                throw new ServiceCircularReferenceException($id, array_merge(array_keys($this->loading), [$id]));
             }
 
             $this->loading[$id] = true;
 
             try {
-                $service = $this->$method();
+                if (isset($this->fileMap[$id])) {
+                    return /* self::IGNORE_ON_UNINITIALIZED_REFERENCE */ 4 === $invalidBehavior ? null : $this->load($this->fileMap[$id]);
+                } elseif (isset($this->methodMap[$id])) {
+                    return /* self::IGNORE_ON_UNINITIALIZED_REFERENCE */ 4 === $invalidBehavior ? null : $this->{$this->methodMap[$id]}();
+                } elseif (--$i && $id !== $normalizedId = $this->normalizeId($id)) {
+                    unset($this->loading[$id]);
+                    $id = $normalizedId;
+                    continue;
+                } elseif (!$this->methodMap && !$this instanceof ContainerBuilder && __CLASS__ !== static::class && method_exists($this, $method = 'get'.strtr($id, $this->underscoreMap).'Service')) {
+                    // We only check the convention-based factory in a compiled container (i.e. a child class other than a ContainerBuilder,
+                    // and only when the dumper has not generated the method map (otherwise the method map is considered to be fully populated by the dumper)
+                    @trigger_error('Generating a dumped container without populating the method map is deprecated since Symfony 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.', E_USER_DEPRECATED);
+
+                    return /* self::IGNORE_ON_UNINITIALIZED_REFERENCE */ 4 === $invalidBehavior ? null : $this->{$method}();
+                }
+
+                break;
             } catch (\Exception $e) {
-                unset($this->loading[$id]);
                 unset($this->services[$id]);
 
-                if ($e instanceof InactiveScopeException && self::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) {
-                    return;
-                }
-
                 throw $e;
-            } catch (\Throwable $e) {
+            } finally {
                 unset($this->loading[$id]);
-                unset($this->services[$id]);
+            }
+        }
 
-                throw $e;
+        if (/* self::EXCEPTION_ON_INVALID_REFERENCE */ 1 === $invalidBehavior) {
+            if (!$id) {
+                throw new ServiceNotFoundException($id);
+            }
+            if (isset($this->syntheticIds[$id])) {
+                throw new ServiceNotFoundException($id, null, null, [], sprintf('The "%s" service is synthetic, it needs to be set at boot time before it can be used.', $id));
+            }
+            if (isset($this->getRemovedIds()[$id])) {
+                throw new ServiceNotFoundException($id, null, null, [], sprintf('The "%s" service or alias has been removed or inlined when the container was compiled. You should either make it public, or stop using the container directly and use dependency injection instead.', $id));
             }
 
-            unset($this->loading[$id]);
+            $alternatives = [];
+            foreach ($this->getServiceIds() as $knownId) {
+                $lev = levenshtein($id, $knownId);
+                if ($lev <= \strlen($id) / 3 || false !== strpos($knownId, $id)) {
+                    $alternatives[] = $knownId;
+                }
+            }
 
-            return $service;
+            throw new ServiceNotFoundException($id, null, null, $alternatives);
         }
     }
 
@@ -324,19 +358,21 @@ class Container implements IntrospectableContainerInterface, ResettableContainer
      */
     public function initialized($id)
     {
-        $id = strtolower($id);
+        $id = $this->normalizeId($id);
+
+        if (isset($this->privates[$id])) {
+            @trigger_error(sprintf('Checking for the initialization of the "%s" private service is deprecated since Symfony 3.4 and won\'t be supported anymore in Symfony 4.0.', $id), E_USER_DEPRECATED);
+        }
 
         if (isset($this->aliases[$id])) {
             $id = $this->aliases[$id];
         }
 
         if ('service_container' === $id) {
-            // BC: 'service_container' was a synthetic service previously.
-            // @todo Change to false in next major release.
-            return true;
+            return false;
         }
 
-        return isset($this->services[$id]) || array_key_exists($id, $this->services);
+        return isset($this->services[$id]);
     }
 
     /**
@@ -344,228 +380,144 @@ class Container implements IntrospectableContainerInterface, ResettableContainer
      */
     public function reset()
     {
-        if (!empty($this->scopedServices)) {
-            throw new LogicException('Resetting the container is not allowed when a scope is active.');
-        }
-
-        $this->services = array();
+        $this->services = [];
     }
 
     /**
      * Gets all service ids.
      *
-     * @return array An array of all defined service ids
+     * @return string[] An array of all defined service ids
      */
     public function getServiceIds()
     {
-        $ids = array();
-        foreach (get_class_methods($this) as $method) {
-            if (preg_match('/^get(.+)Service$/', $method, $match)) {
-                $ids[] = self::underscore($match[1]);
+        $ids = [];
+
+        if (!$this->methodMap && !$this instanceof ContainerBuilder && __CLASS__ !== static::class) {
+            // We only check the convention-based factory in a compiled container (i.e. a child class other than a ContainerBuilder,
+            // and only when the dumper has not generated the method map (otherwise the method map is considered to be fully populated by the dumper)
+            @trigger_error('Generating a dumped container without populating the method map is deprecated since Symfony 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.', E_USER_DEPRECATED);
+
+            foreach (get_class_methods($this) as $method) {
+                if (preg_match('/^get(.+)Service$/', $method, $match)) {
+                    $ids[] = self::underscore($match[1]);
+                }
             }
         }
         $ids[] = 'service_container';
 
-        return array_unique(array_merge($ids, array_keys($this->services)));
+        return array_map('strval', array_unique(array_merge($ids, array_keys($this->methodMap), array_keys($this->fileMap), array_keys($this->aliases), array_keys($this->services))));
     }
 
     /**
-     * This is called when you enter a scope.
-     *
-     * @param string $name
-     *
-     * @throws RuntimeException         When the parent scope is inactive
-     * @throws InvalidArgumentException When the scope does not exist
+     * Gets service ids that existed at compile time.
      *
-     * @deprecated since version 2.8, to be removed in 3.0.
+     * @return array
      */
-    public function enterScope($name)
+    public function getRemovedIds()
     {
-        if ('request' !== $name) {
-            @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-        }
-
-        if (!isset($this->scopes[$name])) {
-            throw new InvalidArgumentException(sprintf('The scope "%s" does not exist.', $name));
-        }
-
-        if (self::SCOPE_CONTAINER !== $this->scopes[$name] && !isset($this->scopedServices[$this->scopes[$name]])) {
-            throw new RuntimeException(sprintf('The parent scope "%s" must be active when entering this scope.', $this->scopes[$name]));
-        }
-
-        // check if a scope of this name is already active, if so we need to
-        // remove all services of this scope, and those of any of its child
-        // scopes from the global services map
-        if (isset($this->scopedServices[$name])) {
-            $services = array($this->services, $name => $this->scopedServices[$name]);
-            unset($this->scopedServices[$name]);
-
-            foreach ($this->scopeChildren[$name] as $child) {
-                if (isset($this->scopedServices[$child])) {
-                    $services[$child] = $this->scopedServices[$child];
-                    unset($this->scopedServices[$child]);
-                }
-            }
-
-            // update global map
-            $this->services = \call_user_func_array('array_diff_key', $services);
-            array_shift($services);
-
-            // add stack entry for this scope so we can restore the removed services later
-            if (!isset($this->scopeStacks[$name])) {
-                $this->scopeStacks[$name] = new \SplStack();
-            }
-            $this->scopeStacks[$name]->push($services);
-        }
-
-        $this->scopedServices[$name] = array();
+        return [];
     }
 
     /**
-     * This is called to leave the current scope, and move back to the parent
-     * scope.
-     *
-     * @param string $name The name of the scope to leave
+     * Camelizes a string.
      *
-     * @throws InvalidArgumentException if the scope is not active
+     * @param string $id A string to camelize
      *
-     * @deprecated since version 2.8, to be removed in 3.0.
+     * @return string The camelized string
      */
-    public function leaveScope($name)
+    public static function camelize($id)
     {
-        if ('request' !== $name) {
-            @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-        }
-
-        if (!isset($this->scopedServices[$name])) {
-            throw new InvalidArgumentException(sprintf('The scope "%s" is not active.', $name));
-        }
-
-        // remove all services of this scope, or any of its child scopes from
-        // the global service map
-        $services = array($this->services, $this->scopedServices[$name]);
-        unset($this->scopedServices[$name]);
-
-        foreach ($this->scopeChildren[$name] as $child) {
-            if (isset($this->scopedServices[$child])) {
-                $services[] = $this->scopedServices[$child];
-                unset($this->scopedServices[$child]);
-            }
-        }
-
-        // update global map
-        $this->services = \call_user_func_array('array_diff_key', $services);
-
-        // check if we need to restore services of a previous scope of this type
-        if (isset($this->scopeStacks[$name]) && \count($this->scopeStacks[$name]) > 0) {
-            $services = $this->scopeStacks[$name]->pop();
-            $this->scopedServices += $services;
-
-            if ($this->scopeStacks[$name]->isEmpty()) {
-                unset($this->scopeStacks[$name]);
-            }
-
-            foreach ($services as $array) {
-                foreach ($array as $id => $service) {
-                    $this->set($id, $service, $name);
-                }
-            }
-        }
+        return strtr(ucwords(strtr($id, ['_' => ' ', '.' => '_ ', '\\' => '_ '])), [' ' => '']);
     }
 
     /**
-     * Adds a scope to the container.
+     * A string to underscore.
      *
-     * @throws InvalidArgumentException
+     * @param string $id The string to underscore
      *
-     * @deprecated since version 2.8, to be removed in 3.0.
+     * @return string The underscored string
      */
-    public function addScope(ScopeInterface $scope)
+    public static function underscore($id)
     {
-        $name = $scope->getName();
-        $parentScope = $scope->getParentName();
-
-        if ('request' !== $name) {
-            @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-        }
-        if (self::SCOPE_CONTAINER === $name || self::SCOPE_PROTOTYPE === $name) {
-            throw new InvalidArgumentException(sprintf('The scope "%s" is reserved.', $name));
-        }
-        if (isset($this->scopes[$name])) {
-            throw new InvalidArgumentException(sprintf('A scope with name "%s" already exists.', $name));
-        }
-        if (self::SCOPE_CONTAINER !== $parentScope && !isset($this->scopes[$parentScope])) {
-            throw new InvalidArgumentException(sprintf('The parent scope "%s" does not exist, or is invalid.', $parentScope));
-        }
-
-        $this->scopes[$name] = $parentScope;
-        $this->scopeChildren[$name] = array();
-
-        // normalize the child relations
-        while (self::SCOPE_CONTAINER !== $parentScope) {
-            $this->scopeChildren[$parentScope][] = $name;
-            $parentScope = $this->scopes[$parentScope];
-        }
+        return strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'], ['\\1_\\2', '\\1_\\2'], str_replace('_', '.', $id)));
     }
 
     /**
-     * Returns whether this container has a certain scope.
-     *
-     * @param string $name The name of the scope
-     *
-     * @return bool
-     *
-     * @deprecated since version 2.8, to be removed in 3.0.
+     * Creates a service by requiring its factory file.
      */
-    public function hasScope($name)
+    protected function load($file)
     {
-        if ('request' !== $name) {
-            @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-        }
-
-        return isset($this->scopes[$name]);
+        return require $file;
     }
 
     /**
-     * Returns whether this scope is currently active.
-     *
-     * This does not actually check if the passed scope actually exists.
+     * Fetches a variable from the environment.
      *
-     * @param string $name
+     * @param string $name The name of the environment variable
      *
-     * @return bool
+     * @return mixed The value to use for the provided environment variable name
      *
-     * @deprecated since version 2.8, to be removed in 3.0.
+     * @throws EnvNotFoundException When the environment variable is not found and has no default value
      */
-    public function isScopeActive($name)
+    protected function getEnv($name)
     {
-        @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
+        if (isset($this->resolving[$envName = "env($name)"])) {
+            throw new ParameterCircularReferenceException(array_keys($this->resolving));
+        }
+        if (isset($this->envCache[$name]) || \array_key_exists($name, $this->envCache)) {
+            return $this->envCache[$name];
+        }
+        if (!$this->has($id = 'container.env_var_processors_locator')) {
+            $this->set($id, new ServiceLocator([]));
+        }
+        if (!$this->getEnv) {
+            $this->getEnv = new \ReflectionMethod($this, __FUNCTION__);
+            $this->getEnv->setAccessible(true);
+            $this->getEnv = $this->getEnv->getClosure($this);
+        }
+        $processors = $this->get($id);
+
+        if (false !== $i = strpos($name, ':')) {
+            $prefix = substr($name, 0, $i);
+            $localName = substr($name, 1 + $i);
+        } else {
+            $prefix = 'string';
+            $localName = $name;
+        }
+        $processor = $processors->has($prefix) ? $processors->get($prefix) : new EnvVarProcessor($this);
 
-        return isset($this->scopedServices[$name]);
+        $this->resolving[$envName] = true;
+        try {
+            return $this->envCache[$name] = $processor->getEnv($prefix, $localName, $this->getEnv);
+        } finally {
+            unset($this->resolving[$envName]);
+        }
     }
 
     /**
-     * Camelizes a string.
-     *
-     * @param string $id A string to camelize
+     * Returns the case sensitive id used at registration time.
      *
-     * @return string The camelized string
-     */
-    public static function camelize($id)
-    {
-        return strtr(ucwords(strtr($id, array('_' => ' ', '.' => '_ ', '\\' => '_ '))), array(' ' => ''));
-    }
-
-    /**
-     * A string to underscore.
+     * @param string $id
      *
-     * @param string $id The string to underscore
+     * @return string
      *
-     * @return string The underscored string
+     * @internal
      */
-    public static function underscore($id)
+    public function normalizeId($id)
     {
-        return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), str_replace('_', '.', $id)));
+        if (!\is_string($id)) {
+            $id = (string) $id;
+        }
+        if (isset($this->normalizedIds[$normalizedId = strtolower($id)])) {
+            $normalizedId = $this->normalizedIds[$normalizedId];
+            if ($id !== $normalizedId) {
+                @trigger_error(sprintf('Service identifiers will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since Symfony 3.3.', $id, $normalizedId), E_USER_DEPRECATED);
+            }
+        } else {
+            $normalizedId = $this->normalizedIds[$normalizedId] = $id;
+        }
+
+        return $normalizedId;
     }
 
     private function __clone()
diff --git a/civicrm/vendor/symfony/dependency-injection/ContainerAware.php b/civicrm/vendor/symfony/dependency-injection/ContainerAware.php
deleted file mode 100644
index f3f2a5065c3110fed10effde25432e8acf75449a..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/dependency-injection/ContainerAware.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\DependencyInjection;
-
-/**
- * A simple implementation of ContainerAwareInterface.
- *
- * @author Fabien Potencier <fabien@symfony.com>
- *
- * @deprecated since version 2.8, to be removed in 3.0. Use the ContainerAwareTrait instead.
- */
-abstract class ContainerAware implements ContainerAwareInterface
-{
-    /**
-     * @var ContainerInterface
-     */
-    protected $container;
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setContainer(ContainerInterface $container = null)
-    {
-        $this->container = $container;
-    }
-}
diff --git a/civicrm/vendor/symfony/dependency-injection/ContainerBuilder.php b/civicrm/vendor/symfony/dependency-injection/ContainerBuilder.php
index 0e1391f9f81e577d007d7a8dc5378c1c868f264c..7c9860b4dc47add661db2b80406c414997809085 100644
--- a/civicrm/vendor/symfony/dependency-injection/ContainerBuilder.php
+++ b/civicrm/vendor/symfony/dependency-injection/ContainerBuilder.php
@@ -11,13 +11,23 @@
 
 namespace Symfony\Component\DependencyInjection;
 
+use Psr\Container\ContainerInterface as PsrContainerInterface;
+use Symfony\Component\Config\Resource\ClassExistenceResource;
+use Symfony\Component\Config\Resource\ComposerResource;
+use Symfony\Component\Config\Resource\DirectoryResource;
+use Symfony\Component\Config\Resource\FileExistenceResource;
 use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Config\Resource\GlobResource;
+use Symfony\Component\Config\Resource\ReflectionClassResource;
 use Symfony\Component\Config\Resource\ResourceInterface;
+use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
+use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
+use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
 use Symfony\Component\DependencyInjection\Compiler\Compiler;
 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
 use Symfony\Component\DependencyInjection\Compiler\PassConfig;
+use Symfony\Component\DependencyInjection\Compiler\ResolveEnvPlaceholdersPass;
 use Symfony\Component\DependencyInjection\Exception\BadMethodCallException;
-use Symfony\Component\DependencyInjection\Exception\InactiveScopeException;
 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
 use Symfony\Component\DependencyInjection\Exception\LogicException;
 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
@@ -26,7 +36,10 @@ use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
 use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
 use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\InstantiatorInterface;
 use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator;
+use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
+use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
 use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
+use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher;
 use Symfony\Component\ExpressionLanguage\Expression;
 use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
 
@@ -40,34 +53,29 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
     /**
      * @var ExtensionInterface[]
      */
-    private $extensions = array();
+    private $extensions = [];
 
     /**
      * @var ExtensionInterface[]
      */
-    private $extensionsByNs = array();
+    private $extensionsByNs = [];
 
     /**
      * @var Definition[]
      */
-    private $definitions = array();
-
-    /**
-     * @var Definition[]
-     */
-    private $obsoleteDefinitions = array();
+    private $definitions = [];
 
     /**
      * @var Alias[]
      */
-    private $aliasDefinitions = array();
+    private $aliasDefinitions = [];
 
     /**
      * @var ResourceInterface[]
      */
-    private $resources = array();
+    private $resources = [];
 
-    private $extensionConfigs = array();
+    private $extensionConfigs = [];
 
     /**
      * @var Compiler
@@ -89,19 +97,62 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
     /**
      * @var ExpressionFunctionProviderInterface[]
      */
-    private $expressionLanguageProviders = array();
+    private $expressionLanguageProviders = [];
+
+    /**
+     * @var string[] with tag names used by findTaggedServiceIds
+     */
+    private $usedTags = [];
+
+    /**
+     * @var string[][] a map of env var names to their placeholders
+     */
+    private $envPlaceholders = [];
+
+    /**
+     * @var int[] a map of env vars to their resolution counter
+     */
+    private $envCounters = [];
+
+    /**
+     * @var string[] the list of vendor directories
+     */
+    private $vendors;
+
+    private $autoconfiguredInstanceof = [];
+
+    private $removedIds = [];
+
+    private $removedBindingIds = [];
+
+    private static $internalTypes = [
+        'int' => true,
+        'float' => true,
+        'string' => true,
+        'bool' => true,
+        'resource' => true,
+        'object' => true,
+        'array' => true,
+        'null' => true,
+        'callable' => true,
+        'iterable' => true,
+        'mixed' => true,
+    ];
 
     public function __construct(ParameterBagInterface $parameterBag = null)
     {
         parent::__construct($parameterBag);
 
         $this->trackResources = interface_exists('Symfony\Component\Config\Resource\ResourceInterface');
+        $this->setDefinition('service_container', (new Definition(ContainerInterface::class))->setSynthetic(true)->setPublic(true));
+        $this->setAlias(PsrContainerInterface::class, new Alias('service_container', false));
+        $this->setAlias(ContainerInterface::class, new Alias('service_container', false));
     }
 
     /**
-     * @var string[] with tag names used by findTaggedServiceIds
+     * @var \ReflectionClass[] a list of class reflectors
      */
-    private $usedTags = array();
+    private $classReflectors;
 
     /**
      * Sets the track resources flag.
@@ -162,7 +213,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
             return $this->extensionsByNs[$name];
         }
 
-        throw new LogicException(sprintf('Container extension "%s" is not registered', $name));
+        throw new LogicException(sprintf('Container extension "%s" is not registered.', $name));
     }
 
     /**
@@ -194,7 +245,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     public function getResources()
     {
-        return array_unique($this->resources);
+        return array_values($this->resources);
     }
 
     /**
@@ -206,7 +257,11 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
             return $this;
         }
 
-        $this->resources[] = $resource;
+        if ($resource instanceof GlobResource && $this->inVendors($resource->getPrefix())) {
+            return $this;
+        }
+
+        $this->resources[(string) $resource] = $resource;
 
         return $this;
     }
@@ -232,14 +287,39 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
     /**
      * Adds the object class hierarchy as resources.
      *
-     * @param object $object An object instance
+     * @param object|string $object An object instance or class name
      *
      * @return $this
      */
     public function addObjectResource($object)
     {
         if ($this->trackResources) {
-            $this->addClassResource(new \ReflectionClass($object));
+            if (\is_object($object)) {
+                $object = \get_class($object);
+            }
+            if (!isset($this->classReflectors[$object])) {
+                $this->classReflectors[$object] = new \ReflectionClass($object);
+            }
+            $class = $this->classReflectors[$object];
+
+            foreach ($class->getInterfaceNames() as $name) {
+                if (null === $interface = &$this->classReflectors[$name]) {
+                    $interface = new \ReflectionClass($name);
+                }
+                $file = $interface->getFileName();
+                if (false !== $file && file_exists($file)) {
+                    $this->fileExists($file);
+                }
+            }
+            do {
+                $file = $class->getFileName();
+                if (false !== $file && file_exists($file)) {
+                    $this->fileExists($file);
+                }
+                foreach ($class->getTraitNames() as $name) {
+                    $this->addObjectResource($name);
+                }
+            } while ($class = $class->getParentClass());
         }
 
         return $this;
@@ -249,20 +329,107 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      * Adds the given class hierarchy as resources.
      *
      * @return $this
+     *
+     * @deprecated since version 3.3, to be removed in 4.0. Use addObjectResource() or getReflectionClass() instead.
      */
     public function addClassResource(\ReflectionClass $class)
     {
-        if (!$this->trackResources) {
-            return $this;
+        @trigger_error('The '.__METHOD__.'() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the addObjectResource() or the getReflectionClass() method instead.', E_USER_DEPRECATED);
+
+        return $this->addObjectResource($class->name);
+    }
+
+    /**
+     * Retrieves the requested reflection class and registers it for resource tracking.
+     *
+     * @param string $class
+     * @param bool   $throw
+     *
+     * @return \ReflectionClass|null
+     *
+     * @throws \ReflectionException when a parent class/interface/trait is not found and $throw is true
+     *
+     * @final
+     */
+    public function getReflectionClass($class, $throw = true)
+    {
+        if (!$class = $this->getParameterBag()->resolveValue($class)) {
+            return null;
+        }
+
+        if (isset(self::$internalTypes[$class])) {
+            return null;
         }
 
-        do {
-            if (is_file($class->getFileName())) {
-                $this->addResource(new FileResource($class->getFileName()));
+        $resource = $classReflector = null;
+
+        try {
+            if (isset($this->classReflectors[$class])) {
+                $classReflector = $this->classReflectors[$class];
+            } elseif (class_exists(ClassExistenceResource::class)) {
+                $resource = new ClassExistenceResource($class, false);
+                $classReflector = $resource->isFresh(0) ? false : new \ReflectionClass($class);
+            } else {
+                $classReflector = class_exists($class) ? new \ReflectionClass($class) : false;
             }
-        } while ($class = $class->getParentClass());
+        } catch (\ReflectionException $e) {
+            if ($throw) {
+                throw $e;
+            }
+        }
 
-        return $this;
+        if ($this->trackResources) {
+            if (!$classReflector) {
+                $this->addResource($resource ?: new ClassExistenceResource($class, false));
+            } elseif (!$classReflector->isInternal()) {
+                $path = $classReflector->getFileName();
+
+                if (!$this->inVendors($path)) {
+                    $this->addResource(new ReflectionClassResource($classReflector, $this->vendors));
+                }
+            }
+            $this->classReflectors[$class] = $classReflector;
+        }
+
+        return $classReflector ?: null;
+    }
+
+    /**
+     * Checks whether the requested file or directory exists and registers the result for resource tracking.
+     *
+     * @param string      $path          The file or directory path for which to check the existence
+     * @param bool|string $trackContents Whether to track contents of the given resource. If a string is passed,
+     *                                   it will be used as pattern for tracking contents of the requested directory
+     *
+     * @return bool
+     *
+     * @final
+     */
+    public function fileExists($path, $trackContents = true)
+    {
+        $exists = file_exists($path);
+
+        if (!$this->trackResources || $this->inVendors($path)) {
+            return $exists;
+        }
+
+        if (!$exists) {
+            $this->addResource(new FileExistenceResource($path));
+
+            return $exists;
+        }
+
+        if (is_dir($path)) {
+            if ($trackContents) {
+                $this->addResource(new DirectoryResource($path, \is_string($trackContents) ? $trackContents : null));
+            } else {
+                $this->addResource(new GlobResource($path, '/*', false));
+            }
+        } elseif ($trackContents) {
+            $this->addResource(new FileResource($path));
+        }
+
+        return $exists;
     }
 
     /**
@@ -273,17 +440,17 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      *
      * @return $this
      *
-     * @throws BadMethodCallException When this ContainerBuilder is frozen
-     * @throws \LogicException        if the container is frozen
+     * @throws BadMethodCallException When this ContainerBuilder is compiled
+     * @throws \LogicException        if the extension is not registered
      */
     public function loadFromExtension($extension, array $values = null)
     {
-        if ($this->isFrozen()) {
-            throw new BadMethodCallException('Cannot load from an extension on a frozen container.');
+        if ($this->isCompiled()) {
+            throw new BadMethodCallException('Cannot load from an extension on a compiled container.');
         }
 
         if (\func_num_args() < 2) {
-            $values = array();
+            $values = [];
         }
 
         $namespace = $this->getExtension($extension)->getAlias();
@@ -296,14 +463,28 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
     /**
      * Adds a compiler pass.
      *
-     * @param CompilerPassInterface $pass A compiler pass
-     * @param string                $type The type of compiler pass
+     * @param CompilerPassInterface $pass     A compiler pass
+     * @param string                $type     The type of compiler pass
+     * @param int                   $priority Used to sort the passes
      *
      * @return $this
      */
-    public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION)
+    public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/*, int $priority = 0*/)
     {
-        $this->getCompiler()->addPass($pass, $type);
+        if (\func_num_args() >= 3) {
+            $priority = func_get_arg(2);
+        } else {
+            if (__CLASS__ !== static::class) {
+                $r = new \ReflectionMethod($this, __FUNCTION__);
+                if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
+                    @trigger_error(sprintf('Method %s() will have a third `int $priority = 0` argument in version 4.0. Not defining it is deprecated since Symfony 3.2.', __METHOD__), E_USER_DEPRECATED);
+                }
+            }
+
+            $priority = 0;
+        }
+
+        $this->getCompiler()->addPass($pass, $type, $priority);
 
         $this->addObjectResource($pass);
 
@@ -334,70 +515,26 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
         return $this->compiler;
     }
 
-    /**
-     * Returns all Scopes.
-     *
-     * @return array An array of scopes
-     *
-     * @deprecated since version 2.8, to be removed in 3.0.
-     */
-    public function getScopes($triggerDeprecationError = true)
-    {
-        if ($triggerDeprecationError) {
-            @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-        }
-
-        return $this->scopes;
-    }
-
-    /**
-     * Returns all Scope children.
-     *
-     * @return array An array of scope children
-     *
-     * @deprecated since version 2.8, to be removed in 3.0.
-     */
-    public function getScopeChildren($triggerDeprecationError = true)
-    {
-        if ($triggerDeprecationError) {
-            @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-        }
-
-        return $this->scopeChildren;
-    }
-
     /**
      * Sets a service.
      *
-     * Note: The $scope parameter is deprecated since version 2.8 and will be removed in 3.0.
-     *
-     * @param string $id      The service identifier
-     * @param object $service The service instance
-     * @param string $scope   The scope
+     * @param string      $id      The service identifier
+     * @param object|null $service The service instance
      *
-     * @throws BadMethodCallException When this ContainerBuilder is frozen
+     * @throws BadMethodCallException When this ContainerBuilder is compiled
      */
-    public function set($id, $service, $scope = self::SCOPE_CONTAINER)
+    public function set($id, $service)
     {
-        $id = strtolower($id);
-        $set = isset($this->definitions[$id]);
-
-        if ($this->isFrozen() && ($set || isset($this->obsoleteDefinitions[$id])) && !$this->{$set ? 'definitions' : 'obsoleteDefinitions'}[$id]->isSynthetic()) {
-            // setting a synthetic service on a frozen container is alright
-            throw new BadMethodCallException(sprintf('Setting service "%s" on a frozen container is not allowed.', $id));
-        }
+        $id = $this->normalizeId($id);
 
-        if ($set) {
-            $this->obsoleteDefinitions[$id] = $this->definitions[$id];
+        if ($this->isCompiled() && (isset($this->definitions[$id]) && !$this->definitions[$id]->isSynthetic())) {
+            // setting a synthetic service on a compiled container is alright
+            throw new BadMethodCallException(sprintf('Setting service "%s" for an unknown or non-synthetic service definition on a compiled container is not allowed.', $id));
         }
 
-        unset($this->definitions[$id], $this->aliasDefinitions[$id]);
+        unset($this->definitions[$id], $this->aliasDefinitions[$id], $this->removedIds[$id]);
 
-        parent::set($id, $service, $scope);
-
-        if (isset($this->obsoleteDefinitions[$id]) && $this->obsoleteDefinitions[$id]->isSynchronized(false)) {
-            $this->synchronize($id);
-        }
+        parent::set($id, $service);
     }
 
     /**
@@ -407,7 +544,10 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     public function removeDefinition($id)
     {
-        unset($this->definitions[strtolower($id)]);
+        if (isset($this->definitions[$id = $this->normalizeId($id)])) {
+            unset($this->definitions[$id]);
+            $this->removedIds[$id] = true;
+        }
     }
 
     /**
@@ -419,7 +559,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     public function has($id)
     {
-        $id = strtolower($id);
+        $id = $this->normalizeId($id);
 
         return isset($this->definitions[$id]) || isset($this->aliasDefinitions[$id]) || parent::has($id);
     }
@@ -430,7 +570,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      * @param string $id              The service identifier
      * @param int    $invalidBehavior The behavior when the service does not exist
      *
-     * @return object The associated service
+     * @return object|null The associated service
      *
      * @throws InvalidArgumentException          when no definitions are available
      * @throws ServiceCircularReferenceException When a circular reference is detected
@@ -441,47 +581,62 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE)
     {
-        $id = strtolower($id);
+        if ($this->isCompiled() && isset($this->removedIds[$id = $this->normalizeId($id)])) {
+            @trigger_error(sprintf('Fetching the "%s" private service or alias is deprecated since Symfony 3.4 and will fail in 4.0. Make it public instead.', $id), E_USER_DEPRECATED);
+        }
+
+        return $this->doGet($id, $invalidBehavior);
+    }
 
-        if ($service = parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) {
-            return $service;
+    private function doGet($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, array &$inlineServices = null, $isConstructorArgument = false)
+    {
+        $id = $this->normalizeId($id);
+
+        if (isset($inlineServices[$id])) {
+            return $inlineServices[$id];
+        }
+        if (null === $inlineServices) {
+            $isConstructorArgument = true;
+            $inlineServices = [];
+        }
+        try {
+            if (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior) {
+                return parent::get($id, $invalidBehavior);
+            }
+            if ($service = parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) {
+                return $service;
+            }
+        } catch (ServiceCircularReferenceException $e) {
+            if ($isConstructorArgument) {
+                throw $e;
+            }
         }
 
-        if (!array_key_exists($id, $this->definitions) && isset($this->aliasDefinitions[$id])) {
-            return $this->get((string) $this->aliasDefinitions[$id], $invalidBehavior);
+        if (!isset($this->definitions[$id]) && isset($this->aliasDefinitions[$id])) {
+            return $this->doGet((string) $this->aliasDefinitions[$id], $invalidBehavior, $inlineServices, $isConstructorArgument);
         }
 
         try {
             $definition = $this->getDefinition($id);
         } catch (ServiceNotFoundException $e) {
             if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) {
-                return;
+                return null;
             }
 
             throw $e;
         }
 
-        $this->loading[$id] = true;
+        if ($isConstructorArgument) {
+            $this->loading[$id] = true;
+        }
 
         try {
-            $service = $this->createService($definition, new \SplObjectStorage(), $id);
-        } catch (\Exception $e) {
-            unset($this->loading[$id]);
-
-            if ($e instanceof InactiveScopeException && self::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) {
-                return;
+            return $this->createService($definition, $inlineServices, $isConstructorArgument, $id);
+        } finally {
+            if ($isConstructorArgument) {
+                unset($this->loading[$id]);
             }
-
-            throw $e;
-        } catch (\Throwable $e) {
-            unset($this->loading[$id]);
-
-            throw $e;
         }
-
-        unset($this->loading[$id]);
-
-        return $service;
     }
 
     /**
@@ -493,7 +648,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      * the parameters passed to the container constructor to have precedence
      * over the loaded ones.
      *
-     *     $container = new ContainerBuilder(new ParameterBag(array('foo' => 'bar')));
+     *     $container = new ContainerBuilder(new ParameterBag(['foo' => 'bar']));
      *     $loader = new LoaderXXX($container);
      *     $loader->load('resource_name');
      *     $container->register('foo', 'stdClass');
@@ -502,12 +657,12 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      * parameter, the value will still be 'bar' as defined in the ContainerBuilder
      * constructor.
      *
-     * @throws BadMethodCallException When this ContainerBuilder is frozen
+     * @throws BadMethodCallException When this ContainerBuilder is compiled
      */
-    public function merge(ContainerBuilder $container)
+    public function merge(self $container)
     {
-        if ($this->isFrozen()) {
-            throw new BadMethodCallException('Cannot merge on a frozen container.');
+        if ($this->isCompiled()) {
+            throw new BadMethodCallException('Cannot merge on a compiled container.');
         }
 
         $this->addDefinitions($container->getDefinitions());
@@ -522,11 +677,37 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
 
         foreach ($this->extensions as $name => $extension) {
             if (!isset($this->extensionConfigs[$name])) {
-                $this->extensionConfigs[$name] = array();
+                $this->extensionConfigs[$name] = [];
             }
 
             $this->extensionConfigs[$name] = array_merge($this->extensionConfigs[$name], $container->getExtensionConfig($name));
         }
+
+        if ($this->getParameterBag() instanceof EnvPlaceholderParameterBag && $container->getParameterBag() instanceof EnvPlaceholderParameterBag) {
+            $envPlaceholders = $container->getParameterBag()->getEnvPlaceholders();
+            $this->getParameterBag()->mergeEnvPlaceholders($container->getParameterBag());
+        } else {
+            $envPlaceholders = [];
+        }
+
+        foreach ($container->envCounters as $env => $count) {
+            if (!$count && !isset($envPlaceholders[$env])) {
+                continue;
+            }
+            if (!isset($this->envCounters[$env])) {
+                $this->envCounters[$env] = $count;
+            } else {
+                $this->envCounters[$env] += $count;
+            }
+        }
+
+        foreach ($container->getAutoconfiguredInstanceof() as $interface => $childDefinition) {
+            if (isset($this->autoconfiguredInstanceof[$interface])) {
+                throw new InvalidArgumentException(sprintf('"%s" has already been autoconfigured and merge() does not support merging autoconfiguration for the same class/interface.', $interface));
+            }
+
+            $this->autoconfiguredInstanceof[$interface] = $childDefinition;
+        }
     }
 
     /**
@@ -539,7 +720,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
     public function getExtensionConfig($name)
     {
         if (!isset($this->extensionConfigs[$name])) {
-            $this->extensionConfigs[$name] = array();
+            $this->extensionConfigs[$name] = [];
         }
 
         return $this->extensionConfigs[$name];
@@ -554,7 +735,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
     public function prependExtensionConfig($name, array $config)
     {
         if (!isset($this->extensionConfigs[$name])) {
-            $this->extensionConfigs[$name] = array();
+            $this->extensionConfigs[$name] = [];
         }
 
         array_unshift($this->extensionConfigs[$name], $config);
@@ -573,9 +754,25 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      *  * Parameter values are resolved;
      *  * The parameter bag is frozen;
      *  * Extension loading is disabled.
+     *
+     * @param bool $resolveEnvPlaceholders Whether %env()% parameters should be resolved using the current
+     *                                     env vars or be replaced by uniquely identifiable placeholders.
+     *                                     Set to "true" when you want to use the current ContainerBuilder
+     *                                     directly, keep to "false" when the container is dumped instead.
      */
-    public function compile()
+    public function compile(/*$resolveEnvPlaceholders = false*/)
     {
+        if (1 <= \func_num_args()) {
+            $resolveEnvPlaceholders = func_get_arg(0);
+        } else {
+            if (__CLASS__ !== static::class) {
+                $r = new \ReflectionMethod($this, __FUNCTION__);
+                if (__CLASS__ !== $r->getDeclaringClass()->getName() && (1 > $r->getNumberOfParameters() || 'resolveEnvPlaceholders' !== $r->getParameters()[0]->name)) {
+                    @trigger_error(sprintf('The %s::compile() method expects a first "$resolveEnvPlaceholders" argument since Symfony 3.3. It will be made mandatory in 4.0.', static::class), E_USER_DEPRECATED);
+                }
+            }
+            $resolveEnvPlaceholders = false;
+        }
         $compiler = $this->getCompiler();
 
         if ($this->trackResources) {
@@ -583,30 +780,55 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
                 $this->addObjectResource($pass);
             }
         }
+        $bag = $this->getParameterBag();
+
+        if ($resolveEnvPlaceholders && $bag instanceof EnvPlaceholderParameterBag) {
+            $compiler->addPass(new ResolveEnvPlaceholdersPass(), PassConfig::TYPE_AFTER_REMOVING, -1000);
+        }
 
         $compiler->compile($this);
 
-        if ($this->trackResources) {
-            foreach ($this->definitions as $definition) {
-                if ($definition->isLazy() && ($class = $definition->getClass()) && class_exists($class)) {
-                    $this->addClassResource(new \ReflectionClass($class));
-                }
+        foreach ($this->definitions as $id => $definition) {
+            if ($this->trackResources && $definition->isLazy()) {
+                $this->getReflectionClass($definition->getClass());
             }
         }
 
-        $this->extensionConfigs = array();
+        $this->extensionConfigs = [];
+
+        if ($bag instanceof EnvPlaceholderParameterBag) {
+            if ($resolveEnvPlaceholders) {
+                $this->parameterBag = new ParameterBag($this->resolveEnvPlaceholders($bag->all(), true));
+            }
+
+            $this->envPlaceholders = $bag->getEnvPlaceholders();
+        }
 
         parent::compile();
+
+        foreach ($this->definitions + $this->aliasDefinitions as $id => $definition) {
+            if (!$definition->isPublic() || $definition->isPrivate()) {
+                $this->removedIds[$id] = true;
+            }
+        }
     }
 
     /**
-     * Gets all service ids.
-     *
-     * @return array An array of all defined service ids
+     * {@inheritdoc}
      */
     public function getServiceIds()
     {
-        return array_unique(array_merge(array_keys($this->getDefinitions()), array_keys($this->aliasDefinitions), parent::getServiceIds()));
+        return array_map('strval', array_unique(array_merge(array_keys($this->getDefinitions()), array_keys($this->aliasDefinitions), parent::getServiceIds())));
+    }
+
+    /**
+     * Gets removed service or alias ids.
+     *
+     * @return array
+     */
+    public function getRemovedIds()
+    {
+        return $this->removedIds;
     }
 
     /**
@@ -624,7 +846,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     public function setAliases(array $aliases)
     {
-        $this->aliasDefinitions = array();
+        $this->aliasDefinitions = [];
         $this->addAliases($aliases);
     }
 
@@ -634,19 +856,21 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      * @param string       $alias The alias to create
      * @param string|Alias $id    The service to alias
      *
+     * @return Alias
+     *
      * @throws InvalidArgumentException if the id is not a string or an Alias
      * @throws InvalidArgumentException if the alias is for itself
      */
     public function setAlias($alias, $id)
     {
-        $alias = strtolower($alias);
+        $alias = $this->normalizeId($alias);
 
         if ('' === $alias || '\\' === substr($alias, -1) || \strlen($alias) !== strcspn($alias, "\0\r\n'")) {
-            throw new InvalidArgumentException(sprintf('Invalid alias id: "%s"', $alias));
+            throw new InvalidArgumentException(sprintf('Invalid alias id: "%s".', $alias));
         }
 
         if (\is_string($id)) {
-            $id = new Alias($id);
+            $id = new Alias($this->normalizeId($id));
         } elseif (!$id instanceof Alias) {
             throw new InvalidArgumentException('$id must be a string, or an Alias object.');
         }
@@ -655,9 +879,9 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
             throw new InvalidArgumentException(sprintf('An alias can not reference itself, got a circular reference on "%s".', $alias));
         }
 
-        unset($this->definitions[$alias]);
+        unset($this->definitions[$alias], $this->removedIds[$alias]);
 
-        $this->aliasDefinitions[$alias] = $id;
+        return $this->aliasDefinitions[$alias] = $id;
     }
 
     /**
@@ -667,7 +891,10 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     public function removeAlias($alias)
     {
-        unset($this->aliasDefinitions[strtolower($alias)]);
+        if (isset($this->aliasDefinitions[$alias = $this->normalizeId($alias)])) {
+            unset($this->aliasDefinitions[$alias]);
+            $this->removedIds[$alias] = true;
+        }
     }
 
     /**
@@ -679,7 +906,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     public function hasAlias($id)
     {
-        return isset($this->aliasDefinitions[strtolower($id)]);
+        return isset($this->aliasDefinitions[$this->normalizeId($id)]);
     }
 
     /**
@@ -703,7 +930,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     public function getAlias($id)
     {
-        $id = strtolower($id);
+        $id = $this->normalizeId($id);
 
         if (!isset($this->aliasDefinitions[$id])) {
             throw new InvalidArgumentException(sprintf('The service alias "%s" does not exist.', $id));
@@ -718,8 +945,8 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      * This methods allows for simple registration of service definition
      * with a fluid interface.
      *
-     * @param string $id         The service identifier
-     * @param string $class|null The service class
+     * @param string      $id    The service identifier
+     * @param string|null $class The service class
      *
      * @return Definition A Definition instance
      */
@@ -728,6 +955,22 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
         return $this->setDefinition($id, new Definition($class));
     }
 
+    /**
+     * Registers an autowired service definition.
+     *
+     * This method implements a shortcut for using setDefinition() with
+     * an autowired definition.
+     *
+     * @param string      $id    The service identifier
+     * @param string|null $class The service class
+     *
+     * @return Definition The created definition
+     */
+    public function autowire($id, $class = null)
+    {
+        return $this->setDefinition($id, (new Definition($class))->setAutowired(true));
+    }
+
     /**
      * Adds the service definitions.
      *
@@ -747,7 +990,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     public function setDefinitions(array $definitions)
     {
-        $this->definitions = array();
+        $this->definitions = [];
         $this->addDefinitions($definitions);
     }
 
@@ -769,21 +1012,21 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      *
      * @return Definition the service definition
      *
-     * @throws BadMethodCallException When this ContainerBuilder is frozen
+     * @throws BadMethodCallException When this ContainerBuilder is compiled
      */
     public function setDefinition($id, Definition $definition)
     {
-        if ($this->isFrozen()) {
-            throw new BadMethodCallException('Adding definition to a frozen container is not allowed');
+        if ($this->isCompiled()) {
+            throw new BadMethodCallException('Adding definition to a compiled container is not allowed.');
         }
 
-        $id = strtolower($id);
+        $id = $this->normalizeId($id);
 
         if ('' === $id || '\\' === substr($id, -1) || \strlen($id) !== strcspn($id, "\0\r\n'")) {
-            throw new InvalidArgumentException(sprintf('Invalid service id: "%s"', $id));
+            throw new InvalidArgumentException(sprintf('Invalid service id: "%s".', $id));
         }
 
-        unset($this->aliasDefinitions[$id]);
+        unset($this->aliasDefinitions[$id], $this->removedIds[$id]);
 
         return $this->definitions[$id] = $definition;
     }
@@ -797,7 +1040,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     public function hasDefinition($id)
     {
-        return array_key_exists(strtolower($id), $this->definitions);
+        return isset($this->definitions[$this->normalizeId($id)]);
     }
 
     /**
@@ -811,9 +1054,9 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     public function getDefinition($id)
     {
-        $id = strtolower($id);
+        $id = $this->normalizeId($id);
 
-        if (!array_key_exists($id, $this->definitions)) {
+        if (!isset($this->definitions[$id])) {
             throw new ServiceNotFoundException($id);
         }
 
@@ -833,10 +1076,21 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     public function findDefinition($id)
     {
-        $id = strtolower($id);
+        $id = $this->normalizeId($id);
 
+        $seen = [];
         while (isset($this->aliasDefinitions[$id])) {
             $id = (string) $this->aliasDefinitions[$id];
+
+            if (isset($seen[$id])) {
+                $seen = array_values($seen);
+                $seen = \array_slice($seen, array_search($id, $seen));
+                $seen[] = $id;
+
+                throw new ServiceCircularReferenceException($id, $seen);
+            }
+
+            $seen[$id] = $id;
         }
 
         return $this->getDefinition($id);
@@ -849,22 +1103,19 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      * @param string     $id         The service identifier
      * @param bool       $tryProxy   Whether to try proxying the service with a lazy proxy
      *
-     * @return object The service described by the service definition
+     * @return mixed The service described by the service definition
      *
-     * @throws RuntimeException         When the scope is inactive
      * @throws RuntimeException         When the factory definition is incomplete
      * @throws RuntimeException         When the service is a synthetic service
      * @throws InvalidArgumentException When configure callable is not callable
-     *
-     * @internal this method is public because of PHP 5.3 limitations, do not use it explicitly in your code
      */
-    public function createService(Definition $definition, \SplObjectStorage $inlinedDefinitions, $id = null, $tryProxy = true)
+    private function createService(Definition $definition, array &$inlineServices, $isConstructorArgument = false, $id = null, $tryProxy = true)
     {
-        if (null === $id && isset($inlinedDefinitions[$definition])) {
-            return $inlinedDefinitions[$definition];
+        if (null === $id && isset($inlineServices[$h = spl_object_hash($definition)])) {
+            return $inlineServices[$h];
         }
 
-        if ($definition instanceof DefinitionDecorator) {
+        if ($definition instanceof ChildDefinition) {
             throw new RuntimeException(sprintf('Constructing service "%s" from a parent definition is not supported at build time.', $id));
         }
 
@@ -876,19 +1127,15 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
             @trigger_error($definition->getDeprecationMessage($id), E_USER_DEPRECATED);
         }
 
-        if ($tryProxy && $definition->isLazy()) {
-            $container = $this;
-
-            $proxy = $this
-                ->getProxyInstantiator()
-                ->instantiateProxy(
-                    $container,
-                    $definition,
-                    $id, function () use ($definition, $inlinedDefinitions, $id, $container) {
-                        return $container->createService($definition, $inlinedDefinitions, $id, false);
-                    }
-                );
-            $this->shareService($definition, $proxy, $id, $inlinedDefinitions);
+        if ($tryProxy && $definition->isLazy() && !$tryProxy = !($proxy = $this->proxyInstantiator) || $proxy instanceof RealServiceInstantiator) {
+            $proxy = $proxy->instantiateProxy(
+                $this,
+                $definition,
+                $id, function () use ($definition, &$inlineServices, $id) {
+                    return $this->createService($definition, $inlineServices, true, $id, false);
+                }
+            );
+            $this->shareService($definition, $proxy, $id, $inlineServices);
 
             return $proxy;
         }
@@ -899,15 +1146,21 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
             require_once $parameterBag->resolveValue($definition->getFile());
         }
 
-        $arguments = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments())), $inlinedDefinitions);
+        $arguments = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments())), $inlineServices, $isConstructorArgument);
 
         if (null !== $factory = $definition->getFactory()) {
             if (\is_array($factory)) {
-                $factory = array($this->doResolveServices($parameterBag->resolveValue($factory[0]), $inlinedDefinitions), $factory[1]);
+                $factory = [$this->doResolveServices($parameterBag->resolveValue($factory[0]), $inlineServices, $isConstructorArgument), $factory[1]];
             } elseif (!\is_string($factory)) {
-                throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory', $id));
+                throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory.', $id));
             }
+        }
+
+        if (null !== $id && $definition->isShared() && isset($this->services[$id]) && ($tryProxy || !$definition->isLazy())) {
+            return $this->services[$id];
+        }
 
+        if (null !== $factory) {
             $service = \call_user_func_array($factory, $arguments);
 
             if (!$definition->isDeprecated() && \is_array($factory) && \is_string($factory[0])) {
@@ -917,38 +1170,31 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
                     @trigger_error(sprintf('The "%s" service relies on the deprecated "%s" factory class. It should either be deprecated or its factory upgraded.', $id, $r->name), E_USER_DEPRECATED);
                 }
             }
-        } elseif (null !== $definition->getFactoryMethod(false)) {
-            if (null !== $definition->getFactoryClass(false)) {
-                $factory = $parameterBag->resolveValue($definition->getFactoryClass(false));
-            } elseif (null !== $definition->getFactoryService(false)) {
-                $factory = $this->get($parameterBag->resolveValue($definition->getFactoryService(false)));
-            } else {
-                throw new RuntimeException(sprintf('Cannot create service "%s" from factory method without a factory service or factory class.', $id));
-            }
-
-            $service = \call_user_func_array(array($factory, $definition->getFactoryMethod(false)), $arguments);
         } else {
-            $r = new \ReflectionClass($parameterBag->resolveValue($definition->getClass()));
+            $r = new \ReflectionClass($class = $parameterBag->resolveValue($definition->getClass()));
 
             $service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments);
+            // don't trigger deprecations for internal uses
+            // @deprecated since version 3.3, to be removed in 4.0 along with the deprecated class
+            $deprecationWhitelist = ['event_dispatcher' => ContainerAwareEventDispatcher::class];
 
-            if (!$definition->isDeprecated() && 0 < strpos($r->getDocComment(), "\n * @deprecated ")) {
+            if (!$definition->isDeprecated() && 0 < strpos($r->getDocComment(), "\n * @deprecated ") && (!isset($deprecationWhitelist[$id]) || $deprecationWhitelist[$id] !== $class)) {
                 @trigger_error(sprintf('The "%s" service relies on the deprecated "%s" class. It should either be deprecated or its implementation upgraded.', $id, $r->name), E_USER_DEPRECATED);
             }
         }
 
         if ($tryProxy || !$definition->isLazy()) {
             // share only if proxying failed, or if not a proxy
-            $this->shareService($definition, $service, $id, $inlinedDefinitions);
+            $this->shareService($definition, $service, $id, $inlineServices);
         }
 
-        $properties = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getProperties())), $inlinedDefinitions);
+        $properties = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getProperties())), $inlineServices);
         foreach ($properties as $name => $value) {
             $service->$name = $value;
         }
 
         foreach ($definition->getMethodCalls() as $call) {
-            $this->callMethod($service, $call, $inlinedDefinitions);
+            $this->callMethod($service, $call, $inlineServices);
         }
 
         if ($callable = $definition->getConfigurator()) {
@@ -956,9 +1202,9 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
                 $callable[0] = $parameterBag->resolveValue($callable[0]);
 
                 if ($callable[0] instanceof Reference) {
-                    $callable[0] = $this->get((string) $callable[0], $callable[0]->getInvalidBehavior());
+                    $callable[0] = $this->doGet((string) $callable[0], $callable[0]->getInvalidBehavior(), $inlineServices);
                 } elseif ($callable[0] instanceof Definition) {
-                    $callable[0] = $this->createService($callable[0], $inlinedDefinitions);
+                    $callable[0] = $this->createService($callable[0], $inlineServices);
                 }
             }
 
@@ -982,21 +1228,63 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     public function resolveServices($value)
     {
-        return $this->doResolveServices($value, new \SplObjectStorage());
+        return $this->doResolveServices($value);
     }
 
-    private function doResolveServices($value, \SplObjectStorage $inlinedDefinitions)
+    private function doResolveServices($value, array &$inlineServices = [], $isConstructorArgument = false)
     {
         if (\is_array($value)) {
             foreach ($value as $k => $v) {
-                $value[$k] = $this->doResolveServices($v, $inlinedDefinitions);
+                $value[$k] = $this->doResolveServices($v, $inlineServices, $isConstructorArgument);
             }
+        } elseif ($value instanceof ServiceClosureArgument) {
+            $reference = $value->getValues()[0];
+            $value = function () use ($reference) {
+                return $this->resolveServices($reference);
+            };
+        } elseif ($value instanceof IteratorArgument) {
+            $value = new RewindableGenerator(function () use ($value) {
+                foreach ($value->getValues() as $k => $v) {
+                    foreach (self::getServiceConditionals($v) as $s) {
+                        if (!$this->has($s)) {
+                            continue 2;
+                        }
+                    }
+                    foreach (self::getInitializedConditionals($v) as $s) {
+                        if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)) {
+                            continue 2;
+                        }
+                    }
+
+                    yield $k => $this->resolveServices($v);
+                }
+            }, function () use ($value) {
+                $count = 0;
+                foreach ($value->getValues() as $v) {
+                    foreach (self::getServiceConditionals($v) as $s) {
+                        if (!$this->has($s)) {
+                            continue 2;
+                        }
+                    }
+                    foreach (self::getInitializedConditionals($v) as $s) {
+                        if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)) {
+                            continue 2;
+                        }
+                    }
+
+                    ++$count;
+                }
+
+                return $count;
+            });
         } elseif ($value instanceof Reference) {
-            $value = $this->get((string) $value, $value->getInvalidBehavior());
+            $value = $this->doGet((string) $value, $value->getInvalidBehavior(), $inlineServices, $isConstructorArgument);
         } elseif ($value instanceof Definition) {
-            $value = $this->createService($value, $inlinedDefinitions);
+            $value = $this->createService($value, $inlineServices, $isConstructorArgument);
+        } elseif ($value instanceof Parameter) {
+            $value = $this->getParameter((string) $value);
         } elseif ($value instanceof Expression) {
-            $value = $this->getExpressionLanguage()->evaluate($value, array('container' => $this));
+            $value = $this->getExpressionLanguage()->evaluate($value, ['container' => $this]);
         }
 
         return $value;
@@ -1007,7 +1295,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      *
      * Example:
      *
-     *     $container->register('foo')->addTag('my.tag', array('hello' => 'world'));
+     *     $container->register('foo')->addTag('my.tag', ['hello' => 'world']);
      *
      *     $serviceIds = $container->findTaggedServiceIds('my.tag');
      *     foreach ($serviceIds as $serviceId => $tags) {
@@ -1016,16 +1304,20 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      *         }
      *     }
      *
-     * @param string $name The tag name
+     * @param string $name
+     * @param bool   $throwOnAbstract
      *
      * @return array An array of tags with the tagged service as key, holding a list of attribute arrays
      */
-    public function findTaggedServiceIds($name)
+    public function findTaggedServiceIds($name, $throwOnAbstract = false)
     {
         $this->usedTags[] = $name;
-        $tags = array();
+        $tags = [];
         foreach ($this->getDefinitions() as $id => $definition) {
             if ($definition->hasTag($name)) {
+                if ($throwOnAbstract && $definition->isAbstract()) {
+                    throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must not be abstract.', $id, $name));
+                }
                 $tags[$id] = $definition->getTag($name);
             }
         }
@@ -1040,7 +1332,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     public function findTags()
     {
-        $tags = array();
+        $tags = [];
         foreach ($this->getDefinitions() as $id => $definition) {
             $tags = array_merge(array_keys($definition->getTags()), $tags);
         }
@@ -1071,16 +1363,195 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
         return $this->expressionLanguageProviders;
     }
 
+    /**
+     * Returns a ChildDefinition that will be used for autoconfiguring the interface/class.
+     *
+     * @param string $interface The class or interface to match
+     *
+     * @return ChildDefinition
+     */
+    public function registerForAutoconfiguration($interface)
+    {
+        if (!isset($this->autoconfiguredInstanceof[$interface])) {
+            $this->autoconfiguredInstanceof[$interface] = new ChildDefinition('');
+        }
+
+        return $this->autoconfiguredInstanceof[$interface];
+    }
+
+    /**
+     * Returns an array of ChildDefinition[] keyed by interface.
+     *
+     * @return ChildDefinition[]
+     */
+    public function getAutoconfiguredInstanceof()
+    {
+        return $this->autoconfiguredInstanceof;
+    }
+
+    /**
+     * Resolves env parameter placeholders in a string or an array.
+     *
+     * @param mixed            $value     The value to resolve
+     * @param string|true|null $format    A sprintf() format returning the replacement for each env var name or
+     *                                    null to resolve back to the original "%env(VAR)%" format or
+     *                                    true to resolve to the actual values of the referenced env vars
+     * @param array            &$usedEnvs Env vars found while resolving are added to this array
+     *
+     * @return mixed The value with env parameters resolved if a string or an array is passed
+     */
+    public function resolveEnvPlaceholders($value, $format = null, array &$usedEnvs = null)
+    {
+        if (null === $format) {
+            $format = '%%env(%s)%%';
+        }
+
+        $bag = $this->getParameterBag();
+        if (true === $format) {
+            $value = $bag->resolveValue($value);
+        }
+
+        if (\is_array($value)) {
+            $result = [];
+            foreach ($value as $k => $v) {
+                $result[\is_string($k) ? $this->resolveEnvPlaceholders($k, $format, $usedEnvs) : $k] = $this->resolveEnvPlaceholders($v, $format, $usedEnvs);
+            }
+
+            return $result;
+        }
+
+        if (!\is_string($value) || 38 > \strlen($value)) {
+            return $value;
+        }
+        $envPlaceholders = $bag instanceof EnvPlaceholderParameterBag ? $bag->getEnvPlaceholders() : $this->envPlaceholders;
+
+        $completed = false;
+        foreach ($envPlaceholders as $env => $placeholders) {
+            foreach ($placeholders as $placeholder) {
+                if (false !== stripos($value, $placeholder)) {
+                    if (true === $format) {
+                        $resolved = $bag->escapeValue($this->getEnv($env));
+                    } else {
+                        $resolved = sprintf($format, $env);
+                    }
+                    if ($placeholder === $value) {
+                        $value = $resolved;
+                        $completed = true;
+                    } else {
+                        if (!\is_string($resolved) && !is_numeric($resolved)) {
+                            throw new RuntimeException(sprintf('A string value must be composed of strings and/or numbers, but found parameter "env(%s)" of type "%s" inside string value "%s".', $env, \gettype($resolved), $this->resolveEnvPlaceholders($value)));
+                        }
+                        $value = str_ireplace($placeholder, $resolved, $value);
+                    }
+                    $usedEnvs[$env] = $env;
+                    $this->envCounters[$env] = isset($this->envCounters[$env]) ? 1 + $this->envCounters[$env] : 1;
+
+                    if ($completed) {
+                        break 2;
+                    }
+                }
+            }
+        }
+
+        return $value;
+    }
+
+    /**
+     * Get statistics about env usage.
+     *
+     * @return int[] The number of time each env vars has been resolved
+     */
+    public function getEnvCounters()
+    {
+        $bag = $this->getParameterBag();
+        $envPlaceholders = $bag instanceof EnvPlaceholderParameterBag ? $bag->getEnvPlaceholders() : $this->envPlaceholders;
+
+        foreach ($envPlaceholders as $env => $placeholders) {
+            if (!isset($this->envCounters[$env])) {
+                $this->envCounters[$env] = 0;
+            }
+        }
+
+        return $this->envCounters;
+    }
+
+    /**
+     * @internal
+     */
+    public function getNormalizedIds()
+    {
+        $normalizedIds = [];
+
+        foreach ($this->normalizedIds as $k => $v) {
+            if ($v !== (string) $k) {
+                $normalizedIds[$k] = $v;
+            }
+        }
+
+        return $normalizedIds;
+    }
+
+    /**
+     * @final
+     */
+    public function log(CompilerPassInterface $pass, $message)
+    {
+        $this->getCompiler()->log($pass, $this->resolveEnvPlaceholders($message));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function normalizeId($id)
+    {
+        if (!\is_string($id)) {
+            $id = (string) $id;
+        }
+
+        return isset($this->definitions[$id]) || isset($this->aliasDefinitions[$id]) || isset($this->removedIds[$id]) ? $id : parent::normalizeId($id);
+    }
+
+    /**
+     * Gets removed binding ids.
+     *
+     * @return array
+     *
+     * @internal
+     */
+    public function getRemovedBindingIds()
+    {
+        return $this->removedBindingIds;
+    }
+
+    /**
+     * Removes bindings for a service.
+     *
+     * @param string $id The service identifier
+     *
+     * @internal
+     */
+    public function removeBindings($id)
+    {
+        if ($this->hasDefinition($id)) {
+            foreach ($this->getDefinition($id)->getBindings() as $key => $binding) {
+                list(, $bindingId) = $binding->getValues();
+                $this->removedBindingIds[(int) $bindingId] = true;
+            }
+        }
+    }
+
     /**
      * Returns the Service Conditionals.
      *
      * @param mixed $value An array of conditionals to return
      *
      * @return array An array of Service conditionals
+     *
+     * @internal since version 3.4
      */
     public static function getServiceConditionals($value)
     {
-        $services = array();
+        $services = [];
 
         if (\is_array($value)) {
             foreach ($value as $v) {
@@ -1094,90 +1565,100 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
     }
 
     /**
-     * Retrieves the currently set proxy instantiator or instantiates one.
+     * Returns the initialized conditionals.
+     *
+     * @param mixed $value An array of conditionals to return
+     *
+     * @return array An array of uninitialized conditionals
      *
-     * @return InstantiatorInterface
+     * @internal
      */
-    private function getProxyInstantiator()
+    public static function getInitializedConditionals($value)
     {
-        if (!$this->proxyInstantiator) {
-            $this->proxyInstantiator = new RealServiceInstantiator();
+        $services = [];
+
+        if (\is_array($value)) {
+            foreach ($value as $v) {
+                $services = array_unique(array_merge($services, self::getInitializedConditionals($v)));
+            }
+        } elseif ($value instanceof Reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior()) {
+            $services[] = (string) $value;
         }
 
-        return $this->proxyInstantiator;
+        return $services;
     }
 
     /**
-     * Synchronizes a service change.
-     *
-     * This method updates all services that depend on the given
-     * service by calling all methods referencing it.
+     * Computes a reasonably unique hash of a value.
      *
-     * @param string $id A service id
+     * @param mixed $value A serializable value
      *
-     * @deprecated since version 2.7, will be removed in 3.0.
+     * @return string
      */
-    private function synchronize($id)
+    public static function hash($value)
     {
-        if ('request' !== $id) {
-            @trigger_error('The '.__METHOD__.' method is deprecated in version 2.7 and will be removed in version 3.0.', E_USER_DEPRECATED);
+        $hash = substr(base64_encode(hash('sha256', serialize($value), true)), 0, 7);
+
+        return str_replace(['/', '+'], ['.', '_'], strtolower($hash));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getEnv($name)
+    {
+        $value = parent::getEnv($name);
+        $bag = $this->getParameterBag();
+
+        if (!\is_string($value) || !$bag instanceof EnvPlaceholderParameterBag) {
+            return $value;
         }
 
-        foreach ($this->definitions as $definitionId => $definition) {
-            // only check initialized services
-            if (!$this->initialized($definitionId)) {
-                continue;
-            }
+        foreach ($bag->getEnvPlaceholders() as $env => $placeholders) {
+            if (isset($placeholders[$value])) {
+                $bag = new ParameterBag($bag->all());
 
-            foreach ($definition->getMethodCalls() as $call) {
-                foreach ($call[1] as $argument) {
-                    if ($argument instanceof Reference && $id == (string) $argument) {
-                        $this->callMethod($this->get($definitionId), $call, new \SplObjectStorage());
-                    }
-                }
+                return $bag->unescapeValue($bag->get("env($name)"));
             }
         }
+
+        $this->resolving["env($name)"] = true;
+        try {
+            return $bag->unescapeValue($this->resolveEnvPlaceholders($bag->escapeValue($value), true));
+        } finally {
+            unset($this->resolving["env($name)"]);
+        }
     }
 
-    private function callMethod($service, $call, \SplObjectStorage $inlinedDefinitions)
+    private function callMethod($service, $call, array &$inlineServices)
     {
-        $services = self::getServiceConditionals($call[1]);
-
-        foreach ($services as $s) {
+        foreach (self::getServiceConditionals($call[1]) as $s) {
             if (!$this->has($s)) {
                 return;
             }
         }
+        foreach (self::getInitializedConditionals($call[1]) as $s) {
+            if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE, $inlineServices)) {
+                return;
+            }
+        }
 
-        \call_user_func_array(array($service, $call[0]), $this->doResolveServices($this->getParameterBag()->unescapeValue($this->getParameterBag()->resolveValue($call[1])), $inlinedDefinitions));
+        \call_user_func_array([$service, $call[0]], $this->doResolveServices($this->getParameterBag()->unescapeValue($this->getParameterBag()->resolveValue($call[1])), $inlineServices));
     }
 
     /**
      * Shares a given service in the container.
      *
-     * @param Definition  $definition
      * @param mixed       $service
      * @param string|null $id
-     *
-     * @throws InactiveScopeException
      */
-    private function shareService(Definition $definition, $service, $id, \SplObjectStorage $inlinedDefinitions)
+    private function shareService(Definition $definition, $service, $id, array &$inlineServices)
     {
-        if (!$definition->isShared() || self::SCOPE_PROTOTYPE === $scope = $definition->getScope(false)) {
-            return;
-        }
-        if (null === $id) {
-            $inlinedDefinitions[$definition] = $service;
-        } else {
-            if (self::SCOPE_CONTAINER !== $scope && !isset($this->scopedServices[$scope])) {
-                throw new InactiveScopeException($id, $scope);
-            }
-
-            $this->services[$lowerId = strtolower($id)] = $service;
+        $inlineServices[null !== $id ? $id : spl_object_hash($definition)] = $service;
 
-            if (self::SCOPE_CONTAINER !== $scope) {
-                $this->scopedServices[$scope][$lowerId] = $service;
-            }
+        if (null !== $id && $definition->isShared()) {
+            $this->services[$id] = $service;
+            unset($this->loading[$id]);
         }
     }
 
@@ -1192,4 +1673,22 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
 
         return $this->expressionLanguage;
     }
+
+    private function inVendors($path)
+    {
+        if (null === $this->vendors) {
+            $resource = new ComposerResource();
+            $this->vendors = $resource->getVendors();
+            $this->addResource($resource);
+        }
+        $path = realpath($path) ?: $path;
+
+        foreach ($this->vendors as $vendor) {
+            if (0 === strpos($path, $vendor) && false !== strpbrk(substr($path, \strlen($vendor), 1), '/'.\DIRECTORY_SEPARATOR)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/ContainerInterface.php b/civicrm/vendor/symfony/dependency-injection/ContainerInterface.php
index d9076eb1f87681f0eb04f9158ff6bbefafaf61fa..c5ab4c2eda7d73d3572b1d2493069ce5ee36a68b 100644
--- a/civicrm/vendor/symfony/dependency-injection/ContainerInterface.php
+++ b/civicrm/vendor/symfony/dependency-injection/ContainerInterface.php
@@ -11,6 +11,7 @@
 
 namespace Symfony\Component\DependencyInjection;
 
+use Psr\Container\ContainerInterface as PsrContainerInterface;
 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
 use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
 use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
@@ -21,24 +22,20 @@ use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
  * @author Fabien Potencier <fabien@symfony.com>
  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  */
-interface ContainerInterface
+interface ContainerInterface extends PsrContainerInterface
 {
     const EXCEPTION_ON_INVALID_REFERENCE = 1;
     const NULL_ON_INVALID_REFERENCE = 2;
     const IGNORE_ON_INVALID_REFERENCE = 3;
-    const SCOPE_CONTAINER = 'container';
-    const SCOPE_PROTOTYPE = 'prototype';
+    const IGNORE_ON_UNINITIALIZED_REFERENCE = 4;
 
     /**
      * Sets a service.
      *
-     * Note: The $scope parameter is deprecated since version 2.8 and will be removed in 3.0.
-     *
-     * @param string $id      The service identifier
-     * @param object $service The service instance
-     * @param string $scope   The scope of the service
+     * @param string      $id      The service identifier
+     * @param object|null $service The service instance
      */
-    public function set($id, $service, $scope = self::SCOPE_CONTAINER);
+    public function set($id, $service);
 
     /**
      * Gets a service.
@@ -46,7 +43,7 @@ interface ContainerInterface
      * @param string $id              The service identifier
      * @param int    $invalidBehavior The behavior when the service does not exist
      *
-     * @return object The associated service
+     * @return object|null The associated service
      *
      * @throws ServiceCircularReferenceException When a circular reference is detected
      * @throws ServiceNotFoundException          When the service is not defined
@@ -64,6 +61,15 @@ interface ContainerInterface
      */
     public function has($id);
 
+    /**
+     * Check for whether or not a service has been initialized.
+     *
+     * @param string $id
+     *
+     * @return bool true if the service has been initialized, false otherwise
+     */
+    public function initialized($id);
+
     /**
      * Gets a parameter.
      *
@@ -91,55 +97,4 @@ interface ContainerInterface
      * @param mixed  $value The parameter value
      */
     public function setParameter($name, $value);
-
-    /**
-     * Enters the given scope.
-     *
-     * @param string $name
-     *
-     * @deprecated since version 2.8, to be removed in 3.0.
-     */
-    public function enterScope($name);
-
-    /**
-     * Leaves the current scope, and re-enters the parent scope.
-     *
-     * @param string $name
-     *
-     * @deprecated since version 2.8, to be removed in 3.0.
-     */
-    public function leaveScope($name);
-
-    /**
-     * Adds a scope to the container.
-     *
-     * @param ScopeInterface $scope
-     *
-     * @deprecated since version 2.8, to be removed in 3.0.
-     */
-    public function addScope(ScopeInterface $scope);
-
-    /**
-     * Whether this container has the given scope.
-     *
-     * @param string $name
-     *
-     * @return bool
-     *
-     * @deprecated since version 2.8, to be removed in 3.0.
-     */
-    public function hasScope($name);
-
-    /**
-     * Determines whether the given scope is currently active.
-     *
-     * It does however not check if the scope actually exists.
-     *
-     * @param string $name
-     *
-     * @return bool
-     *
-     * @deprecated since version 2.8, to be removed in 3.0.
-     */
-    public function isScopeActive($name);
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Definition.php b/civicrm/vendor/symfony/dependency-injection/Definition.php
index d9c37ea50420dbcf3d03ab2e43593cd3d5e4b302..c7d204948401d5fdc819ec543b337005f0b07bf0 100644
--- a/civicrm/vendor/symfony/dependency-injection/Definition.php
+++ b/civicrm/vendor/symfony/dependency-injection/Definition.php
@@ -11,6 +11,7 @@
 
 namespace Symfony\Component\DependencyInjection;
 
+use Symfony\Component\DependencyInjection\Argument\BoundArgument;
 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
 use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException;
 
@@ -24,119 +25,95 @@ class Definition
     private $class;
     private $file;
     private $factory;
-    private $factoryClass;
-    private $factoryMethod;
-    private $factoryService;
     private $shared = true;
     private $deprecated = false;
     private $deprecationTemplate;
-    private $scope = ContainerInterface::SCOPE_CONTAINER;
-    private $properties = array();
-    private $calls = array();
+    private $properties = [];
+    private $calls = [];
+    private $instanceof = [];
+    private $autoconfigured = false;
     private $configurator;
-    private $tags = array();
+    private $tags = [];
     private $public = true;
+    private $private = true;
     private $synthetic = false;
     private $abstract = false;
-    private $synchronized = false;
     private $lazy = false;
     private $decoratedService;
     private $autowired = false;
-    private $autowiringTypes = array();
+    private $autowiringTypes = [];
+    private $changes = [];
+    private $bindings = [];
+    private $errors = [];
 
-    private static $defaultDeprecationTemplate = 'The "%service_id%" service is deprecated. You should stop using it, as it will soon be removed.';
+    protected $arguments = [];
 
-    protected $arguments;
+    private static $defaultDeprecationTemplate = 'The "%service_id%" service is deprecated. You should stop using it, as it will soon be removed.';
 
     /**
      * @param string|null $class     The service class
      * @param array       $arguments An array of arguments to pass to the service constructor
      */
-    public function __construct($class = null, array $arguments = array())
+    public function __construct($class = null, array $arguments = [])
     {
-        $this->class = $class;
-        $this->arguments = $arguments;
-    }
-
-    /**
-     * Sets a factory.
-     *
-     * @param string|array $factory A PHP function or an array containing a class/Reference and a method to call
-     *
-     * @return $this
-     */
-    public function setFactory($factory)
-    {
-        if (\is_string($factory) && false !== strpos($factory, '::')) {
-            $factory = explode('::', $factory, 2);
+        if (null !== $class) {
+            $this->setClass($class);
         }
-
-        $this->factory = $factory;
-
-        return $this;
+        $this->arguments = $arguments;
     }
 
     /**
-     * Gets the factory.
+     * Returns all changes tracked for the Definition object.
      *
-     * @return string|array|null The PHP function or an array containing a class/Reference and a method to call
+     * @return array An array of changes for this Definition
      */
-    public function getFactory()
+    public function getChanges()
     {
-        return $this->factory;
+        return $this->changes;
     }
 
     /**
-     * Sets the name of the class that acts as a factory using the factory method,
-     * which will be invoked statically.
+     * Sets the tracked changes for the Definition object.
      *
-     * @param string $factoryClass The factory class name
+     * @param array $changes An array of changes for this Definition
      *
      * @return $this
-     *
-     * @deprecated since version 2.6, to be removed in 3.0.
      */
-    public function setFactoryClass($factoryClass)
+    public function setChanges(array $changes)
     {
-        @trigger_error(sprintf('%s(%s) is deprecated since Symfony 2.6 and will be removed in 3.0. Use Definition::setFactory() instead.', __METHOD__, $factoryClass), E_USER_DEPRECATED);
-
-        $this->factoryClass = $factoryClass;
+        $this->changes = $changes;
 
         return $this;
     }
 
     /**
-     * Gets the factory class.
+     * Sets a factory.
      *
-     * @return string|null The factory class name
+     * @param string|array $factory A PHP function or an array containing a class/Reference and a method to call
      *
-     * @deprecated since version 2.6, to be removed in 3.0.
+     * @return $this
      */
-    public function getFactoryClass($triggerDeprecationError = true)
+    public function setFactory($factory)
     {
-        if ($triggerDeprecationError) {
-            @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.6 and will be removed in 3.0.', E_USER_DEPRECATED);
+        $this->changes['factory'] = true;
+
+        if (\is_string($factory) && false !== strpos($factory, '::')) {
+            $factory = explode('::', $factory, 2);
         }
 
-        return $this->factoryClass;
+        $this->factory = $factory;
+
+        return $this;
     }
 
     /**
-     * Sets the factory method able to create an instance of this class.
-     *
-     * @param string $factoryMethod The factory method name
-     *
-     * @return $this
+     * Gets the factory.
      *
-     * @deprecated since version 2.6, to be removed in 3.0.
+     * @return string|array|null The PHP function or an array containing a class/Reference and a method to call
      */
-    public function setFactoryMethod($factoryMethod)
+    public function getFactory()
     {
-        @trigger_error(sprintf('%s(%s) is deprecated since Symfony 2.6 and will be removed in 3.0. Use Definition::setFactory() instead.', __METHOD__, $factoryMethod), E_USER_DEPRECATED);
-
-        $this->factoryMethod = $factoryMethod;
-
-        return $this;
+        return $this->factory;
     }
 
     /**
@@ -153,13 +130,15 @@ class Definition
     public function setDecoratedService($id, $renamedId = null, $priority = 0)
     {
         if ($renamedId && $id === $renamedId) {
-            throw new \InvalidArgumentException(sprintf('The decorated service inner name for "%s" must be different than the service name itself.', $id));
+            throw new InvalidArgumentException(sprintf('The decorated service inner name for "%s" must be different than the service name itself.', $id));
         }
 
+        $this->changes['decorated_service'] = true;
+
         if (null === $id) {
             $this->decoratedService = null;
         } else {
-            $this->decoratedService = array($id, $renamedId, (int) $priority);
+            $this->decoratedService = [$id, $renamedId, (int) $priority];
         }
 
         return $this;
@@ -175,58 +154,6 @@ class Definition
         return $this->decoratedService;
     }
 
-    /**
-     * Gets the factory method.
-     *
-     * @return string|null The factory method name
-     *
-     * @deprecated since version 2.6, to be removed in 3.0.
-     */
-    public function getFactoryMethod($triggerDeprecationError = true)
-    {
-        if ($triggerDeprecationError) {
-            @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.6 and will be removed in 3.0.', E_USER_DEPRECATED);
-        }
-
-        return $this->factoryMethod;
-    }
-
-    /**
-     * Sets the name of the service that acts as a factory using the factory method.
-     *
-     * @param string $factoryService The factory service id
-     *
-     * @return $this
-     *
-     * @deprecated since version 2.6, to be removed in 3.0.
-     */
-    public function setFactoryService($factoryService, $triggerDeprecationError = true)
-    {
-        if ($triggerDeprecationError) {
-            @trigger_error(sprintf('%s(%s) is deprecated since Symfony 2.6 and will be removed in 3.0. Use Definition::setFactory() instead.', __METHOD__, $factoryService), E_USER_DEPRECATED);
-        }
-
-        $this->factoryService = $factoryService;
-
-        return $this;
-    }
-
-    /**
-     * Gets the factory service id.
-     *
-     * @return string|null The factory service id
-     *
-     * @deprecated since version 2.6, to be removed in 3.0.
-     */
-    public function getFactoryService($triggerDeprecationError = true)
-    {
-        if ($triggerDeprecationError) {
-            @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.6 and will be removed in 3.0.', E_USER_DEPRECATED);
-        }
-
-        return $this->factoryService;
-    }
-
     /**
      * Sets the service class.
      *
@@ -236,6 +163,8 @@ class Definition
      */
     public function setClass($class)
     {
+        $this->changes['class'] = true;
+
         $this->class = $class;
 
         return $this;
@@ -317,8 +246,8 @@ class Definition
     /**
      * Replaces a specific argument.
      *
-     * @param int   $index
-     * @param mixed $argument
+     * @param int|string $index
+     * @param mixed      $argument
      *
      * @return $this
      *
@@ -330,15 +259,34 @@ class Definition
             throw new OutOfBoundsException('Cannot replace arguments if none have been configured yet.');
         }
 
-        if ($index < 0 || $index > \count($this->arguments) - 1) {
+        if (\is_int($index) && ($index < 0 || $index > \count($this->arguments) - 1)) {
             throw new OutOfBoundsException(sprintf('The index "%d" is not in the range [0, %d].', $index, \count($this->arguments) - 1));
         }
 
+        if (!\array_key_exists($index, $this->arguments)) {
+            throw new OutOfBoundsException(sprintf('The argument "%s" doesn\'t exist.', $index));
+        }
+
         $this->arguments[$index] = $argument;
 
         return $this;
     }
 
+    /**
+     * Sets a specific argument.
+     *
+     * @param int|string $key
+     * @param mixed      $value
+     *
+     * @return $this
+     */
+    public function setArgument($key, $value)
+    {
+        $this->arguments[$key] = $value;
+
+        return $this;
+    }
+
     /**
      * Gets the arguments to pass to the service constructor/factory method.
      *
@@ -352,7 +300,7 @@ class Definition
     /**
      * Gets an argument to pass to the service constructor/factory method.
      *
-     * @param int $index
+     * @param int|string $index
      *
      * @return mixed The argument value
      *
@@ -360,8 +308,8 @@ class Definition
      */
     public function getArgument($index)
     {
-        if ($index < 0 || $index > \count($this->arguments) - 1) {
-            throw new OutOfBoundsException(sprintf('The index "%d" is not in the range [0, %d].', $index, \count($this->arguments) - 1));
+        if (!\array_key_exists($index, $this->arguments)) {
+            throw new OutOfBoundsException(sprintf('The argument "%s" doesn\'t exist.', $index));
         }
 
         return $this->arguments[$index];
@@ -372,9 +320,9 @@ class Definition
      *
      * @return $this
      */
-    public function setMethodCalls(array $calls = array())
+    public function setMethodCalls(array $calls = [])
     {
-        $this->calls = array();
+        $this->calls = [];
         foreach ($calls as $call) {
             $this->addMethodCall($call[0], $call[1]);
         }
@@ -392,12 +340,12 @@ class Definition
      *
      * @throws InvalidArgumentException on empty $method param
      */
-    public function addMethodCall($method, array $arguments = array())
+    public function addMethodCall($method, array $arguments = [])
     {
         if (empty($method)) {
             throw new InvalidArgumentException('Method name cannot be empty.');
         }
-        $this->calls[] = array($method, $arguments);
+        $this->calls[] = [$method, $arguments];
 
         return $this;
     }
@@ -449,6 +397,54 @@ class Definition
         return $this->calls;
     }
 
+    /**
+     * Sets the definition templates to conditionally apply on the current definition, keyed by parent interface/class.
+     *
+     * @param ChildDefinition[] $instanceof
+     *
+     * @return $this
+     */
+    public function setInstanceofConditionals(array $instanceof)
+    {
+        $this->instanceof = $instanceof;
+
+        return $this;
+    }
+
+    /**
+     * Gets the definition templates to conditionally apply on the current definition, keyed by parent interface/class.
+     *
+     * @return ChildDefinition[]
+     */
+    public function getInstanceofConditionals()
+    {
+        return $this->instanceof;
+    }
+
+    /**
+     * Sets whether or not instanceof conditionals should be prepended with a global set.
+     *
+     * @param bool $autoconfigured
+     *
+     * @return $this
+     */
+    public function setAutoconfigured($autoconfigured)
+    {
+        $this->changes['autoconfigured'] = true;
+
+        $this->autoconfigured = $autoconfigured;
+
+        return $this;
+    }
+
+    /**
+     * @return bool
+     */
+    public function isAutoconfigured()
+    {
+        return $this->autoconfigured;
+    }
+
     /**
      * Sets tags for this definition.
      *
@@ -480,7 +476,7 @@ class Definition
      */
     public function getTag($name)
     {
-        return isset($this->tags[$name]) ? $this->tags[$name] : array();
+        return isset($this->tags[$name]) ? $this->tags[$name] : [];
     }
 
     /**
@@ -491,7 +487,7 @@ class Definition
      *
      * @return $this
      */
-    public function addTag($name, array $attributes = array())
+    public function addTag($name, array $attributes = [])
     {
         $this->tags[$name][] = $attributes;
 
@@ -531,7 +527,7 @@ class Definition
      */
     public function clearTags()
     {
-        $this->tags = array();
+        $this->tags = [];
 
         return $this;
     }
@@ -545,6 +541,8 @@ class Definition
      */
     public function setFile($file)
     {
+        $this->changes['file'] = true;
+
         $this->file = $file;
 
         return $this;
@@ -569,6 +567,8 @@ class Definition
      */
     public function setShared($shared)
     {
+        $this->changes['shared'] = true;
+
         $this->shared = (bool) $shared;
 
         return $this;
@@ -584,46 +584,6 @@ class Definition
         return $this->shared;
     }
 
-    /**
-     * Sets the scope of the service.
-     *
-     * @param string $scope Whether the service must be shared or not
-     *
-     * @return $this
-     *
-     * @deprecated since version 2.8, to be removed in 3.0.
-     */
-    public function setScope($scope, $triggerDeprecationError = true)
-    {
-        if ($triggerDeprecationError) {
-            @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-        }
-
-        if (ContainerInterface::SCOPE_PROTOTYPE === $scope) {
-            $this->setShared(false);
-        }
-
-        $this->scope = $scope;
-
-        return $this;
-    }
-
-    /**
-     * Returns the scope of the service.
-     *
-     * @return string
-     *
-     * @deprecated since version 2.8, to be removed in 3.0.
-     */
-    public function getScope($triggerDeprecationError = true)
-    {
-        if ($triggerDeprecationError) {
-            @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-        }
-
-        return $this->scope;
-    }
-
     /**
      * Sets the visibility of this service.
      *
@@ -633,7 +593,10 @@ class Definition
      */
     public function setPublic($boolean)
     {
+        $this->changes['public'] = true;
+
         $this->public = (bool) $boolean;
+        $this->private = false;
 
         return $this;
     }
@@ -649,39 +612,32 @@ class Definition
     }
 
     /**
-     * Sets the synchronized flag of this service.
+     * Sets if this service is private.
+     *
+     * When set, the "private" state has a higher precedence than "public".
+     * In version 3.4, a "private" service always remains publicly accessible,
+     * but triggers a deprecation notice when accessed from the container,
+     * so that the service can be made really private in 4.0.
      *
      * @param bool $boolean
      *
      * @return $this
-     *
-     * @deprecated since version 2.7, will be removed in 3.0.
      */
-    public function setSynchronized($boolean, $triggerDeprecationError = true)
+    public function setPrivate($boolean)
     {
-        if ($triggerDeprecationError) {
-            @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.7 and will be removed in 3.0.', E_USER_DEPRECATED);
-        }
-
-        $this->synchronized = (bool) $boolean;
+        $this->private = (bool) $boolean;
 
         return $this;
     }
 
     /**
-     * Whether this service is synchronized.
+     * Whether this service is private.
      *
      * @return bool
-     *
-     * @deprecated since version 2.7, will be removed in 3.0.
      */
-    public function isSynchronized($triggerDeprecationError = true)
+    public function isPrivate()
     {
-        if ($triggerDeprecationError) {
-            @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.7 and will be removed in 3.0.', E_USER_DEPRECATED);
-        }
-
-        return $this->synchronized;
+        return $this->private;
     }
 
     /**
@@ -693,6 +649,8 @@ class Definition
      */
     public function setLazy($lazy)
     {
+        $this->changes['lazy'] = true;
+
         $this->lazy = (bool) $lazy;
 
         return $this;
@@ -785,6 +743,8 @@ class Definition
             $this->deprecationTemplate = $template;
         }
 
+        $this->changes['deprecated'] = true;
+
         $this->deprecated = (bool) $status;
 
         return $this;
@@ -816,13 +776,19 @@ class Definition
     /**
      * Sets a configurator to call after the service is fully initialized.
      *
-     * @param callable $callable A PHP callable
+     * @param string|array $configurator A PHP callable
      *
      * @return $this
      */
-    public function setConfigurator($callable)
+    public function setConfigurator($configurator)
     {
-        $this->configurator = $callable;
+        $this->changes['configurator'] = true;
+
+        if (\is_string($configurator) && false !== strpos($configurator, '::')) {
+            $configurator = explode('::', $configurator, 2);
+        }
+
+        $this->configurator = $configurator;
 
         return $this;
     }
@@ -830,7 +796,7 @@ class Definition
     /**
      * Gets the configurator to call after the service is fully initialized.
      *
-     * @return callable|null The PHP callable to call
+     * @return callable|array|null
      */
     public function getConfigurator()
     {
@@ -843,10 +809,14 @@ class Definition
      * @param string[] $types
      *
      * @return $this
+     *
+     * @deprecated since version 3.3, to be removed in 4.0.
      */
     public function setAutowiringTypes(array $types)
     {
-        $this->autowiringTypes = array();
+        @trigger_error('Autowiring-types are deprecated since Symfony 3.3 and will be removed in 4.0. Use aliases instead.', E_USER_DEPRECATED);
+
+        $this->autowiringTypes = [];
 
         foreach ($types as $type) {
             $this->autowiringTypes[$type] = true;
@@ -874,7 +844,9 @@ class Definition
      */
     public function setAutowired($autowired)
     {
-        $this->autowired = $autowired;
+        $this->changes['autowired'] = true;
+
+        $this->autowired = (bool) $autowired;
 
         return $this;
     }
@@ -883,9 +855,15 @@ class Definition
      * Gets autowiring types that will default to this definition.
      *
      * @return string[]
+     *
+     * @deprecated since version 3.3, to be removed in 4.0.
      */
-    public function getAutowiringTypes()
+    public function getAutowiringTypes(/*$triggerDeprecation = true*/)
     {
+        if (1 > \func_num_args() || func_get_arg(0)) {
+            @trigger_error('Autowiring-types are deprecated since Symfony 3.3 and will be removed in 4.0. Use aliases instead.', E_USER_DEPRECATED);
+        }
+
         return array_keys($this->autowiringTypes);
     }
 
@@ -895,9 +873,13 @@ class Definition
      * @param string $type
      *
      * @return $this
+     *
+     * @deprecated since version 3.3, to be removed in 4.0.
      */
     public function addAutowiringType($type)
     {
+        @trigger_error(sprintf('Autowiring-types are deprecated since Symfony 3.3 and will be removed in 4.0. Use aliases instead for "%s".', $type), E_USER_DEPRECATED);
+
         $this->autowiringTypes[$type] = true;
 
         return $this;
@@ -909,9 +891,13 @@ class Definition
      * @param string $type
      *
      * @return $this
+     *
+     * @deprecated since version 3.3, to be removed in 4.0.
      */
     public function removeAutowiringType($type)
     {
+        @trigger_error(sprintf('Autowiring-types are deprecated since Symfony 3.3 and will be removed in 4.0. Use aliases instead for "%s".', $type), E_USER_DEPRECATED);
+
         unset($this->autowiringTypes[$type]);
 
         return $this;
@@ -923,9 +909,65 @@ class Definition
      * @param string $type
      *
      * @return bool
+     *
+     * @deprecated since version 3.3, to be removed in 4.0.
      */
     public function hasAutowiringType($type)
     {
+        @trigger_error(sprintf('Autowiring-types are deprecated since Symfony 3.3 and will be removed in 4.0. Use aliases instead for "%s".', $type), E_USER_DEPRECATED);
+
         return isset($this->autowiringTypes[$type]);
     }
+
+    /**
+     * Gets bindings.
+     *
+     * @return array
+     */
+    public function getBindings()
+    {
+        return $this->bindings;
+    }
+
+    /**
+     * Sets bindings.
+     *
+     * Bindings map $named or FQCN arguments to values that should be
+     * injected in the matching parameters (of the constructor, of methods
+     * called and of controller actions).
+     *
+     * @return $this
+     */
+    public function setBindings(array $bindings)
+    {
+        foreach ($bindings as $key => $binding) {
+            if (!$binding instanceof BoundArgument) {
+                $bindings[$key] = new BoundArgument($binding);
+            }
+        }
+
+        $this->bindings = $bindings;
+
+        return $this;
+    }
+
+    /**
+     * Add an error that occurred when building this Definition.
+     *
+     * @param string $error
+     */
+    public function addError($error)
+    {
+        $this->errors[] = $error;
+    }
+
+    /**
+     * Returns any errors that occurred while building this Definition.
+     *
+     * @return array
+     */
+    public function getErrors()
+    {
+        return $this->errors;
+    }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/DefinitionDecorator.php b/civicrm/vendor/symfony/dependency-injection/DefinitionDecorator.php
index 44e9c0f7d4653a0905903f7cb2b16c683128d91b..99af39e89dbfd2b9aecb2cbc1b7cce3534c35264 100644
--- a/civicrm/vendor/symfony/dependency-injection/DefinitionDecorator.php
+++ b/civicrm/vendor/symfony/dependency-injection/DefinitionDecorator.php
@@ -11,219 +11,19 @@
 
 namespace Symfony\Component\DependencyInjection;
 
-use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
-use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException;
+@trigger_error('The '.__NAMESPACE__.'\DefinitionDecorator class is deprecated since Symfony 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead.', E_USER_DEPRECATED);
 
-/**
- * This definition decorates another definition.
- *
- * @author Johannes M. Schmitt <schmittjoh@gmail.com>
- */
-class DefinitionDecorator extends Definition
-{
-    private $parent;
-    private $changes = array();
-
-    /**
-     * @param string $parent The id of Definition instance to decorate
-     */
-    public function __construct($parent)
-    {
-        parent::__construct();
-
-        $this->parent = $parent;
-    }
-
-    /**
-     * Returns the Definition being decorated.
-     *
-     * @return string
-     */
-    public function getParent()
-    {
-        return $this->parent;
-    }
-
-    /**
-     * Returns all changes tracked for the Definition object.
-     *
-     * @return array An array of changes for this Definition
-     */
-    public function getChanges()
-    {
-        return $this->changes;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setClass($class)
-    {
-        $this->changes['class'] = true;
-
-        return parent::setClass($class);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setFactory($callable)
-    {
-        $this->changes['factory'] = true;
-
-        return parent::setFactory($callable);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setFactoryClass($class)
-    {
-        $this->changes['factory_class'] = true;
-
-        return parent::setFactoryClass($class);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setFactoryMethod($method)
-    {
-        $this->changes['factory_method'] = true;
-
-        return parent::setFactoryMethod($method);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setFactoryService($service, $triggerDeprecationError = true)
-    {
-        $this->changes['factory_service'] = true;
-
-        return parent::setFactoryService($service, $triggerDeprecationError);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setConfigurator($callable)
-    {
-        $this->changes['configurator'] = true;
-
-        return parent::setConfigurator($callable);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setFile($file)
-    {
-        $this->changes['file'] = true;
-
-        return parent::setFile($file);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setPublic($boolean)
-    {
-        $this->changes['public'] = true;
-
-        return parent::setPublic($boolean);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setLazy($boolean)
-    {
-        $this->changes['lazy'] = true;
-
-        return parent::setLazy($boolean);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setDecoratedService($id, $renamedId = null, $priority = 0)
-    {
-        $this->changes['decorated_service'] = true;
-
-        return parent::setDecoratedService($id, $renamedId, $priority);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setDeprecated($boolean = true, $template = null)
-    {
-        $this->changes['deprecated'] = true;
-
-        return parent::setDeprecated($boolean, $template);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setAutowired($autowired)
-    {
-        $this->changes['autowire'] = true;
-
-        return parent::setAutowired($autowired);
-    }
+class_exists(ChildDefinition::class);
 
+if (false) {
     /**
-     * Gets an argument to pass to the service constructor/factory method.
+     * This definition decorates another definition.
      *
-     * If replaceArgument() has been used to replace an argument, this method
-     * will return the replacement value.
+     * @author Johannes M. Schmitt <schmittjoh@gmail.com>
      *
-     * @param int $index
-     *
-     * @return mixed The argument value
-     *
-     * @throws OutOfBoundsException When the argument does not exist
+     * @deprecated The DefinitionDecorator class is deprecated since version 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead.
      */
-    public function getArgument($index)
+    class DefinitionDecorator extends Definition
     {
-        if (array_key_exists('index_'.$index, $this->arguments)) {
-            return $this->arguments['index_'.$index];
-        }
-
-        $lastIndex = \count(array_filter(array_keys($this->arguments), 'is_int')) - 1;
-
-        if ($index < 0 || $index > $lastIndex) {
-            throw new OutOfBoundsException(sprintf('The index "%d" is not in the range [0, %d].', $index, $lastIndex));
-        }
-
-        return $this->arguments[$index];
-    }
-
-    /**
-     * You should always use this method when overwriting existing arguments
-     * of the parent definition.
-     *
-     * If you directly call setArguments() keep in mind that you must follow
-     * certain conventions when you want to overwrite the arguments of the
-     * parent definition, otherwise your arguments will only be appended.
-     *
-     * @param int   $index
-     * @param mixed $value
-     *
-     * @return $this
-     *
-     * @throws InvalidArgumentException when $index isn't an integer
-     */
-    public function replaceArgument($index, $value)
-    {
-        if (!\is_int($index)) {
-            throw new InvalidArgumentException('$index must be an integer.');
-        }
-
-        $this->arguments['index_'.$index] = $value;
-
-        return $this;
     }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Dumper/DumperInterface.php b/civicrm/vendor/symfony/dependency-injection/Dumper/DumperInterface.php
index dd001e4ed080c4441bf25b3af568b325ba0088b2..8abc19250f70b62929f5b026c02036bc3decc3be 100644
--- a/civicrm/vendor/symfony/dependency-injection/Dumper/DumperInterface.php
+++ b/civicrm/vendor/symfony/dependency-injection/Dumper/DumperInterface.php
@@ -21,9 +21,7 @@ interface DumperInterface
     /**
      * Dumps the service container.
      *
-     * @param array $options An array of options
-     *
-     * @return string The representation of the service container
+     * @return string|array The representation of the service container
      */
-    public function dump(array $options = array());
+    public function dump(array $options = []);
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Dumper/GraphvizDumper.php b/civicrm/vendor/symfony/dependency-injection/Dumper/GraphvizDumper.php
index dfb0dee19325f79c46631f8173230cffe1920bbc..0591e024f5753c855e71e128001e35b00d35f5a6 100644
--- a/civicrm/vendor/symfony/dependency-injection/Dumper/GraphvizDumper.php
+++ b/civicrm/vendor/symfony/dependency-injection/Dumper/GraphvizDumper.php
@@ -11,14 +11,13 @@
 
 namespace Symfony\Component\DependencyInjection\Dumper;
 
+use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\DependencyInjection\Definition;
 use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
 use Symfony\Component\DependencyInjection\Parameter;
 use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
 use Symfony\Component\DependencyInjection\Reference;
-use Symfony\Component\DependencyInjection\Scope;
 
 /**
  * GraphvizDumper dumps a service container as a graphviz file.
@@ -33,14 +32,15 @@ class GraphvizDumper extends Dumper
 {
     private $nodes;
     private $edges;
-    private $options = array(
-            'graph' => array('ratio' => 'compress'),
-            'node' => array('fontsize' => 11, 'fontname' => 'Arial', 'shape' => 'record'),
-            'edge' => array('fontsize' => 9, 'fontname' => 'Arial', 'color' => 'grey', 'arrowhead' => 'open', 'arrowsize' => 0.5),
-            'node.instance' => array('fillcolor' => '#9999ff', 'style' => 'filled'),
-            'node.definition' => array('fillcolor' => '#eeeeee'),
-            'node.missing' => array('fillcolor' => '#ff9999', 'style' => 'filled'),
-        );
+    // All values should be strings
+    private $options = [
+            'graph' => ['ratio' => 'compress'],
+            'node' => ['fontsize' => '11', 'fontname' => 'Arial', 'shape' => 'record'],
+            'edge' => ['fontsize' => '9', 'fontname' => 'Arial', 'color' => 'grey', 'arrowhead' => 'open', 'arrowsize' => '0.5'],
+            'node.instance' => ['fillcolor' => '#9999ff', 'style' => 'filled'],
+            'node.definition' => ['fillcolor' => '#eeeeee'],
+            'node.missing' => ['fillcolor' => '#ff9999', 'style' => 'filled'],
+        ];
 
     /**
      * Dumps the service container as a graphviz graph.
@@ -56,9 +56,9 @@ class GraphvizDumper extends Dumper
      *
      * @return string The dot representation of the service container
      */
-    public function dump(array $options = array())
+    public function dump(array $options = [])
     {
-        foreach (array('graph', 'node', 'edge', 'node.instance', 'node.definition', 'node.missing') as $key) {
+        foreach (['graph', 'node', 'edge', 'node.instance', 'node.definition', 'node.missing'] as $key) {
             if (isset($options[$key])) {
                 $this->options[$key] = array_merge($this->options[$key], $options[$key]);
             }
@@ -66,7 +66,7 @@ class GraphvizDumper extends Dumper
 
         $this->nodes = $this->findNodes();
 
-        $this->edges = array();
+        $this->edges = [];
         foreach ($this->container->getDefinitions() as $id => $definition) {
             $this->edges[$id] = array_merge(
                 $this->findEdges($id, $definition->getArguments(), true, ''),
@@ -81,7 +81,7 @@ class GraphvizDumper extends Dumper
             }
         }
 
-        return $this->startDot().$this->addNodes().$this->addEdges().$this->endDot();
+        return $this->container->resolveEnvPlaceholders($this->startDot().$this->addNodes().$this->addEdges().$this->endDot(), '__ENV_%s__');
     }
 
     /**
@@ -111,7 +111,7 @@ class GraphvizDumper extends Dumper
         $code = '';
         foreach ($this->edges as $id => $edges) {
             foreach ($edges as $edge) {
-                $code .= sprintf("  node_%s -> node_%s [label=\"%s\" style=\"%s\"];\n", $this->dotize($id), $this->dotize($edge['to']), $edge['name'], $edge['required'] ? 'filled' : 'dashed');
+                $code .= sprintf("  node_%s -> node_%s [label=\"%s\" style=\"%s\"%s];\n", $this->dotize($id), $this->dotize($edge['to']), $edge['name'], $edge['required'] ? 'filled' : 'dashed', $edge['lazy'] ? ' color="#9999ff"' : '');
             }
         }
 
@@ -128,9 +128,9 @@ class GraphvizDumper extends Dumper
      *
      * @return array An array of edges
      */
-    private function findEdges($id, array $arguments, $required, $name)
+    private function findEdges($id, array $arguments, $required, $name, $lazy = false)
     {
-        $edges = array();
+        $edges = [];
         foreach ($arguments as $argument) {
             if ($argument instanceof Parameter) {
                 $argument = $this->container->hasParameter($argument) ? $this->container->getParameter($argument) : null;
@@ -139,13 +139,27 @@ class GraphvizDumper extends Dumper
             }
 
             if ($argument instanceof Reference) {
+                $lazyEdge = $lazy;
+
                 if (!$this->container->has((string) $argument)) {
-                    $this->nodes[(string) $argument] = array('name' => $name, 'required' => $required, 'class' => '', 'attributes' => $this->options['node.missing']);
+                    $this->nodes[(string) $argument] = ['name' => $name, 'required' => $required, 'class' => '', 'attributes' => $this->options['node.missing']];
+                } elseif ('service_container' !== (string) $argument) {
+                    $lazyEdge = $lazy || $this->container->getDefinition((string) $argument)->isLazy();
                 }
 
-                $edges[] = array('name' => $name, 'required' => $required, 'to' => $argument);
+                $edges[] = ['name' => $name, 'required' => $required, 'to' => $argument, 'lazy' => $lazyEdge];
+            } elseif ($argument instanceof ArgumentInterface) {
+                $edges = array_merge($edges, $this->findEdges($id, $argument->getValues(), $required, $name, true));
+            } elseif ($argument instanceof Definition) {
+                $edges = array_merge($edges,
+                    $this->findEdges($id, $argument->getArguments(), $required, ''),
+                    $this->findEdges($id, $argument->getProperties(), false, '')
+                );
+                foreach ($argument->getMethodCalls() as $call) {
+                    $edges = array_merge($edges, $this->findEdges($id, $call[1], false, $call[0].'()'));
+                }
             } elseif (\is_array($argument)) {
-                $edges = array_merge($edges, $this->findEdges($id, $argument, $required, $name));
+                $edges = array_merge($edges, $this->findEdges($id, $argument, $required, $name, $lazy));
             }
         }
 
@@ -159,7 +173,7 @@ class GraphvizDumper extends Dumper
      */
     private function findNodes()
     {
-        $nodes = array();
+        $nodes = [];
 
         $container = $this->cloneContainer();
 
@@ -175,20 +189,17 @@ class GraphvizDumper extends Dumper
             } catch (ParameterNotFoundException $e) {
             }
 
-            $nodes[$id] = array('class' => str_replace('\\', '\\\\', $class), 'attributes' => array_merge($this->options['node.definition'], array('style' => $definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE !== $definition->getScope(false) ? 'filled' : 'dotted')));
+            $nodes[$id] = ['class' => str_replace('\\', '\\\\', $class), 'attributes' => array_merge($this->options['node.definition'], ['style' => $definition->isShared() ? 'filled' : 'dotted'])];
             $container->setDefinition($id, new Definition('stdClass'));
         }
 
         foreach ($container->getServiceIds() as $id) {
-            $service = $container->get($id);
-
-            if (array_key_exists($id, $container->getAliases())) {
+            if (\array_key_exists($id, $container->getAliases())) {
                 continue;
             }
 
             if (!$container->hasDefinition($id)) {
-                $class = ('service_container' === $id) ? \get_class($this->container) : \get_class($service);
-                $nodes[$id] = array('class' => str_replace('\\', '\\\\', $class), 'attributes' => $this->options['node.instance']);
+                $nodes[$id] = ['class' => str_replace('\\', '\\\\', \get_class($container->get($id))), 'attributes' => $this->options['node.instance']];
             }
         }
 
@@ -203,9 +214,6 @@ class GraphvizDumper extends Dumper
         $container->setDefinitions($this->container->getDefinitions());
         $container->setAliases($this->container->getAliases());
         $container->setResources($this->container->getResources());
-        foreach ($this->container->getScopes(false) as $scope => $parentScope) {
-            $container->addScope(new Scope($scope, $parentScope));
-        }
         foreach ($this->container->getExtensions() as $extension) {
             $container->registerExtension($extension);
         }
@@ -246,7 +254,7 @@ class GraphvizDumper extends Dumper
      */
     private function addAttributes(array $attributes)
     {
-        $code = array();
+        $code = [];
         foreach ($attributes as $k => $v) {
             $code[] = sprintf('%s="%s"', $k, $v);
         }
@@ -263,7 +271,7 @@ class GraphvizDumper extends Dumper
      */
     private function addOptions(array $options)
     {
-        $code = array();
+        $code = [];
         foreach ($options as $k => $v) {
             $code[] = sprintf('%s="%s"', $k, $v);
         }
@@ -292,7 +300,7 @@ class GraphvizDumper extends Dumper
      */
     private function getAliases($id)
     {
-        $aliases = array();
+        $aliases = [];
         foreach ($this->container->getAliases() as $alias => $origin) {
             if ($id == $origin) {
                 $aliases[] = $alias;
diff --git a/civicrm/vendor/symfony/dependency-injection/Dumper/PhpDumper.php b/civicrm/vendor/symfony/dependency-injection/Dumper/PhpDumper.php
index 2f530e9e3e4ed7146ea39c0d562ab860d988aaae..3bfa0c318891eb8752bd1e0e1bd52b7a3dd06a19 100644
--- a/civicrm/vendor/symfony/dependency-injection/Dumper/PhpDumper.php
+++ b/civicrm/vendor/symfony/dependency-injection/Dumper/PhpDumper.php
@@ -11,10 +11,16 @@
 
 namespace Symfony\Component\DependencyInjection\Dumper;
 
+use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
+use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
+use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
+use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass;
+use Symfony\Component\DependencyInjection\Compiler\CheckCircularReferencesPass;
 use Symfony\Component\DependencyInjection\Container;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Exception\EnvParameterException;
 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
 use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
@@ -23,9 +29,9 @@ use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface as
 use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper;
 use Symfony\Component\DependencyInjection\Parameter;
 use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\TypedReference;
 use Symfony\Component\DependencyInjection\Variable;
 use Symfony\Component\ExpressionLanguage\Expression;
-use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
 use Symfony\Component\HttpKernel\Kernel;
 
 /**
@@ -46,20 +52,24 @@ class PhpDumper extends Dumper
      */
     const NON_FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_';
 
-    private $inlinedDefinitions;
     private $definitionVariables;
     private $referenceVariables;
     private $variableCount;
-    private $reservedVariables = array('instance', 'class');
+    private $inlinedDefinitions;
+    private $serviceCalls;
+    private $reservedVariables = ['instance', 'class', 'this'];
     private $expressionLanguage;
     private $targetDirRegex;
     private $targetDirMaxMatches;
     private $docStar;
-
-    /**
-     * @var ExpressionFunctionProviderInterface[]
-     */
-    private $expressionLanguageProviders = array();
+    private $serviceIdToMethodNameMap;
+    private $usedMethodNames;
+    private $namespace;
+    private $asFiles;
+    private $hotPathTag;
+    private $inlineRequires;
+    private $inlinedRequires = [];
+    private $circularReferences = [];
 
     /**
      * @var ProxyDumper
@@ -71,9 +81,11 @@ class PhpDumper extends Dumper
      */
     public function __construct(ContainerBuilder $container)
     {
-        parent::__construct($container);
+        if (!$container->isCompiled()) {
+            @trigger_error('Dumping an uncompiled ContainerBuilder is deprecated since Symfony 3.3 and will not be supported anymore in 4.0. Compile the container beforehand.', E_USER_DEPRECATED);
+        }
 
-        $this->inlinedDefinitions = new \SplObjectStorage();
+        parent::__construct($container);
     }
 
     /**
@@ -92,31 +104,83 @@ class PhpDumper extends Dumper
      *  * class:      The class name
      *  * base_class: The base class name
      *  * namespace:  The class namespace
+     *  * as_files:   To split the container in several files
+     *
+     * @return string|array A PHP class representing the service container or an array of PHP files if the "as_files" option is set
      *
-     * @return string A PHP class representing of the service container
+     * @throws EnvParameterException When an env var exists but has not been dumped
      */
-    public function dump(array $options = array())
+    public function dump(array $options = [])
     {
         $this->targetDirRegex = null;
-        $options = array_merge(array(
+        $this->inlinedRequires = [];
+        $options = array_merge([
             'class' => 'ProjectServiceContainer',
             'base_class' => 'Container',
             'namespace' => '',
+            'as_files' => false,
             'debug' => true,
-        ), $options);
+            'hot_path_tag' => 'container.hot_path',
+            'inline_class_loader_parameter' => 'container.dumper.inline_class_loader',
+            'build_time' => time(),
+        ], $options);
+
+        $this->namespace = $options['namespace'];
+        $this->asFiles = $options['as_files'];
+        $this->hotPathTag = $options['hot_path_tag'];
+        $this->inlineRequires = $options['inline_class_loader_parameter'] && $this->container->hasParameter($options['inline_class_loader_parameter']) && $this->container->getParameter($options['inline_class_loader_parameter']);
+
+        if (0 !== strpos($baseClass = $options['base_class'], '\\') && 'Container' !== $baseClass) {
+            $baseClass = sprintf('%s\%s', $options['namespace'] ? '\\'.$options['namespace'] : '', $baseClass);
+            $baseClassWithNamespace = $baseClass;
+        } elseif ('Container' === $baseClass) {
+            $baseClassWithNamespace = Container::class;
+        } else {
+            $baseClassWithNamespace = $baseClass;
+        }
+
+        $this->initializeMethodNamesMap('Container' === $baseClass ? Container::class : $baseClass);
+
+        if ($this->getProxyDumper() instanceof NullDumper) {
+            (new AnalyzeServiceReferencesPass(true, false))->process($this->container);
+            try {
+                (new CheckCircularReferencesPass())->process($this->container);
+            } catch (ServiceCircularReferenceException $e) {
+                $path = $e->getPath();
+                end($path);
+                $path[key($path)] .= '". Try running "composer require symfony/proxy-manager-bridge';
+
+                throw new ServiceCircularReferenceException($e->getServiceId(), $path);
+            }
+        }
+
+        (new AnalyzeServiceReferencesPass(false, !$this->getProxyDumper() instanceof NullDumper))->process($this->container);
+        $checkedNodes = [];
+        $this->circularReferences = [];
+        foreach ($this->container->getCompiler()->getServiceReferenceGraph()->getNodes() as $id => $node) {
+            if (!$node->getValue() instanceof Definition) {
+                continue;
+            }
+            if (!isset($checkedNodes[$id])) {
+                $this->analyzeCircularReferences($id, $node->getOutEdges(), $checkedNodes);
+            }
+        }
+        $this->container->getCompiler()->getServiceReferenceGraph()->clear();
+        $checkedNodes = [];
+
         $this->docStar = $options['debug'] ? '*' : '';
 
         if (!empty($options['file']) && is_dir($dir = \dirname($options['file']))) {
             // Build a regexp where the first root dirs are mandatory,
             // but every other sub-dir is optional up to the full path in $dir
-            // Mandate at least 2 root dirs and not more that 5 optional dirs.
+            // Mandate at least 1 root dir and not more than 5 optional dirs.
 
             $dir = explode(\DIRECTORY_SEPARATOR, realpath($dir));
             $i = \count($dir);
 
-            if (3 <= $i) {
+            if (2 + (int) ('\\' === \DIRECTORY_SEPARATOR) <= $i) {
                 $regex = '';
-                $lastOptionalDir = $i > 8 ? $i - 5 : 3;
+                $lastOptionalDir = $i > 8 ? $i - 5 : (2 + (int) ('\\' === \DIRECTORY_SEPARATOR));
                 $this->targetDirMaxMatches = $i - $lastOptionalDir;
 
                 while (--$i >= $lastOptionalDir) {
@@ -131,23 +195,95 @@ class PhpDumper extends Dumper
             }
         }
 
-        $code = $this->startClass($options['class'], $options['base_class'], $options['namespace']);
+        $code =
+            $this->startClass($options['class'], $baseClass, $baseClassWithNamespace).
+            $this->addServices().
+            $this->addDefaultParametersMethod().
+            $this->endClass()
+        ;
+
+        if ($this->asFiles) {
+            $fileStart = <<<EOF
+<?php
+
+use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
+
+// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
+
+EOF;
+            $files = [];
+
+            if ($ids = array_keys($this->container->getRemovedIds())) {
+                sort($ids);
+                $c = "<?php\n\nreturn [\n";
+                foreach ($ids as $id) {
+                    $c .= '    '.$this->doExport($id)." => true,\n";
+                }
+                $files['removed-ids.php'] = $c."];\n";
+            }
+
+            foreach ($this->generateServiceFiles() as $file => $c) {
+                $files[$file] = $fileStart.$c;
+            }
+            foreach ($this->generateProxyClasses() as $file => $c) {
+                $files[$file] = "<?php\n".$c;
+            }
+            $files[$options['class'].'.php'] = $code;
+            $hash = ucfirst(strtr(ContainerBuilder::hash($files), '._', 'xx'));
+            $code = [];
+
+            foreach ($files as $file => $c) {
+                $code["Container{$hash}/{$file}"] = $c;
+            }
+            array_pop($code);
+            $code["Container{$hash}/{$options['class']}.php"] = substr_replace($files[$options['class'].'.php'], "<?php\n\nnamespace Container{$hash};\n", 0, 6);
+            $namespaceLine = $this->namespace ? "\nnamespace {$this->namespace};\n" : '';
+            $time = $options['build_time'];
+            $id = hash('crc32', $hash.$time);
 
-        if ($this->container->isFrozen()) {
-            $code .= $this->addFrozenConstructor();
-            $code .= $this->addFrozenCompile();
-            $code .= $this->addIsFrozenMethod();
+            $code[$options['class'].'.php'] = <<<EOF
+<?php
+{$namespaceLine}
+// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
+
+if (\\class_exists(\\Container{$hash}\\{$options['class']}::class, false)) {
+    // no-op
+} elseif (!include __DIR__.'/Container{$hash}/{$options['class']}.php') {
+    touch(__DIR__.'/Container{$hash}.legacy');
+
+    return;
+}
+
+if (!\\class_exists({$options['class']}::class, false)) {
+    \\class_alias(\\Container{$hash}\\{$options['class']}::class, {$options['class']}::class, false);
+}
+
+return new \\Container{$hash}\\{$options['class']}([
+    'container.build_hash' => '$hash',
+    'container.build_id' => '$id',
+    'container.build_time' => $time,
+], __DIR__.\\DIRECTORY_SEPARATOR.'Container{$hash}');
+
+EOF;
         } else {
-            $code .= $this->addConstructor();
+            foreach ($this->generateProxyClasses() as $c) {
+                $code .= $c;
+            }
         }
 
-        $code .=
-            $this->addServices().
-            $this->addDefaultParametersMethod().
-            $this->endClass().
-            $this->addProxyClasses()
-        ;
         $this->targetDirRegex = null;
+        $this->inlinedRequires = [];
+        $this->circularReferences = [];
+
+        $unusedEnvs = [];
+        foreach ($this->container->getEnvCounters() as $env => $use) {
+            if (!$use) {
+                $unusedEnvs[] = $env;
+            }
+        }
+        if ($unusedEnvs) {
+            throw new EnvParameterException($unusedEnvs, null, 'Environment variables "%s" are never used. Please, check your container\'s configuration.');
+        }
 
         return $code;
     }
@@ -166,84 +302,123 @@ class PhpDumper extends Dumper
         return $this->proxyDumper;
     }
 
-    /**
-     * Generates Service local temp variables.
-     *
-     * @param string $cId
-     * @param string $definition
-     *
-     * @return string
-     */
-    private function addServiceLocalTempVariables($cId, $definition)
+    private function analyzeCircularReferences($sourceId, array $edges, &$checkedNodes, &$currentPath = [], $byConstructor = true)
     {
-        static $template = "        \$%s = %s;\n";
+        $checkedNodes[$sourceId] = true;
+        $currentPath[$sourceId] = $byConstructor;
+
+        foreach ($edges as $edge) {
+            $node = $edge->getDestNode();
+            $id = $node->getId();
+
+            if (!$node->getValue() instanceof Definition || $sourceId === $id || $edge->isLazy() || $edge->isWeak()) {
+                // no-op
+            } elseif (isset($currentPath[$id])) {
+                $this->addCircularReferences($id, $currentPath, $edge->isReferencedByConstructor());
+            } elseif (!isset($checkedNodes[$id])) {
+                $this->analyzeCircularReferences($id, $node->getOutEdges(), $checkedNodes, $currentPath, $edge->isReferencedByConstructor());
+            } elseif (isset($this->circularReferences[$id])) {
+                $this->connectCircularReferences($id, $currentPath, $edge->isReferencedByConstructor());
+            }
+        }
+        unset($currentPath[$sourceId]);
+    }
 
-        $localDefinitions = array_merge(
-            array($definition),
-            $this->getInlinedDefinitions($definition)
-        );
+    private function connectCircularReferences($sourceId, &$currentPath, $byConstructor, &$subPath = [])
+    {
+        $currentPath[$sourceId] = $subPath[$sourceId] = $byConstructor;
 
-        $calls = $behavior = array();
-        foreach ($localDefinitions as $iDefinition) {
-            $this->getServiceCallsFromArguments($iDefinition->getArguments(), $calls, $behavior);
-            $this->getServiceCallsFromArguments($iDefinition->getMethodCalls(), $calls, $behavior);
-            $this->getServiceCallsFromArguments($iDefinition->getProperties(), $calls, $behavior);
-            $this->getServiceCallsFromArguments(array($iDefinition->getConfigurator()), $calls, $behavior);
-            $this->getServiceCallsFromArguments(array($iDefinition->getFactory()), $calls, $behavior);
+        foreach ($this->circularReferences[$sourceId] as $id => $byConstructor) {
+            if (isset($currentPath[$id])) {
+                $this->addCircularReferences($id, $currentPath, $byConstructor);
+            } elseif (!isset($subPath[$id]) && isset($this->circularReferences[$id])) {
+                $this->connectCircularReferences($id, $currentPath, $byConstructor, $subPath);
+            }
         }
+        unset($currentPath[$sourceId], $subPath[$sourceId]);
+    }
 
-        $code = '';
-        foreach ($calls as $id => $callCount) {
-            if ('service_container' === $id || $id === $cId) {
-                continue;
-            }
+    private function addCircularReferences($id, $currentPath, $byConstructor)
+    {
+        $currentPath[$id] = $byConstructor;
+        $circularRefs = [];
 
-            if ($callCount > 1) {
-                $name = $this->getNextVariableName();
-                $this->referenceVariables[$id] = new Variable($name);
+        foreach (array_reverse($currentPath) as $parentId => $v) {
+            $byConstructor = $byConstructor && $v;
+            $circularRefs[] = $parentId;
 
-                if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $behavior[$id]) {
-                    $code .= sprintf($template, $name, $this->getServiceCall($id));
-                } else {
-                    $code .= sprintf($template, $name, $this->getServiceCall($id, new Reference($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)));
-                }
+            if ($parentId === $id) {
+                break;
             }
         }
 
-        if ('' !== $code) {
-            $code .= "\n";
+        $currentId = $id;
+        foreach ($circularRefs as $parentId) {
+            if (empty($this->circularReferences[$parentId][$currentId])) {
+                $this->circularReferences[$parentId][$currentId] = $byConstructor;
+            }
+
+            $currentId = $parentId;
         }
+    }
 
-        return $code;
+    private function collectLineage($class, array &$lineage)
+    {
+        if (isset($lineage[$class])) {
+            return;
+        }
+        if (!$r = $this->container->getReflectionClass($class, false)) {
+            return;
+        }
+        if ($this->container instanceof $class) {
+            return;
+        }
+        $file = $r->getFileName();
+        if (!$file || $this->doExport($file) === $exportedFile = $this->export($file)) {
+            return;
+        }
+
+        if ($parent = $r->getParentClass()) {
+            $this->collectLineage($parent->name, $lineage);
+        }
+
+        foreach ($r->getInterfaces() as $parent) {
+            $this->collectLineage($parent->name, $lineage);
+        }
+
+        foreach ($r->getTraits() as $parent) {
+            $this->collectLineage($parent->name, $lineage);
+        }
+
+        $lineage[$class] = substr($exportedFile, 1, -1);
     }
 
-    /**
-     * Generates code for the proxies to be attached after the container class.
-     *
-     * @return string
-     */
-    private function addProxyClasses()
+    private function generateProxyClasses()
     {
-        /* @var $definitions Definition[] */
-        $definitions = array_filter(
-            $this->container->getDefinitions(),
-            array($this->getProxyDumper(), 'isProxyCandidate')
-        );
-        $code = '';
+        $alreadyGenerated = [];
+        $definitions = $this->container->getDefinitions();
         $strip = '' === $this->docStar && method_exists('Symfony\Component\HttpKernel\Kernel', 'stripComments');
-
+        $proxyDumper = $this->getProxyDumper();
+        ksort($definitions);
         foreach ($definitions as $definition) {
-            if ("\n" === $proxyCode = "\n".$this->getProxyDumper()->getProxyCode($definition)) {
+            if (!$proxyDumper->isProxyCandidate($definition)) {
+                continue;
+            }
+            if (isset($alreadyGenerated[$class = $definition->getClass()])) {
+                continue;
+            }
+            $alreadyGenerated[$class] = true;
+            // register class' reflector for resource tracking
+            $this->container->getReflectionClass($class);
+            if ("\n" === $proxyCode = "\n".$proxyDumper->getProxyCode($definition)) {
                 continue;
             }
             if ($strip) {
                 $proxyCode = "<?php\n".$proxyCode;
                 $proxyCode = substr(Kernel::stripComments($proxyCode), 5);
             }
-            $code .= $proxyCode;
+            yield sprintf('%s.php', explode(' ', $proxyCode, 3)[1]) => $proxyCode;
         }
-
-        return $code;
     }
 
     /**
@@ -251,173 +426,121 @@ class PhpDumper extends Dumper
      *
      * @return string
      */
-    private function addServiceInclude(Definition $definition)
+    private function addServiceInclude($cId, Definition $definition)
     {
-        $template = "        require_once %s;\n";
         $code = '';
 
-        if (null !== $file = $definition->getFile()) {
-            $code .= sprintf($template, $this->dumpValue($file));
-        }
-
-        foreach ($this->getInlinedDefinitions($definition) as $definition) {
-            if (null !== $file = $definition->getFile()) {
-                $code .= sprintf($template, $this->dumpValue($file));
+        if ($this->inlineRequires && !$this->isHotPath($definition)) {
+            $lineage = [];
+            foreach ($this->inlinedDefinitions as $def) {
+                if (!$def->isDeprecated() && \is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())) {
+                    $this->collectLineage($class, $lineage);
+                }
             }
-        }
-
-        if ('' !== $code) {
-            $code .= "\n";
-        }
 
-        return $code;
-    }
-
-    /**
-     * Generates the inline definition of a service.
-     *
-     * @param string     $id
-     * @param Definition $definition
-     *
-     * @return string
-     *
-     * @throws RuntimeException                  When the factory definition is incomplete
-     * @throws ServiceCircularReferenceException When a circular reference is detected
-     */
-    private function addServiceInlinedDefinitions($id, Definition $definition)
-    {
-        $code = '';
-        $variableMap = $this->definitionVariables;
-        $nbOccurrences = new \SplObjectStorage();
-        $processed = new \SplObjectStorage();
-        $inlinedDefinitions = $this->getInlinedDefinitions($definition);
-
-        foreach ($inlinedDefinitions as $definition) {
-            if (false === $nbOccurrences->contains($definition)) {
-                $nbOccurrences->offsetSet($definition, 1);
-            } else {
-                $i = $nbOccurrences->offsetGet($definition);
-                $nbOccurrences->offsetSet($definition, $i + 1);
+            foreach ($this->serviceCalls as $id => list($callCount, $behavior)) {
+                if ('service_container' !== $id && $id !== $cId
+                    && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $behavior
+                    && $this->container->has($id)
+                    && $this->isTrivialInstance($def = $this->container->findDefinition($id))
+                    && \is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())
+                ) {
+                    $this->collectLineage($class, $lineage);
+                }
             }
-        }
 
-        foreach ($inlinedDefinitions as $sDefinition) {
-            if ($processed->contains($sDefinition)) {
-                continue;
+            foreach (array_diff_key(array_flip($lineage), $this->inlinedRequires) as $file => $class) {
+                $code .= sprintf("        include_once %s;\n", $file);
             }
-            $processed->offsetSet($sDefinition);
-
-            $class = $this->dumpValue($sDefinition->getClass());
-            if ($nbOccurrences->offsetGet($sDefinition) > 1 || $sDefinition->getMethodCalls() || $sDefinition->getProperties() || null !== $sDefinition->getConfigurator() || false !== strpos($class, '$')) {
-                $name = $this->getNextVariableName();
-                $variableMap->offsetSet($sDefinition, new Variable($name));
-
-                // a construct like:
-                // $a = new ServiceA(ServiceB $b); $b = new ServiceB(ServiceA $a);
-                // this is an indication for a wrong implementation, you can circumvent this problem
-                // by setting up your service structure like this:
-                // $b = new ServiceB();
-                // $a = new ServiceA(ServiceB $b);
-                // $b->setServiceA(ServiceA $a);
-                if ($this->hasReference($id, $sDefinition->getArguments())) {
-                    throw new ServiceCircularReferenceException($id, array($id));
-                }
-
-                $code .= $this->addNewInstance($id, $sDefinition, '$'.$name, ' = ');
-
-                if (!$this->hasReference($id, $sDefinition->getMethodCalls(), true) && !$this->hasReference($id, $sDefinition->getProperties(), true)) {
-                    $code .= $this->addServiceProperties($sDefinition, $name);
-                    $code .= $this->addServiceMethodCalls($sDefinition, $name);
-                    $code .= $this->addServiceConfigurator($sDefinition, $name);
-                }
+        }
 
-                $code .= "\n";
+        foreach ($this->inlinedDefinitions as $def) {
+            if ($file = $def->getFile()) {
+                $code .= sprintf("        include_once %s;\n", $this->dumpValue($file));
             }
         }
 
-        return $code;
-    }
-
-    /**
-     * Adds the service return statement.
-     *
-     * @param string     $id         Service id
-     * @param Definition $definition
-     *
-     * @return string
-     */
-    private function addServiceReturn($id, Definition $definition)
-    {
-        if ($this->isSimpleInstance($id, $definition)) {
-            return "    }\n";
+        if ('' !== $code) {
+            $code .= "\n";
         }
 
-        return "\n        return \$instance;\n    }\n";
+        return $code;
     }
 
     /**
      * Generates the service instance.
      *
-     * @param string     $id
-     * @param Definition $definition
+     * @param string $id
+     * @param bool   $isSimpleInstance
      *
      * @return string
      *
      * @throws InvalidArgumentException
      * @throws RuntimeException
      */
-    private function addServiceInstance($id, Definition $definition)
+    private function addServiceInstance($id, Definition $definition, $isSimpleInstance)
     {
         $class = $this->dumpValue($definition->getClass());
 
-        if (0 === strpos($class, "'") && !preg_match('/^\'(?:\\\{2})?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) {
+        if (0 === strpos($class, "'") && false === strpos($class, '$') && !preg_match('/^\'(?:\\\{2})?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) {
             throw new InvalidArgumentException(sprintf('"%s" is not a valid class name for the "%s" service.', $class, $id));
         }
 
-        $simple = $this->isSimpleInstance($id, $definition);
         $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition);
         $instantiation = '';
 
-        if (!$isProxyCandidate && $definition->isShared() && ContainerInterface::SCOPE_CONTAINER === $definition->getScope(false)) {
-            $instantiation = sprintf('$this->services[%s] = %s', var_export($id, true), $simple ? '' : '$instance');
-        } elseif (!$isProxyCandidate && $definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope(false)) {
-            $instantiation = sprintf('$this->services[%s] = $this->scopedServices[%s][%1$s] = %s', var_export($id, true), var_export($scope, true), $simple ? '' : '$instance');
-        } elseif (!$simple) {
+        if (!$isProxyCandidate && $definition->isShared()) {
+            $instantiation = sprintf('$this->services[%s] = %s', $this->doExport($id), $isSimpleInstance ? '' : '$instance');
+        } elseif (!$isSimpleInstance) {
             $instantiation = '$instance';
         }
 
         $return = '';
-        if ($simple) {
+        if ($isSimpleInstance) {
             $return = 'return ';
         } else {
             $instantiation .= ' = ';
         }
 
-        $code = $this->addNewInstance($id, $definition, $return, $instantiation);
-
-        if (!$simple) {
-            $code .= "\n";
-        }
-
-        return $code;
+        return $this->addNewInstance($definition, $return, $instantiation, $id);
     }
 
     /**
-     * Checks if the definition is a simple instance.
-     *
-     * @param string     $id
-     * @param Definition $definition
+     * Checks if the definition is a trivial instance.
      *
      * @return bool
      */
-    private function isSimpleInstance($id, Definition $definition)
+    private function isTrivialInstance(Definition $definition)
     {
-        foreach (array_merge(array($definition), $this->getInlinedDefinitions($definition)) as $sDefinition) {
-            if ($definition !== $sDefinition && !$this->hasReference($id, $sDefinition->getMethodCalls())) {
+        if ($definition->isSynthetic() || $definition->getFile() || $definition->getMethodCalls() || $definition->getProperties() || $definition->getConfigurator()) {
+            return false;
+        }
+        if ($definition->isDeprecated() || $definition->isLazy() || $definition->getFactory() || 3 < \count($definition->getArguments())) {
+            return false;
+        }
+
+        foreach ($definition->getArguments() as $arg) {
+            if (!$arg || $arg instanceof Parameter) {
                 continue;
             }
-
-            if ($sDefinition->getMethodCalls() || $sDefinition->getProperties() || $sDefinition->getConfigurator()) {
+            if (\is_array($arg) && 3 >= \count($arg)) {
+                foreach ($arg as $k => $v) {
+                    if ($this->dumpValue($k) !== $this->dumpValue($k, false)) {
+                        return false;
+                    }
+                    if (!$v || $v instanceof Parameter) {
+                        continue;
+                    }
+                    if ($v instanceof Reference && $this->container->has($id = (string) $v) && $this->container->findDefinition($id)->isSynthetic()) {
+                        continue;
+                    }
+                    if (!is_scalar($v) || $this->dumpValue($v) !== $this->dumpValue($v, false)) {
+                        return false;
+                    }
+                }
+            } elseif ($arg instanceof Reference && $this->container->has($id = (string) $arg) && $this->container->findDefinition($id)->isSynthetic()) {
+                continue;
+            } elseif (!is_scalar($arg) || $this->dumpValue($arg) !== $this->dumpValue($arg, false)) {
                 return false;
             }
         }
@@ -428,8 +551,7 @@ class PhpDumper extends Dumper
     /**
      * Adds method calls to a service definition.
      *
-     * @param Definition $definition
-     * @param string     $variableName
+     * @param string $variableName
      *
      * @return string
      */
@@ -437,7 +559,7 @@ class PhpDumper extends Dumper
     {
         $calls = '';
         foreach ($definition->getMethodCalls() as $call) {
-            $arguments = array();
+            $arguments = [];
             foreach ($call[1] as $value) {
                 $arguments[] = $this->dumpValue($value);
             }
@@ -458,56 +580,10 @@ class PhpDumper extends Dumper
         return $code;
     }
 
-    /**
-     * Generates the inline definition setup.
-     *
-     * @param string     $id
-     * @param Definition $definition
-     *
-     * @return string
-     *
-     * @throws ServiceCircularReferenceException when the container contains a circular reference
-     */
-    private function addServiceInlinedDefinitionsSetup($id, Definition $definition)
-    {
-        $this->referenceVariables[$id] = new Variable('instance');
-
-        $code = '';
-        $processed = new \SplObjectStorage();
-        foreach ($this->getInlinedDefinitions($definition) as $iDefinition) {
-            if ($processed->contains($iDefinition)) {
-                continue;
-            }
-            $processed->offsetSet($iDefinition);
-
-            if (!$this->hasReference($id, $iDefinition->getMethodCalls(), true) && !$this->hasReference($id, $iDefinition->getProperties(), true)) {
-                continue;
-            }
-
-            // if the instance is simple, the return statement has already been generated
-            // so, the only possible way to get there is because of a circular reference
-            if ($this->isSimpleInstance($id, $definition)) {
-                throw new ServiceCircularReferenceException($id, array($id));
-            }
-
-            $name = (string) $this->definitionVariables->offsetGet($iDefinition);
-            $code .= $this->addServiceProperties($iDefinition, $name);
-            $code .= $this->addServiceMethodCalls($iDefinition, $name);
-            $code .= $this->addServiceConfigurator($iDefinition, $name);
-        }
-
-        if ('' !== $code) {
-            $code .= "\n";
-        }
-
-        return $code;
-    }
-
     /**
      * Adds configurator definition.
      *
-     * @param Definition $definition
-     * @param string     $variableName
+     * @param string $variableName
      *
      * @return string
      */
@@ -519,17 +595,22 @@ class PhpDumper extends Dumper
 
         if (\is_array($callable)) {
             if ($callable[0] instanceof Reference
-                || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))) {
+                || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))
+            ) {
                 return sprintf("        %s->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
             }
 
             $class = $this->dumpValue($callable[0]);
             // If the class is a string we can optimize call_user_func away
-            if (0 === strpos($class, "'")) {
+            if (0 === strpos($class, "'") && false === strpos($class, '$')) {
                 return sprintf("        %s::%s(\$%s);\n", $this->dumpLiteralClass($class), $callable[1], $variableName);
             }
 
-            return sprintf("        call_user_func(array(%s, '%s'), \$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
+            if (0 === strpos($class, 'new ')) {
+                return sprintf("        (%s)->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
+            }
+
+            return sprintf("        \\call_user_func([%s, '%s'], \$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
         }
 
         return sprintf("        %s(\$%s);\n", $callable, $variableName);
@@ -538,46 +619,32 @@ class PhpDumper extends Dumper
     /**
      * Adds a service.
      *
-     * @param string     $id
-     * @param Definition $definition
+     * @param string $id
+     * @param string &$file
      *
      * @return string
      */
-    private function addService($id, Definition $definition)
+    private function addService($id, Definition $definition, &$file = null)
     {
         $this->definitionVariables = new \SplObjectStorage();
-        $this->referenceVariables = array();
+        $this->referenceVariables = [];
         $this->variableCount = 0;
+        $this->referenceVariables[$id] = new Variable('instance');
 
-        $return = array();
+        $return = [];
 
-        if ($definition->isSynthetic()) {
-            $return[] = '@throws RuntimeException always since this service is expected to be injected dynamically';
-        } elseif ($class = $definition->getClass()) {
+        if ($class = $definition->getClass()) {
+            $class = $class instanceof Parameter ? '%'.$class.'%' : $this->container->resolveEnvPlaceholders($class);
             $return[] = sprintf(0 === strpos($class, '%') ? '@return object A %1$s instance' : '@return \%s', ltrim($class, '\\'));
         } elseif ($definition->getFactory()) {
             $factory = $definition->getFactory();
             if (\is_string($factory)) {
                 $return[] = sprintf('@return object An instance returned by %s()', $factory);
             } elseif (\is_array($factory) && (\is_string($factory[0]) || $factory[0] instanceof Definition || $factory[0] instanceof Reference)) {
-                if (\is_string($factory[0]) || $factory[0] instanceof Reference) {
-                    $return[] = sprintf('@return object An instance returned by %s::%s()', (string) $factory[0], $factory[1]);
-                } elseif ($factory[0] instanceof Definition) {
-                    $return[] = sprintf('@return object An instance returned by %s::%s()', $factory[0]->getClass(), $factory[1]);
-                }
+                $class = $factory[0] instanceof Definition ? $factory[0]->getClass() : (string) $factory[0];
+                $class = $class instanceof Parameter ? '%'.$class.'%' : $this->container->resolveEnvPlaceholders($class);
+                $return[] = sprintf('@return object An instance returned by %s::%s()', $class, $factory[1]);
             }
-        } elseif ($definition->getFactoryClass(false)) {
-            $return[] = sprintf('@return object An instance returned by %s::%s()', $definition->getFactoryClass(false), $definition->getFactoryMethod(false));
-        } elseif ($definition->getFactoryService(false)) {
-            $return[] = sprintf('@return object An instance returned by %s::%s()', $definition->getFactoryService(false), $definition->getFactoryMethod(false));
-        }
-
-        $scope = $definition->getScope(false);
-        if (!\in_array($scope, array(ContainerInterface::SCOPE_CONTAINER, ContainerInterface::SCOPE_PROTOTYPE))) {
-            if ($return && 0 === strpos($return[\count($return) - 1], '@return')) {
-                $return[] = '';
-            }
-            $return[] = sprintf("@throws InactiveScopeException when the '%s' service is requested while the '%s' scope is not active", $id, $scope);
         }
 
         if ($definition->isDeprecated()) {
@@ -589,8 +656,9 @@ class PhpDumper extends Dumper
         }
 
         $return = str_replace("\n     * \n", "\n     *\n", implode("\n     * ", $return));
+        $return = $this->container->resolveEnvPlaceholders($return);
 
-        $shared = $definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE !== $scope ? ' shared' : '';
+        $shared = $definition->isShared() ? ' shared' : '';
         $public = $definition->isPublic() ? 'public' : 'private';
         $autowired = $definition->isAutowired() ? ' autowired' : '';
 
@@ -600,10 +668,13 @@ class PhpDumper extends Dumper
             $lazyInitialization = '';
         }
 
-        // with proxies, for 5.3.3 compatibility, the getter must be public to be accessible to the initializer
-        $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition);
-        $visibility = $isProxyCandidate ? 'public' : 'protected';
-        $code = <<<EOF
+        $asFile = $this->asFiles && $definition->isShared() && !$this->isHotPath($definition);
+        $methodName = $this->generateMethodName($id);
+        if ($asFile) {
+            $file = $methodName.'.php';
+            $code = "        // Returns the $public '$id'$shared$autowired service.\n\n";
+        } else {
+            $code = <<<EOF
 
     /*{$this->docStar}
      * Gets the $public '$id'$shared$autowired service.
@@ -613,137 +684,209 @@ EOF;
             $code = str_replace('*/', ' ', $code).<<<EOF
 
      */
-    {$visibility} function get{$this->camelize($id)}Service($lazyInitialization)
+    protected function {$methodName}($lazyInitialization)
     {
 
 EOF;
+        }
 
-        $code .= $isProxyCandidate ? $this->getProxyDumper()->getProxyFactoryCode($definition, $id) : '';
+        $this->serviceCalls = [];
+        $this->inlinedDefinitions = $this->getDefinitionsFromArguments([$definition], null, $this->serviceCalls);
 
-        if (!\in_array($scope, array(ContainerInterface::SCOPE_CONTAINER, ContainerInterface::SCOPE_PROTOTYPE))) {
-            $code .= <<<EOF
-        if (!isset(\$this->scopedServices['$scope'])) {
-            throw new InactiveScopeException({$this->export($id)}, '$scope');
-        }
+        $code .= $this->addServiceInclude($id, $definition);
 
+        if ($this->getProxyDumper()->isProxyCandidate($definition)) {
+            $factoryCode = $asFile ? "\$this->load('%s.php', false)" : '$this->%s(false)';
+            $code .= $this->getProxyDumper()->getProxyFactoryCode($definition, $id, sprintf($factoryCode, $methodName, $this->doExport($id)));
+        }
 
-EOF;
+        if ($definition->isDeprecated()) {
+            $code .= sprintf("        @trigger_error(%s, E_USER_DEPRECATED);\n\n", $this->export($definition->getDeprecationMessage($id)));
         }
 
-        if ($definition->isSynthetic()) {
-            $code .= sprintf("        throw new RuntimeException(%s);\n    }\n", var_export("You have requested a synthetic service (\"$id\"). The DIC does not know how to construct this service.", true));
-        } else {
-            if ($definition->isDeprecated()) {
-                $code .= sprintf("        @trigger_error(%s, E_USER_DEPRECATED);\n\n", var_export($definition->getDeprecationMessage($id), true));
-            }
+        $code .= $this->addInlineService($id, $definition);
 
-            $code .=
-                $this->addServiceInclude($definition).
-                $this->addServiceLocalTempVariables($id, $definition).
-                $this->addServiceInlinedDefinitions($id, $definition).
-                $this->addServiceInstance($id, $definition).
-                $this->addServiceInlinedDefinitionsSetup($id, $definition).
-                $this->addServiceProperties($definition).
-                $this->addServiceMethodCalls($definition).
-                $this->addServiceConfigurator($definition).
-                $this->addServiceReturn($id, $definition)
-            ;
+        if ($asFile) {
+            $code = implode("\n", array_map(function ($line) { return $line ? substr($line, 8) : $line; }, explode("\n", $code)));
+        } else {
+            $code .= "    }\n";
         }
 
-        $this->definitionVariables = null;
-        $this->referenceVariables = null;
+        $this->definitionVariables = $this->inlinedDefinitions = null;
+        $this->referenceVariables = $this->serviceCalls = null;
 
         return $code;
     }
 
-    /**
-     * Adds multiple services.
-     *
-     * @return string
-     */
-    private function addServices()
+    private function addInlineVariables($id, Definition $definition, array $arguments, $forConstructor)
     {
-        $publicServices = $privateServices = $synchronizers = '';
-        $definitions = $this->container->getDefinitions();
-        ksort($definitions);
-        foreach ($definitions as $id => $definition) {
-            if ($definition->isPublic()) {
-                $publicServices .= $this->addService($id, $definition);
-            } else {
-                $privateServices .= $this->addService($id, $definition);
-            }
+        $code = '';
 
-            $synchronizers .= $this->addServiceSynchronizer($id, $definition);
+        foreach ($arguments as $argument) {
+            if (\is_array($argument)) {
+                $code .= $this->addInlineVariables($id, $definition, $argument, $forConstructor);
+            } elseif ($argument instanceof Reference) {
+                $code .= $this->addInlineReference($id, $definition, $this->container->normalizeId($argument), $forConstructor);
+            } elseif ($argument instanceof Definition) {
+                $code .= $this->addInlineService($id, $definition, $argument, $forConstructor);
+            }
         }
 
-        return $publicServices.$synchronizers.$privateServices;
+        return $code;
     }
 
-    /**
-     * Adds synchronizer methods.
-     *
-     * @param string     $id         A service identifier
-     * @param Definition $definition A Definition instance
-     *
-     * @return string|null
-     *
-     * @deprecated since version 2.7, will be removed in 3.0.
-     */
-    private function addServiceSynchronizer($id, Definition $definition)
+    private function addInlineReference($id, Definition $definition, $targetId, $forConstructor)
     {
-        if (!$definition->isSynchronized(false)) {
-            return;
+        while ($this->container->hasAlias($targetId)) {
+            $targetId = (string) $this->container->getAlias($targetId);
         }
 
-        if ('request' !== $id) {
-            @trigger_error('Synchronized services were deprecated in version 2.7 and won\'t work anymore in 3.0.', E_USER_DEPRECATED);
+        list($callCount, $behavior) = $this->serviceCalls[$targetId];
+
+        if ($id === $targetId) {
+            return $this->addInlineService($id, $definition, $definition);
         }
 
-        $code = '';
-        foreach ($this->container->getDefinitions() as $definitionId => $definition) {
-            foreach ($definition->getMethodCalls() as $call) {
-                foreach ($call[1] as $argument) {
-                    if ($argument instanceof Reference && $id == (string) $argument) {
-                        $arguments = array();
-                        foreach ($call[1] as $value) {
-                            $arguments[] = $this->dumpValue($value);
-                        }
+        if ('service_container' === $targetId || isset($this->referenceVariables[$targetId])) {
+            return '';
+        }
 
-                        $definitionId = var_export($definitionId, true);
-                        $call = $this->wrapServiceConditionals($call[1], sprintf('$this->get(%s)->%s(%s);', $definitionId, $call[0], implode(', ', $arguments)));
+        $hasSelfRef = isset($this->circularReferences[$id][$targetId]) && !isset($this->definitionVariables[$definition]);
 
-                        $code .= <<<EOF
-        if (\$this->initialized($definitionId)) {
-            $call
+        if ($hasSelfRef && !$forConstructor && !$forConstructor = !$this->circularReferences[$id][$targetId]) {
+            $code = $this->addInlineService($id, $definition, $definition);
+        } else {
+            $code = '';
         }
 
-EOF;
-                    }
+        if (isset($this->referenceVariables[$targetId]) || (2 > $callCount && (!$hasSelfRef || !$forConstructor))) {
+            return $code;
+        }
+
+        $name = $this->getNextVariableName();
+        $this->referenceVariables[$targetId] = new Variable($name);
+
+        $reference = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE >= $behavior ? new Reference($targetId, $behavior) : null;
+        $code .= sprintf("        \$%s = %s;\n", $name, $this->getServiceCall($targetId, $reference));
+
+        if (!$hasSelfRef || !$forConstructor) {
+            return $code;
+        }
+
+        $code .= sprintf(<<<'EOTXT'
+
+        if (isset($this->%s[%s])) {
+            return $this->%1$s[%2$s];
+        }
+
+EOTXT
+            ,
+            'services',
+            $this->doExport($id)
+        );
+
+        return $code;
+    }
+
+    private function addInlineService($id, Definition $definition, Definition $inlineDef = null, $forConstructor = true)
+    {
+        $code = '';
+
+        if ($isSimpleInstance = $isRootInstance = null === $inlineDef) {
+            foreach ($this->serviceCalls as $targetId => list($callCount, $behavior, $byConstructor)) {
+                if ($byConstructor && isset($this->circularReferences[$id][$targetId]) && !$this->circularReferences[$id][$targetId]) {
+                    $code .= $this->addInlineReference($id, $definition, $targetId, $forConstructor);
                 }
             }
         }
 
-        if (!$code) {
-            return;
+        if (isset($this->definitionVariables[$inlineDef = $inlineDef ?: $definition])) {
+            return $code;
         }
 
-        return <<<EOF
+        $arguments = [$inlineDef->getArguments(), $inlineDef->getFactory()];
 
-    /*{$this->docStar}
-     * Updates the '$id' service.
+        $code .= $this->addInlineVariables($id, $definition, $arguments, $forConstructor);
+
+        if ($arguments = array_filter([$inlineDef->getProperties(), $inlineDef->getMethodCalls(), $inlineDef->getConfigurator()])) {
+            $isSimpleInstance = false;
+        } elseif ($definition !== $inlineDef && 2 > $this->inlinedDefinitions[$inlineDef]) {
+            return $code;
+        }
+
+        if (isset($this->definitionVariables[$inlineDef])) {
+            $isSimpleInstance = false;
+        } else {
+            $name = $definition === $inlineDef ? 'instance' : $this->getNextVariableName();
+            $this->definitionVariables[$inlineDef] = new Variable($name);
+            $code .= '' !== $code ? "\n" : '';
+
+            if ('instance' === $name) {
+                $code .= $this->addServiceInstance($id, $definition, $isSimpleInstance);
+            } else {
+                $code .= $this->addNewInstance($inlineDef, '$'.$name, ' = ', $id);
+            }
+
+            if ('' !== $inline = $this->addInlineVariables($id, $definition, $arguments, false)) {
+                $code .= "\n".$inline."\n";
+            } elseif ($arguments && 'instance' === $name) {
+                $code .= "\n";
+            }
+
+            $code .= $this->addServiceProperties($inlineDef, $name);
+            $code .= $this->addServiceMethodCalls($inlineDef, $name);
+            $code .= $this->addServiceConfigurator($inlineDef, $name);
+        }
+
+        if ($isRootInstance && !$isSimpleInstance) {
+            $code .= "\n        return \$instance;\n";
+        }
+
+        return $code;
+    }
+
+    /**
+     * Adds multiple services.
+     *
+     * @return string
      */
-    protected function synchronize{$this->camelize($id)}Service()
+    private function addServices()
     {
-$code    }
+        $publicServices = $privateServices = '';
+        $definitions = $this->container->getDefinitions();
+        ksort($definitions);
+        foreach ($definitions as $id => $definition) {
+            if ($definition->isSynthetic() || ($this->asFiles && $definition->isShared() && !$this->isHotPath($definition))) {
+                continue;
+            }
+            if ($definition->isPublic()) {
+                $publicServices .= $this->addService($id, $definition);
+            } else {
+                $privateServices .= $this->addService($id, $definition);
+            }
+        }
 
-EOF;
+        return $publicServices.$privateServices;
+    }
+
+    private function generateServiceFiles()
+    {
+        $definitions = $this->container->getDefinitions();
+        ksort($definitions);
+        foreach ($definitions as $id => $definition) {
+            if (!$definition->isSynthetic() && $definition->isShared() && !$this->isHotPath($definition)) {
+                $code = $this->addService($id, $definition, $file);
+                yield $file => $code;
+            }
+        }
     }
 
-    private function addNewInstance($id, Definition $definition, $return, $instantiation)
+    private function addNewInstance(Definition $definition, $return, $instantiation, $id)
     {
         $class = $this->dumpValue($definition->getClass());
+        $return = '        '.$return.$instantiation;
 
-        $arguments = array();
+        $arguments = [];
         foreach ($definition->getArguments() as $value) {
             $arguments[] = $this->dumpValue($value);
         }
@@ -752,70 +895,61 @@ EOF;
             $callable = $definition->getFactory();
             if (\is_array($callable)) {
                 if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $callable[1])) {
-                    throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s)', $callable[1] ?: 'n/a'));
+                    throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s).', $callable[1] ?: 'n/a'));
                 }
 
                 if ($callable[0] instanceof Reference
                     || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))) {
-                    return sprintf("        $return{$instantiation}%s->%s(%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? implode(', ', $arguments) : '');
+                    return $return.sprintf("%s->%s(%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? implode(', ', $arguments) : '');
                 }
 
                 $class = $this->dumpValue($callable[0]);
                 // If the class is a string we can optimize call_user_func away
-                if (0 === strpos($class, "'")) {
-                    return sprintf("        $return{$instantiation}%s::%s(%s);\n", $this->dumpLiteralClass($class), $callable[1], $arguments ? implode(', ', $arguments) : '');
-                }
-
-                return sprintf("        $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? ', '.implode(', ', $arguments) : '');
-            }
-
-            return sprintf("        $return{$instantiation}\\%s(%s);\n", $callable, $arguments ? implode(', ', $arguments) : '');
-        } elseif (null !== $definition->getFactoryMethod(false)) {
-            if (null !== $definition->getFactoryClass(false)) {
-                $class = $this->dumpValue($definition->getFactoryClass(false));
+                if (0 === strpos($class, "'") && false === strpos($class, '$')) {
+                    if ("''" === $class) {
+                        throw new RuntimeException(sprintf('Cannot dump definition: The "%s" service is defined to be created by a factory but is missing the service reference, did you forget to define the factory service id or class?', $id));
+                    }
 
-                // If the class is a string we can optimize call_user_func away
-                if (0 === strpos($class, "'")) {
-                    return sprintf("        $return{$instantiation}%s::%s(%s);\n", $this->dumpLiteralClass($class), $definition->getFactoryMethod(false), $arguments ? implode(', ', $arguments) : '');
+                    return $return.sprintf("%s::%s(%s);\n", $this->dumpLiteralClass($class), $callable[1], $arguments ? implode(', ', $arguments) : '');
                 }
 
-                return sprintf("        $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($definition->getFactoryClass(false)), $definition->getFactoryMethod(false), $arguments ? ', '.implode(', ', $arguments) : '');
-            }
+                if (0 === strpos($class, 'new ')) {
+                    return $return.sprintf("(%s)->%s(%s);\n", $class, $callable[1], $arguments ? implode(', ', $arguments) : '');
+                }
 
-            if (null !== $definition->getFactoryService(false)) {
-                return sprintf("        $return{$instantiation}%s->%s(%s);\n", $this->getServiceCall($definition->getFactoryService(false)), $definition->getFactoryMethod(false), implode(', ', $arguments));
+                return $return.sprintf("\\call_user_func([%s, '%s']%s);\n", $class, $callable[1], $arguments ? ', '.implode(', ', $arguments) : '');
             }
 
-            throw new RuntimeException(sprintf('Factory method requires a factory service or factory class in service definition for %s', $id));
+            return $return.sprintf("%s(%s);\n", $this->dumpLiteralClass($this->dumpValue($callable)), $arguments ? implode(', ', $arguments) : '');
         }
 
         if (false !== strpos($class, '$')) {
-            return sprintf("        \$class = %s;\n\n        $return{$instantiation}new \$class(%s);\n", $class, implode(', ', $arguments));
+            return sprintf("        \$class = %s;\n\n%snew \$class(%s);\n", $class, $return, implode(', ', $arguments));
         }
 
-        return sprintf("        $return{$instantiation}new %s(%s);\n", $this->dumpLiteralClass($class), implode(', ', $arguments));
+        return $return.sprintf("new %s(%s);\n", $this->dumpLiteralClass($class), implode(', ', $arguments));
     }
 
     /**
      * Adds the class headers.
      *
-     * @param string $class     Class name
-     * @param string $baseClass The name of the base class
-     * @param string $namespace The class namespace
+     * @param string $class                  Class name
+     * @param string $baseClass              The name of the base class
+     * @param string $baseClassWithNamespace Fully qualified base class name
      *
      * @return string
      */
-    private function startClass($class, $baseClass, $namespace)
+    private function startClass($class, $baseClass, $baseClassWithNamespace)
     {
-        $bagClass = $this->container->isFrozen() ? 'use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;' : 'use Symfony\Component\DependencyInjection\ParameterBag\\ParameterBag;';
-        $namespaceLine = $namespace ? "\nnamespace $namespace;\n" : '';
+        $bagClass = $this->container->isCompiled() ? 'use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;' : 'use Symfony\Component\DependencyInjection\ParameterBag\\ParameterBag;';
+        $namespaceLine = !$this->asFiles && $this->namespace ? "\nnamespace {$this->namespace};\n" : '';
 
-        return <<<EOF
+        $code = <<<EOF
 <?php
 $namespaceLine
+use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\DependencyInjection\Container;
-use Symfony\Component\DependencyInjection\Exception\InactiveScopeException;
 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
 use Symfony\Component\DependencyInjection\Exception\LogicException;
 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
@@ -824,154 +958,280 @@ $bagClass
 /*{$this->docStar}
  * This class has been auto-generated
  * by the Symfony Dependency Injection Component.
+ *
+ * @final since Symfony 3.3
  */
 class $class extends $baseClass
 {
-    private \$parameters;
-    private \$targetDirs = array();
-
-EOF;
-    }
+    private \$parameters = [];
+    private \$targetDirs = [];
 
-    /**
-     * Adds the constructor.
-     *
-     * @return string
-     */
-    private function addConstructor()
+    public function __construct()
     {
-        $targetDirs = $this->exportTargetDirs();
-        $arguments = $this->container->getParameterBag()->all() ? 'new ParameterBag($this->getDefaultParameters())' : null;
 
-        $code = <<<EOF
-
-    public function __construct()
-    {{$targetDirs}
-        parent::__construct($arguments);
+EOF;
+        if (null !== $this->targetDirRegex) {
+            $dir = $this->asFiles ? '$this->targetDirs[0] = \\dirname($containerDir)' : '__DIR__';
+            $code .= <<<EOF
+        \$dir = {$dir};
+        for (\$i = 1; \$i <= {$this->targetDirMaxMatches}; ++\$i) {
+            \$this->targetDirs[\$i] = \$dir = \\dirname(\$dir);
+        }
 
 EOF;
+        }
+        if ($this->asFiles) {
+            $code = str_replace('$parameters', "\$buildParameters;\n    private \$containerDir;\n    private \$parameters", $code);
+            $code = str_replace('__construct()', '__construct(array $buildParameters = [], $containerDir = __DIR__)', $code);
+            $code .= "        \$this->buildParameters = \$buildParameters;\n";
+            $code .= "        \$this->containerDir = \$containerDir;\n";
+        }
+
+        if ($this->container->isCompiled()) {
+            if (Container::class !== $baseClassWithNamespace) {
+                $r = $this->container->getReflectionClass($baseClassWithNamespace, false);
+                if (null !== $r
+                    && (null !== $constructor = $r->getConstructor())
+                    && 0 === $constructor->getNumberOfRequiredParameters()
+                    && Container::class !== $constructor->getDeclaringClass()->name
+                ) {
+                    $code .= "        parent::__construct();\n";
+                    $code .= "        \$this->parameterBag = null;\n\n";
+                }
+            }
 
-        if (\count($scopes = $this->container->getScopes(false)) > 0) {
-            $code .= "\n";
-            $code .= '        $this->scopes = '.$this->dumpValue($scopes).";\n";
-            $code .= '        $this->scopeChildren = '.$this->dumpValue($this->container->getScopeChildren(false)).";\n";
+            if ($this->container->getParameterBag()->all()) {
+                $code .= "        \$this->parameters = \$this->getDefaultParameters();\n\n";
+            }
+
+            $code .= "        \$this->services = [];\n";
+        } else {
+            $arguments = $this->container->getParameterBag()->all() ? 'new ParameterBag($this->getDefaultParameters())' : null;
+            $code .= "        parent::__construct($arguments);\n";
         }
 
+        $code .= $this->addNormalizedIds();
+        $code .= $this->addSyntheticIds();
         $code .= $this->addMethodMap();
+        $code .= $this->asFiles ? $this->addFileMap() : '';
+        $code .= $this->addPrivateServices();
         $code .= $this->addAliases();
-
+        $code .= $this->addInlineRequires();
         $code .= <<<'EOF'
     }
 
 EOF;
+        $code .= $this->addRemovedIds();
 
-        return $code;
+        if ($this->container->isCompiled()) {
+            $code .= <<<EOF
+
+    public function compile()
+    {
+        throw new LogicException('You cannot compile a dumped container that was already compiled.');
     }
 
-    /**
-     * Adds the constructor for a frozen container.
-     *
-     * @return string
-     */
-    private function addFrozenConstructor()
+    public function isCompiled()
     {
-        $targetDirs = $this->exportTargetDirs();
+        return true;
+    }
 
-        $code = <<<EOF
+    public function isFrozen()
+    {
+        @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED);
 
-    public function __construct()
-    {{$targetDirs}
-EOF;
+        return true;
+    }
 
-        if ($this->container->getParameterBag()->all()) {
-            $code .= "\n        \$this->parameters = \$this->getDefaultParameters();\n";
+EOF;
         }
 
-        $code .= <<<'EOF'
+        if ($this->asFiles) {
+            $code .= <<<EOF
 
-        $this->services =
-        $this->scopedServices =
-        $this->scopeStacks = array();
-EOF;
+    protected function load(\$file, \$lazyLoad = true)
+    {
+        return require \$this->containerDir.\\DIRECTORY_SEPARATOR.\$file;
+    }
 
-        $code .= "\n";
-        if (\count($scopes = $this->container->getScopes(false)) > 0) {
-            $code .= '        $this->scopes = '.$this->dumpValue($scopes).";\n";
-            $code .= '        $this->scopeChildren = '.$this->dumpValue($this->container->getScopeChildren(false)).";\n";
-        } else {
-            $code .= "        \$this->scopes = array();\n";
-            $code .= "        \$this->scopeChildren = array();\n";
+EOF;
         }
 
-        $code .= $this->addMethodMap();
-        $code .= $this->addAliases();
+        $proxyDumper = $this->getProxyDumper();
+        foreach ($this->container->getDefinitions() as $definition) {
+            if (!$proxyDumper->isProxyCandidate($definition)) {
+                continue;
+            }
+            if ($this->asFiles) {
+                $proxyLoader = '$this->load("{$class}.php")';
+            } elseif ($this->namespace) {
+                $proxyLoader = 'class_alias("'.$this->namespace.'\\\\{$class}", $class, false)';
+            } else {
+                $proxyLoader = '';
+            }
+            if ($proxyLoader) {
+                $proxyLoader = "class_exists(\$class, false) || {$proxyLoader};\n\n        ";
+            }
+            $code .= <<<EOF
 
-        $code .= <<<'EOF'
+    protected function createProxy(\$class, \Closure \$factory)
+    {
+        {$proxyLoader}return \$factory();
     }
 
 EOF;
+            break;
+        }
+
+        return $code;
+    }
+
+    /**
+     * Adds the normalizedIds property definition.
+     *
+     * @return string
+     */
+    private function addNormalizedIds()
+    {
+        $code = '';
+        $normalizedIds = $this->container->getNormalizedIds();
+        ksort($normalizedIds);
+        foreach ($normalizedIds as $id => $normalizedId) {
+            if ($this->container->has($normalizedId)) {
+                $code .= '            '.$this->doExport($id).' => '.$this->doExport($normalizedId).",\n";
+            }
+        }
+
+        return $code ? "        \$this->normalizedIds = [\n".$code."        ];\n" : '';
+    }
+
+    /**
+     * Adds the syntheticIds definition.
+     *
+     * @return string
+     */
+    private function addSyntheticIds()
+    {
+        $code = '';
+        $definitions = $this->container->getDefinitions();
+        ksort($definitions);
+        foreach ($definitions as $id => $definition) {
+            if ($definition->isSynthetic() && 'service_container' !== $id) {
+                $code .= '            '.$this->doExport($id)." => true,\n";
+            }
+        }
 
-        return $code;
+        return $code ? "        \$this->syntheticIds = [\n{$code}        ];\n" : '';
     }
 
     /**
-     * Adds the constructor for a frozen container.
+     * Adds the removedIds definition.
      *
      * @return string
      */
-    private function addFrozenCompile()
+    private function addRemovedIds()
     {
+        if (!$ids = $this->container->getRemovedIds()) {
+            return '';
+        }
+        if ($this->asFiles) {
+            $code = "require \$this->containerDir.\\DIRECTORY_SEPARATOR.'removed-ids.php'";
+        } else {
+            $code = '';
+            $ids = array_keys($ids);
+            sort($ids);
+            foreach ($ids as $id) {
+                if (preg_match('/^\d+_[^~]++~[._a-zA-Z\d]{7}$/', $id)) {
+                    continue;
+                }
+                $code .= '            '.$this->doExport($id)." => true,\n";
+            }
+
+            $code = "[\n{$code}        ]";
+        }
+
         return <<<EOF
 
-    /*{$this->docStar}
-     * {@inheritdoc}
-     */
-    public function compile()
+    public function getRemovedIds()
     {
-        throw new LogicException('You cannot compile a dumped frozen container.');
+        return {$code};
     }
 
 EOF;
     }
 
     /**
-     * Adds the isFrozen method for a frozen container.
+     * Adds the methodMap property definition.
      *
      * @return string
      */
-    private function addIsFrozenMethod()
+    private function addMethodMap()
     {
-        return <<<EOF
+        $code = '';
+        $definitions = $this->container->getDefinitions();
+        ksort($definitions);
+        foreach ($definitions as $id => $definition) {
+            if (!$definition->isSynthetic() && (!$this->asFiles || !$definition->isShared() || $this->isHotPath($definition))) {
+                $code .= '            '.$this->doExport($id).' => '.$this->doExport($this->generateMethodName($id)).",\n";
+            }
+        }
 
-    /*{$this->docStar}
-     * {@inheritdoc}
+        return $code ? "        \$this->methodMap = [\n{$code}        ];\n" : '';
+    }
+
+    /**
+     * Adds the fileMap property definition.
+     *
+     * @return string
      */
-    public function isFrozen()
+    private function addFileMap()
     {
-        return true;
-    }
+        $code = '';
+        $definitions = $this->container->getDefinitions();
+        ksort($definitions);
+        foreach ($definitions as $id => $definition) {
+            if (!$definition->isSynthetic() && $definition->isShared() && !$this->isHotPath($definition)) {
+                $code .= sprintf("            %s => '%s.php',\n", $this->doExport($id), $this->generateMethodName($id));
+            }
+        }
 
-EOF;
+        return $code ? "        \$this->fileMap = [\n{$code}        ];\n" : '';
     }
 
     /**
-     * Adds the methodMap property definition.
+     * Adds the privates property definition.
      *
      * @return string
      */
-    private function addMethodMap()
+    private function addPrivateServices()
     {
-        if (!$definitions = $this->container->getDefinitions()) {
-            return '';
+        $code = '';
+
+        $aliases = $this->container->getAliases();
+        ksort($aliases);
+        foreach ($aliases as $id => $alias) {
+            if ($alias->isPrivate()) {
+                $code .= '            '.$this->doExport($id)." => true,\n";
+            }
         }
 
-        $code = "        \$this->methodMap = array(\n";
+        $definitions = $this->container->getDefinitions();
         ksort($definitions);
         foreach ($definitions as $id => $definition) {
-            $code .= '            '.var_export($id, true).' => '.var_export('get'.$this->camelize($id).'Service', true).",\n";
+            if (!$definition->isPublic()) {
+                $code .= '            '.$this->doExport($id)." => true,\n";
+            }
+        }
+
+        if (empty($code)) {
+            return '';
         }
 
-        return $code."        );\n";
+        $out = "        \$this->privates = [\n";
+        $out .= $code;
+        $out .= "        ];\n";
+
+        return $out;
     }
 
     /**
@@ -982,20 +1242,51 @@ EOF;
     private function addAliases()
     {
         if (!$aliases = $this->container->getAliases()) {
-            return $this->container->isFrozen() ? "\n        \$this->aliases = array();\n" : '';
+            return $this->container->isCompiled() ? "\n        \$this->aliases = [];\n" : '';
         }
 
-        $code = "        \$this->aliases = array(\n";
+        $code = "        \$this->aliases = [\n";
         ksort($aliases);
         foreach ($aliases as $alias => $id) {
-            $id = (string) $id;
+            $id = $this->container->normalizeId($id);
             while (isset($aliases[$id])) {
-                $id = (string) $aliases[$id];
+                $id = $this->container->normalizeId($aliases[$id]);
+            }
+            $code .= '            '.$this->doExport($alias).' => '.$this->doExport($id).",\n";
+        }
+
+        return $code."        ];\n";
+    }
+
+    private function addInlineRequires()
+    {
+        if (!$this->hotPathTag || !$this->inlineRequires) {
+            return '';
+        }
+
+        $lineage = [];
+
+        foreach ($this->container->findTaggedServiceIds($this->hotPathTag) as $id => $tags) {
+            $definition = $this->container->getDefinition($id);
+            $inlinedDefinitions = $this->getDefinitionsFromArguments([$definition]);
+
+            foreach ($inlinedDefinitions as $def) {
+                if (\is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())) {
+                    $this->collectLineage($class, $lineage);
+                }
+            }
+        }
+
+        $code = '';
+
+        foreach ($lineage as $file) {
+            if (!isset($this->inlinedRequires[$file])) {
+                $this->inlinedRequires[$file] = true;
+                $code .= sprintf("\n            include_once %s;", $file);
             }
-            $code .= '            '.var_export($alias, true).' => '.var_export($id, true).",\n";
         }
 
-        return $code."        );\n";
+        return $code ? sprintf("\n        \$this->privates['service_container'] = function () {%s\n        };\n", $code) : '';
     }
 
     /**
@@ -1009,60 +1300,149 @@ EOF;
             return '';
         }
 
-        $parameters = $this->exportParameters($this->container->getParameterBag()->all());
+        $php = [];
+        $dynamicPhp = [];
+        $normalizedParams = [];
+
+        foreach ($this->container->getParameterBag()->all() as $key => $value) {
+            if ($key !== $resolvedKey = $this->container->resolveEnvPlaceholders($key)) {
+                throw new InvalidArgumentException(sprintf('Parameter name cannot use env parameters: "%s".', $resolvedKey));
+            }
+            if ($key !== $lcKey = strtolower($key)) {
+                $normalizedParams[] = sprintf('        %s => %s,', $this->export($lcKey), $this->export($key));
+            }
+            $export = $this->exportParameters([$value]);
+            $export = explode('0 => ', substr(rtrim($export, " ]\n"), 2, -1), 2);
+
+            if (preg_match("/\\\$this->(?:getEnv\('(?:\w++:)*+\w++'\)|targetDirs\[\d++\])/", $export[1])) {
+                $dynamicPhp[$key] = sprintf('%scase %s: $value = %s; break;', $export[0], $this->export($key), $export[1]);
+            } else {
+                $php[] = sprintf('%s%s => %s,', $export[0], $this->export($key), $export[1]);
+            }
+        }
+
+        $parameters = sprintf("[\n%s\n%s]", implode("\n", $php), str_repeat(' ', 8));
 
         $code = '';
-        if ($this->container->isFrozen()) {
+        if ($this->container->isCompiled()) {
             $code .= <<<'EOF'
 
-    /**
-     * {@inheritdoc}
-     */
     public function getParameter($name)
     {
-        $name = strtolower($name);
+        $name = (string) $name;
+        if (isset($this->buildParameters[$name])) {
+            return $this->buildParameters[$name];
+        }
+        if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
+            $name = $this->normalizeParameterName($name);
 
-        if (!(isset($this->parameters[$name]) || array_key_exists($name, $this->parameters))) {
-            throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
+            if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
+                throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
+            }
+        }
+        if (isset($this->loadedDynamicParameters[$name])) {
+            return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name);
         }
 
         return $this->parameters[$name];
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public function hasParameter($name)
     {
-        $name = strtolower($name);
+        $name = (string) $name;
+        if (isset($this->buildParameters[$name])) {
+            return true;
+        }
+        $name = $this->normalizeParameterName($name);
 
-        return isset($this->parameters[$name]) || array_key_exists($name, $this->parameters);
+        return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public function setParameter($name, $value)
     {
         throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public function getParameterBag()
     {
         if (null === $this->parameterBag) {
-            $this->parameterBag = new FrozenParameterBag($this->parameters);
+            $parameters = $this->parameters;
+            foreach ($this->loadedDynamicParameters as $name => $loaded) {
+                $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name);
+            }
+            foreach ($this->buildParameters as $name => $value) {
+                $parameters[$name] = $value;
+            }
+            $this->parameterBag = new FrozenParameterBag($parameters);
         }
 
         return $this->parameterBag;
     }
 
 EOF;
-            if ('' === $this->docStar) {
-                $code = str_replace('/**', '/*', $code);
+            if (!$this->asFiles) {
+                $code = preg_replace('/^.*buildParameters.*\n.*\n.*\n/m', '', $code);
+            }
+
+            if ($dynamicPhp) {
+                $loadedDynamicParameters = $this->exportParameters(array_combine(array_keys($dynamicPhp), array_fill(0, \count($dynamicPhp), false)), '', 8);
+                $getDynamicParameter = <<<'EOF'
+        switch ($name) {
+%s
+            default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%%s" must be defined.', $name));
+        }
+        $this->loadedDynamicParameters[$name] = true;
+
+        return $this->dynamicParameters[$name] = $value;
+EOF;
+                $getDynamicParameter = sprintf($getDynamicParameter, implode("\n", $dynamicPhp));
+            } else {
+                $loadedDynamicParameters = '[]';
+                $getDynamicParameter = str_repeat(' ', 8).'throw new InvalidArgumentException(sprintf(\'The dynamic parameter "%s" must be defined.\', $name));';
+            }
+
+            $code .= <<<EOF
+
+    private \$loadedDynamicParameters = {$loadedDynamicParameters};
+    private \$dynamicParameters = [];
+
+    /*{$this->docStar}
+     * Computes a dynamic parameter.
+     *
+     * @param string \$name The name of the dynamic parameter to load
+     *
+     * @return mixed The value of the dynamic parameter
+     *
+     * @throws InvalidArgumentException When the dynamic parameter does not exist
+     */
+    private function getDynamicParameter(\$name)
+    {
+{$getDynamicParameter}
+    }
+
+
+EOF;
+
+            $code .= '    private $normalizedParameterNames = '.($normalizedParams ? sprintf("[\n%s\n    ];", implode("\n", $normalizedParams)) : '[];')."\n";
+            $code .= <<<'EOF'
+
+    private function normalizeParameterName($name)
+    {
+        if (isset($this->normalizedParameterNames[$normalizedName = strtolower($name)]) || isset($this->parameters[$normalizedName]) || array_key_exists($normalizedName, $this->parameters)) {
+            $normalizedName = isset($this->normalizedParameterNames[$normalizedName]) ? $this->normalizedParameterNames[$normalizedName] : $normalizedName;
+            if ((string) $name !== $normalizedName) {
+                @trigger_error(sprintf('Parameter names will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since Symfony 3.4.', $name, $normalizedName), E_USER_DEPRECATED);
             }
+        } else {
+            $normalizedName = $this->normalizedParameterNames[$normalizedName] = (string) $name;
+        }
+
+        return $normalizedName;
+    }
+
+EOF;
+        } elseif ($dynamicPhp) {
+            throw new RuntimeException('You cannot dump a not-frozen container with dynamic parameters.');
         }
 
         $code .= <<<EOF
@@ -1085,7 +1465,6 @@ EOF;
     /**
      * Exports parameters.
      *
-     * @param array  $parameters
      * @param string $path
      * @param int    $indent
      *
@@ -1095,10 +1474,12 @@ EOF;
      */
     private function exportParameters(array $parameters, $path = '', $indent = 12)
     {
-        $php = array();
+        $php = [];
         foreach ($parameters as $key => $value) {
             if (\is_array($value)) {
                 $value = $this->exportParameters($value, $path.'/'.$key, $indent + 4);
+            } elseif ($value instanceof ArgumentInterface) {
+                throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain special arguments. "%s" found in "%s".', \get_class($value), $path.'/'.$key));
             } elseif ($value instanceof Variable) {
                 throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain variable references. Variable "%s" found in "%s".', $value, $path.'/'.$key));
             } elseif ($value instanceof Definition) {
@@ -1111,10 +1492,10 @@ EOF;
                 $value = $this->export($value);
             }
 
-            $php[] = sprintf('%s%s => %s,', str_repeat(' ', $indent), var_export($key, true), $value);
+            $php[] = sprintf('%s%s => %s,', str_repeat(' ', $indent), $this->export($key), $value);
         }
 
-        return sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', $indent - 4));
+        return sprintf("[\n%s\n%s]", implode("\n", $php), str_repeat(' ', $indent - 4));
     }
 
     /**
@@ -1140,137 +1521,84 @@ EOF;
      */
     private function wrapServiceConditionals($value, $code)
     {
-        if (!$services = ContainerBuilder::getServiceConditionals($value)) {
+        if (!$condition = $this->getServiceConditionals($value)) {
             return $code;
         }
 
-        $conditions = array();
-        foreach ($services as $service) {
-            $conditions[] = sprintf('$this->has(%s)', var_export($service, true));
-        }
-
         // re-indent the wrapped code
         $code = implode("\n", array_map(function ($line) { return $line ? '    '.$line : $line; }, explode("\n", $code)));
 
-        return sprintf("        if (%s) {\n%s        }\n", implode(' && ', $conditions), $code);
+        return sprintf("        if (%s) {\n%s        }\n", $condition, $code);
     }
 
     /**
-     * Builds service calls from arguments.
+     * Get the conditions to execute for conditional services.
+     *
+     * @param string $value
+     *
+     * @return string|null
      */
-    private function getServiceCallsFromArguments(array $arguments, array &$calls, array &$behavior)
+    private function getServiceConditionals($value)
     {
-        foreach ($arguments as $argument) {
-            if (\is_array($argument)) {
-                $this->getServiceCallsFromArguments($argument, $calls, $behavior);
-            } elseif ($argument instanceof Reference) {
-                $id = (string) $argument;
-
-                if (!isset($calls[$id])) {
-                    $calls[$id] = 0;
-                }
-                if (!isset($behavior[$id])) {
-                    $behavior[$id] = $argument->getInvalidBehavior();
-                } elseif (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $behavior[$id]) {
-                    $behavior[$id] = $argument->getInvalidBehavior();
-                }
-
-                ++$calls[$id];
+        $conditions = [];
+        foreach (ContainerBuilder::getInitializedConditionals($value) as $service) {
+            if (!$this->container->hasDefinition($service)) {
+                return 'false';
             }
+            $conditions[] = sprintf('isset($this->services[%s])', $this->doExport($service));
         }
-    }
-
-    /**
-     * Returns the inline definition.
-     *
-     * @return array
-     */
-    private function getInlinedDefinitions(Definition $definition)
-    {
-        if (false === $this->inlinedDefinitions->contains($definition)) {
-            $definitions = array_merge(
-                $this->getDefinitionsFromArguments($definition->getArguments()),
-                $this->getDefinitionsFromArguments($definition->getMethodCalls()),
-                $this->getDefinitionsFromArguments($definition->getProperties()),
-                $this->getDefinitionsFromArguments(array($definition->getConfigurator())),
-                $this->getDefinitionsFromArguments(array($definition->getFactory()))
-            );
+        foreach (ContainerBuilder::getServiceConditionals($value) as $service) {
+            if ($this->container->hasDefinition($service) && !$this->container->getDefinition($service)->isPublic()) {
+                continue;
+            }
 
-            $this->inlinedDefinitions->offsetSet($definition, $definitions);
+            $conditions[] = sprintf('$this->has(%s)', $this->doExport($service));
+        }
 
-            return $definitions;
+        if (!$conditions) {
+            return '';
         }
 
-        return $this->inlinedDefinitions->offsetGet($definition);
+        return implode(' && ', $conditions);
     }
 
-    /**
-     * Gets the definition from arguments.
-     *
-     * @return array
-     */
-    private function getDefinitionsFromArguments(array $arguments)
+    private function getDefinitionsFromArguments(array $arguments, \SplObjectStorage $definitions = null, array &$calls = [], $byConstructor = null)
     {
-        $definitions = array();
-        foreach ($arguments as $argument) {
-            if (\is_array($argument)) {
-                $definitions = array_merge($definitions, $this->getDefinitionsFromArguments($argument));
-            } elseif ($argument instanceof Definition) {
-                $definitions = array_merge(
-                    $definitions,
-                    $this->getInlinedDefinitions($argument),
-                    array($argument)
-                );
-            }
+        if (null === $definitions) {
+            $definitions = new \SplObjectStorage();
         }
 
-        return $definitions;
-    }
-
-    /**
-     * Checks if a service id has a reference.
-     *
-     * @param string $id
-     * @param array  $arguments
-     * @param bool   $deep
-     * @param array  $visited
-     *
-     * @return bool
-     */
-    private function hasReference($id, array $arguments, $deep = false, array &$visited = array())
-    {
         foreach ($arguments as $argument) {
             if (\is_array($argument)) {
-                if ($this->hasReference($id, $argument, $deep, $visited)) {
-                    return true;
-                }
+                $this->getDefinitionsFromArguments($argument, $definitions, $calls, $byConstructor);
             } elseif ($argument instanceof Reference) {
-                $argumentId = (string) $argument;
-                if ($id === $argumentId) {
-                    return true;
-                }
-
-                if ($deep && !isset($visited[$argumentId]) && 'service_container' !== $argumentId) {
-                    $visited[$argumentId] = true;
-
-                    $service = $this->container->getDefinition($argumentId);
+                $id = $this->container->normalizeId($argument);
 
-                    // if the proxy manager is enabled, disable searching for references in lazy services,
-                    // as these services will be instantiated lazily and don't have direct related references.
-                    if ($service->isLazy() && !$this->getProxyDumper() instanceof NullDumper) {
-                        continue;
-                    }
-
-                    $arguments = array_merge($service->getMethodCalls(), $service->getArguments(), $service->getProperties());
+                while ($this->container->hasAlias($id)) {
+                    $id = (string) $this->container->getAlias($id);
+                }
 
-                    if ($this->hasReference($id, $arguments, $deep, $visited)) {
-                        return true;
-                    }
+                if (!isset($calls[$id])) {
+                    $calls[$id] = [0, $argument->getInvalidBehavior(), $byConstructor];
+                } else {
+                    $calls[$id][1] = min($calls[$id][1], $argument->getInvalidBehavior());
                 }
+
+                ++$calls[$id][0];
+            } elseif (!$argument instanceof Definition) {
+                // no-op
+            } elseif (isset($definitions[$argument])) {
+                $definitions[$argument] = 1 + $definitions[$argument];
+            } else {
+                $definitions[$argument] = 1;
+                $arguments = [$argument->getArguments(), $argument->getFactory()];
+                $this->getDefinitionsFromArguments($arguments, $definitions, $calls, null === $byConstructor || $byConstructor);
+                $arguments = [$argument->getProperties(), $argument->getMethodCalls(), $argument->getConfigurator()];
+                $this->getDefinitionsFromArguments($arguments, $definitions, $calls, null !== $byConstructor && $byConstructor);
             }
         }
 
-        return false;
+        return $definitions;
     }
 
     /**
@@ -1286,15 +1614,68 @@ EOF;
     private function dumpValue($value, $interpolate = true)
     {
         if (\is_array($value)) {
-            $code = array();
+            if ($value && $interpolate && false !== $param = array_search($value, $this->container->getParameterBag()->all(), true)) {
+                return $this->dumpValue("%$param%");
+            }
+            $code = [];
             foreach ($value as $k => $v) {
                 $code[] = sprintf('%s => %s', $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate));
             }
 
-            return sprintf('array(%s)', implode(', ', $code));
+            return sprintf('[%s]', implode(', ', $code));
+        } elseif ($value instanceof ArgumentInterface) {
+            $scope = [$this->definitionVariables, $this->referenceVariables];
+            $this->definitionVariables = $this->referenceVariables = null;
+
+            try {
+                if ($value instanceof ServiceClosureArgument) {
+                    $value = $value->getValues()[0];
+                    $code = $this->dumpValue($value, $interpolate);
+
+                    if ($value instanceof TypedReference) {
+                        $code = sprintf('$f = function (\\%s $v%s) { return $v; }; return $f(%s);', $value->getType(), ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $value->getInvalidBehavior() ? ' = null' : '', $code);
+                    } else {
+                        $code = sprintf('return %s;', $code);
+                    }
+
+                    return sprintf("function () {\n            %s\n        }", $code);
+                }
+
+                if ($value instanceof IteratorArgument) {
+                    $operands = [0];
+                    $code = [];
+                    $code[] = 'new RewindableGenerator(function () {';
+
+                    if (!$values = $value->getValues()) {
+                        $code[] = '            return new \EmptyIterator();';
+                    } else {
+                        $countCode = [];
+                        $countCode[] = 'function () {';
+
+                        foreach ($values as $k => $v) {
+                            ($c = $this->getServiceConditionals($v)) ? $operands[] = "(int) ($c)" : ++$operands[0];
+                            $v = $this->wrapServiceConditionals($v, sprintf("        yield %s => %s;\n", $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate)));
+                            foreach (explode("\n", $v) as $v) {
+                                if ($v) {
+                                    $code[] = '    '.$v;
+                                }
+                            }
+                        }
+
+                        $countCode[] = sprintf('            return %s;', implode(' + ', $operands));
+                        $countCode[] = '        }';
+                    }
+
+                    $code[] = sprintf('        }, %s)', \count($operands) > 1 ? implode("\n", $countCode) : $operands[0]);
+
+                    return implode("\n", $code);
+                }
+            } finally {
+                list($this->definitionVariables, $this->referenceVariables) = $scope;
+            }
         } elseif ($value instanceof Definition) {
             if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) {
-                return $this->dumpValue($this->definitionVariables->offsetGet($value), $interpolate);
+                return $this->dumpValue($this->definitionVariables[$value], $interpolate);
             }
             if ($value->getMethodCalls()) {
                 throw new RuntimeException('Cannot dump definitions which have method calls.');
@@ -1306,7 +1687,7 @@ EOF;
                 throw new RuntimeException('Cannot dump definitions which have a configurator.');
             }
 
-            $arguments = array();
+            $arguments = [];
             foreach ($value->getArguments() as $argument) {
                 $arguments[] = $this->dumpValue($argument);
             }
@@ -1315,40 +1696,33 @@ EOF;
                 $factory = $value->getFactory();
 
                 if (\is_string($factory)) {
-                    return sprintf('\\%s(%s)', $factory, implode(', ', $arguments));
+                    return sprintf('%s(%s)', $this->dumpLiteralClass($this->dumpValue($factory)), implode(', ', $arguments));
                 }
 
                 if (\is_array($factory)) {
                     if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $factory[1])) {
-                        throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s)', $factory[1] ?: 'n/a'));
+                        throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s).', $factory[1] ?: 'n/a'));
                     }
 
+                    $class = $this->dumpValue($factory[0]);
                     if (\is_string($factory[0])) {
-                        return sprintf('%s::%s(%s)', $this->dumpLiteralClass($this->dumpValue($factory[0])), $factory[1], implode(', ', $arguments));
+                        return sprintf('%s::%s(%s)', $this->dumpLiteralClass($class), $factory[1], implode(', ', $arguments));
                     }
 
                     if ($factory[0] instanceof Definition) {
-                        return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($factory[0]), $factory[1], \count($arguments) > 0 ? ', '.implode(', ', $arguments) : '');
+                        if (0 === strpos($class, 'new ')) {
+                            return sprintf('(%s)->%s(%s)', $class, $factory[1], implode(', ', $arguments));
+                        }
+
+                        return sprintf("\\call_user_func([%s, '%s']%s)", $class, $factory[1], \count($arguments) > 0 ? ', '.implode(', ', $arguments) : '');
                     }
 
                     if ($factory[0] instanceof Reference) {
-                        return sprintf('%s->%s(%s)', $this->dumpValue($factory[0]), $factory[1], implode(', ', $arguments));
+                        return sprintf('%s->%s(%s)', $class, $factory[1], implode(', ', $arguments));
                     }
                 }
 
-                throw new RuntimeException('Cannot dump definition because of invalid factory');
-            }
-
-            if (null !== $value->getFactoryMethod(false)) {
-                if (null !== $value->getFactoryClass(false)) {
-                    return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($value->getFactoryClass(false)), $value->getFactoryMethod(false), \count($arguments) > 0 ? ', '.implode(', ', $arguments) : '');
-                } elseif (null !== $value->getFactoryService(false)) {
-                    $service = $this->dumpValue($value->getFactoryService(false));
-
-                    return sprintf('%s->%s(%s)', 0 === strpos($service, '$') ? sprintf('$this->get(%s)', $service) : $this->getServiceCall($value->getFactoryService(false)), $value->getFactoryMethod(false), implode(', ', $arguments));
-                }
-
-                throw new RuntimeException('Cannot dump definitions which have factory method without factory service or factory class.');
+                throw new RuntimeException('Cannot dump definition because of invalid factory.');
             }
 
             $class = $value->getClass();
@@ -1360,24 +1734,29 @@ EOF;
         } elseif ($value instanceof Variable) {
             return '$'.$value;
         } elseif ($value instanceof Reference) {
-            if (null !== $this->referenceVariables && isset($this->referenceVariables[$id = (string) $value])) {
+            $id = $this->container->normalizeId($value);
+
+            while ($this->container->hasAlias($id)) {
+                $id = (string) $this->container->getAlias($id);
+            }
+
+            if (null !== $this->referenceVariables && isset($this->referenceVariables[$id])) {
                 return $this->dumpValue($this->referenceVariables[$id], $interpolate);
             }
 
-            return $this->getServiceCall((string) $value, $value);
+            return $this->getServiceCall($id, $value);
         } elseif ($value instanceof Expression) {
-            return $this->getExpressionLanguage()->compile((string) $value, array('this' => 'container'));
+            return $this->getExpressionLanguage()->compile((string) $value, ['this' => 'container']);
         } elseif ($value instanceof Parameter) {
             return $this->dumpParameter($value);
         } elseif (true === $interpolate && \is_string($value)) {
             if (preg_match('/^%([^%]+)%$/', $value, $match)) {
                 // we do this to deal with non string values (Boolean, integer, ...)
                 // the preg_replace_callback converts them to strings
-                return $this->dumpParameter(strtolower($match[1]));
+                return $this->dumpParameter($match[1]);
             } else {
-                $that = $this;
-                $replaceParameters = function ($match) use ($that) {
-                    return "'.".$that->dumpParameter(strtolower($match[2])).".'";
+                $replaceParameters = function ($match) {
+                    return "'.".$this->dumpParameter($match[2]).".'";
                 };
 
                 $code = str_replace('%%', '%', preg_replace_callback('/(?<!%)(%)([^%]+)\1/', $replaceParameters, $this->export($value)));
@@ -1403,10 +1782,10 @@ EOF;
     private function dumpLiteralClass($class)
     {
         if (false !== strpos($class, '$')) {
-            throw new RuntimeException('Cannot dump definitions which have a variable class name.');
+            return sprintf('${($_ = %s) && false ?: "_"}', $class);
         }
         if (0 !== strpos($class, "'") || !preg_match('/^\'(?:\\\{2})?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) {
-            throw new RuntimeException(sprintf('Cannot dump definition because of invalid class name (%s)', $class ?: 'n/a'));
+            throw new RuntimeException(sprintf('Cannot dump definition because of invalid class name (%s).', $class ?: 'n/a'));
         }
 
         $class = substr(str_replace('\\\\', '\\', $class), 1, -1);
@@ -1421,28 +1800,24 @@ EOF;
      *
      * @return string
      */
-    public function dumpParameter($name)
+    private function dumpParameter($name)
     {
         $name = (string) $name;
 
-        if ($this->container->isFrozen() && $this->container->hasParameter($name)) {
-            return $this->dumpValue($this->container->getParameter($name), false);
-        }
+        if ($this->container->isCompiled() && $this->container->hasParameter($name)) {
+            $value = $this->container->getParameter($name);
+            $dumpedValue = $this->dumpValue($value, false);
 
-        return sprintf('$this->getParameter(%s)', var_export($name, true));
-    }
+            if (!$value || !\is_array($value)) {
+                return $dumpedValue;
+            }
 
-    /**
-     * @deprecated since version 2.6.2, to be removed in 3.0.
-     *             Use \Symfony\Component\DependencyInjection\ContainerBuilder::addExpressionLanguageProvider instead.
-     *
-     * @param ExpressionFunctionProviderInterface $provider
-     */
-    public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
-    {
-        @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.6.2 and will be removed in 3.0. Use the Symfony\Component\DependencyInjection\ContainerBuilder::addExpressionLanguageProvider method instead.', E_USER_DEPRECATED);
+            if (!preg_match("/\\\$this->(?:getEnv\('(?:\w++:)*+\w++'\)|targetDirs\[\d++\])/", $dumpedValue)) {
+                return sprintf('$this->parameters[%s]', $this->doExport($name));
+            }
+        }
 
-        $this->expressionLanguageProviders[] = $provider;
+        return sprintf('$this->getParameter(%s)', $this->doExport($name));
     }
 
     /**
@@ -1458,16 +1833,59 @@ EOF;
         while ($this->container->hasAlias($id)) {
             $id = (string) $this->container->getAlias($id);
         }
+        $id = $this->container->normalizeId($id);
 
         if ('service_container' === $id) {
             return '$this';
         }
 
-        if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) {
-            return sprintf('$this->get(%s, ContainerInterface::NULL_ON_INVALID_REFERENCE)', var_export($id, true));
+        if ($this->container->hasDefinition($id) && $definition = $this->container->getDefinition($id)) {
+            if ($definition->isSynthetic()) {
+                $code = sprintf('$this->get(%s%s)', $this->doExport($id), null !== $reference ? ', '.$reference->getInvalidBehavior() : '');
+            } elseif (null !== $reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $reference->getInvalidBehavior()) {
+                $code = 'null';
+                if (!$definition->isShared()) {
+                    return $code;
+                }
+            } elseif ($this->isTrivialInstance($definition)) {
+                $code = substr($this->addNewInstance($definition, '', '', $id), 8, -2);
+                if ($definition->isShared()) {
+                    $code = sprintf('$this->services[%s] = %s', $this->doExport($id), $code);
+                }
+                $code = "($code)";
+            } elseif ($this->asFiles && $definition->isShared() && !$this->isHotPath($definition)) {
+                $code = sprintf("\$this->load('%s.php')", $this->generateMethodName($id));
+            } else {
+                $code = sprintf('$this->%s()', $this->generateMethodName($id));
+            }
+        } elseif (null !== $reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $reference->getInvalidBehavior()) {
+            return 'null';
+        } elseif (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) {
+            $code = sprintf('$this->get(%s, /* ContainerInterface::NULL_ON_INVALID_REFERENCE */ %d)', $this->doExport($id), ContainerInterface::NULL_ON_INVALID_REFERENCE);
+        } else {
+            $code = sprintf('$this->get(%s)', $this->doExport($id));
         }
 
-        return sprintf('$this->get(%s)', var_export($id, true));
+        // The following is PHP 5.5 syntax for what could be written as "(\$this->services['$id'] ?? $code)" on PHP>=7.0
+
+        return sprintf("\${(\$_ = isset(\$this->services[%s]) ? \$this->services[%1\$s] : %s) && false ?: '_'}", $this->doExport($id), $code);
+    }
+
+    /**
+     * Initializes the method names map to avoid conflicts with the Container methods.
+     *
+     * @param string $class the container base class
+     */
+    private function initializeMethodNamesMap($class)
+    {
+        $this->serviceIdToMethodNameMap = [];
+        $this->usedMethodNames = [];
+
+        if ($reflectionClass = $this->container->getReflectionClass($class)) {
+            foreach ($reflectionClass->getMethods() as $method) {
+                $this->usedMethodNames[strtolower($method->getName())] = true;
+            }
+        }
     }
 
     /**
@@ -1479,15 +1897,27 @@ EOF;
      *
      * @throws InvalidArgumentException
      */
-    private function camelize($id)
+    private function generateMethodName($id)
     {
-        $name = Container::camelize($id);
+        if (isset($this->serviceIdToMethodNameMap[$id])) {
+            return $this->serviceIdToMethodNameMap[$id];
+        }
+
+        $i = strrpos($id, '\\');
+        $name = Container::camelize(false !== $i && isset($id[1 + $i]) ? substr($id, 1 + $i) : $id);
+        $name = preg_replace('/[^a-zA-Z0-9_\x7f-\xff]/', '', $name);
+        $methodName = 'get'.$name.'Service';
+        $suffix = 1;
 
-        if (!preg_match('/^[a-zA-Z0-9_\x7f-\xff]+$/', $name)) {
-            throw new InvalidArgumentException(sprintf('Service id "%s" cannot be converted to a valid PHP method name.', $id));
+        while (isset($this->usedMethodNames[strtolower($methodName)])) {
+            ++$suffix;
+            $methodName = 'get'.$name.$suffix.'Service';
         }
 
-        return $name;
+        $this->serviceIdToMethodNameMap[$id] = $methodName;
+        $this->usedMethodNames[strtolower($methodName)] = true;
+
+        return $methodName;
     }
 
     /**
@@ -1534,8 +1964,16 @@ EOF;
             if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
                 throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
             }
-            $providers = array_merge($this->container->getExpressionLanguageProviders(), $this->expressionLanguageProviders);
-            $this->expressionLanguage = new ExpressionLanguage(null, $providers);
+            $providers = $this->container->getExpressionLanguageProviders();
+            $this->expressionLanguage = new ExpressionLanguage(null, $providers, function ($arg) {
+                $id = '""' === substr_replace($arg, '', 1, -1) ? stripcslashes(substr($arg, 1, -1)) : null;
+
+                if (null !== $id && ($this->container->hasAlias($id) || $this->container->hasDefinition($id))) {
+                    return $this->getServiceCall($id);
+                }
+
+                return sprintf('$this->get(%s)', $arg);
+            });
 
             if ($this->container->isTrackingResources()) {
                 foreach ($providers as $provider) {
@@ -1547,26 +1985,21 @@ EOF;
         return $this->expressionLanguage;
     }
 
-    private function exportTargetDirs()
+    private function isHotPath(Definition $definition)
     {
-        return null === $this->targetDirRegex ? '' : <<<EOF
-
-        \$dir = __DIR__;
-        for (\$i = 1; \$i <= {$this->targetDirMaxMatches}; ++\$i) {
-            \$this->targetDirs[\$i] = \$dir = dirname(\$dir);
-        }
-EOF;
+        return $this->hotPathTag && $definition->hasTag($this->hotPathTag) && !$definition->isDeprecated();
     }
 
     private function export($value)
     {
         if (null !== $this->targetDirRegex && \is_string($value) && preg_match($this->targetDirRegex, $value, $matches, PREG_OFFSET_CAPTURE)) {
-            $prefix = $matches[0][1] ? var_export(substr($value, 0, $matches[0][1]), true).'.' : '';
+            $prefix = $matches[0][1] ? $this->doExport(substr($value, 0, $matches[0][1]), true).'.' : '';
             $suffix = $matches[0][1] + \strlen($matches[0][0]);
-            $suffix = isset($value[$suffix]) ? '.'.var_export(substr($value, $suffix), true) : '';
-            $dirname = '__DIR__';
+            $suffix = isset($value[$suffix]) ? '.'.$this->doExport(substr($value, $suffix), true) : '';
+            $dirname = $this->asFiles ? '$this->containerDir' : '__DIR__';
+            $offset = 1 + $this->targetDirMaxMatches - \count($matches);
 
-            if (0 < $offset = 1 + $this->targetDirMaxMatches - \count($matches)) {
+            if ($this->asFiles || 0 < $offset) {
                 $dirname = sprintf('$this->targetDirs[%d]', $offset);
             }
 
@@ -1577,13 +2010,32 @@ EOF;
             return $dirname;
         }
 
+        return $this->doExport($value, true);
+    }
+
+    private function doExport($value, $resolveEnv = false)
+    {
         if (\is_string($value) && false !== strpos($value, "\n")) {
             $cleanParts = explode("\n", $value);
             $cleanParts = array_map(function ($part) { return var_export($part, true); }, $cleanParts);
+            $export = implode('."\n".', $cleanParts);
+        } else {
+            $export = var_export($value, true);
+        }
 
-            return implode('."\n".', $cleanParts);
+        if ($resolveEnv && "'" === $export[0] && $export !== $resolvedExport = $this->container->resolveEnvPlaceholders($export, "'.\$this->getEnv('string:%s').'")) {
+            $export = $resolvedExport;
+            if (".''" === substr($export, -3)) {
+                $export = substr($export, 0, -3);
+                if ("'" === $export[1]) {
+                    $export = substr_replace($export, '', 18, 7);
+                }
+            }
+            if ("'" === $export[1]) {
+                $export = substr($export, 3);
+            }
         }
 
-        return var_export($value, true);
+        return $export;
     }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Dumper/XmlDumper.php b/civicrm/vendor/symfony/dependency-injection/Dumper/XmlDumper.php
index 5a85d67ddcb8d21915b19b7aec898272977bc08c..eff421ec4e71fc9c3885cb2d9ce159e907eb936d 100644
--- a/civicrm/vendor/symfony/dependency-injection/Dumper/XmlDumper.php
+++ b/civicrm/vendor/symfony/dependency-injection/Dumper/XmlDumper.php
@@ -12,6 +12,9 @@
 namespace Symfony\Component\DependencyInjection\Dumper;
 
 use Symfony\Component\DependencyInjection\Alias;
+use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
+use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
+use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\DependencyInjection\Definition;
 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
@@ -37,14 +40,14 @@ class XmlDumper extends Dumper
      *
      * @return string An xml string representing of the service container
      */
-    public function dump(array $options = array())
+    public function dump(array $options = [])
     {
         $this->document = new \DOMDocument('1.0', 'utf-8');
         $this->document->formatOutput = true;
 
         $container = $this->document->createElementNS('http://symfony.com/schema/dic/services', 'container');
         $container->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
-        $container->setAttribute('xsi:schemaLocation', 'http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd');
+        $container->setAttribute('xsi:schemaLocation', 'http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd');
 
         $this->addParameters($container);
         $this->addServices($container);
@@ -53,7 +56,7 @@ class XmlDumper extends Dumper
         $xml = $this->document->saveXML();
         $this->document = null;
 
-        return $xml;
+        return $this->container->resolveEnvPlaceholders($xml);
     }
 
     private function addParameters(\DOMElement $parent)
@@ -63,7 +66,7 @@ class XmlDumper extends Dumper
             return;
         }
 
-        if ($this->container->isFrozen()) {
+        if ($this->container->isCompiled()) {
             $data = $this->escape($data);
         }
 
@@ -87,9 +90,8 @@ class XmlDumper extends Dumper
     /**
      * Adds a service.
      *
-     * @param Definition  $definition
-     * @param string      $id
-     * @param \DOMElement $parent
+     * @param Definition $definition
+     * @param string     $id
      */
     private function addService($definition, $id, \DOMElement $parent)
     {
@@ -104,30 +106,15 @@ class XmlDumper extends Dumper
 
             $service->setAttribute('class', $class);
         }
-        if ($definition->getFactoryMethod(false)) {
-            $service->setAttribute('factory-method', $definition->getFactoryMethod(false));
-        }
-        if ($definition->getFactoryClass(false)) {
-            $service->setAttribute('factory-class', $definition->getFactoryClass(false));
-        }
-        if ($definition->getFactoryService(false)) {
-            $service->setAttribute('factory-service', $definition->getFactoryService(false));
-        }
         if (!$definition->isShared()) {
             $service->setAttribute('shared', 'false');
         }
-        if (ContainerInterface::SCOPE_CONTAINER !== $scope = $definition->getScope(false)) {
-            $service->setAttribute('scope', $scope);
-        }
-        if (!$definition->isPublic()) {
-            $service->setAttribute('public', 'false');
+        if (!$definition->isPrivate()) {
+            $service->setAttribute('public', $definition->isPublic() ? 'true' : 'false');
         }
         if ($definition->isSynthetic()) {
             $service->setAttribute('synthetic', 'true');
         }
-        if ($definition->isSynchronized(false)) {
-            $service->setAttribute('synchronized', 'true');
-        }
         if ($definition->isLazy()) {
             $service->setAttribute('lazy', 'true');
         }
@@ -176,7 +163,9 @@ class XmlDumper extends Dumper
                 $this->addService($callable[0], null, $factory);
                 $factory->setAttribute('method', $callable[1]);
             } elseif (\is_array($callable)) {
-                $factory->setAttribute($callable[0] instanceof Reference ? 'service' : 'class', $callable[0]);
+                if (null !== $callable[0]) {
+                    $factory->setAttribute($callable[0] instanceof Reference ? 'service' : 'class', $callable[0]);
+                }
                 $factory->setAttribute('method', $callable[1]);
             } else {
                 $factory->setAttribute('function', $callable);
@@ -195,13 +184,17 @@ class XmlDumper extends Dumper
             $service->setAttribute('autowire', 'true');
         }
 
-        foreach ($definition->getAutowiringTypes() as $autowiringTypeValue) {
+        foreach ($definition->getAutowiringTypes(false) as $autowiringTypeValue) {
             $autowiringType = $this->document->createElement('autowiring-type');
             $autowiringType->appendChild($this->document->createTextNode($autowiringTypeValue));
 
             $service->appendChild($autowiringType);
         }
 
+        if ($definition->isAutoconfigured()) {
+            $service->setAttribute('autoconfigure', 'true');
+        }
+
         if ($definition->isAbstract()) {
             $service->setAttribute('abstract', 'true');
         }
@@ -227,17 +220,15 @@ class XmlDumper extends Dumper
     /**
      * Adds a service alias.
      *
-     * @param string      $alias
-     * @param Alias       $id
-     * @param \DOMElement $parent
+     * @param string $alias
      */
     private function addServiceAlias($alias, Alias $id, \DOMElement $parent)
     {
         $service = $this->document->createElement('service');
         $service->setAttribute('id', $alias);
         $service->setAttribute('alias', $id);
-        if (!$id->isPublic()) {
-            $service->setAttribute('public', 'false');
+        if (!$id->isPrivate()) {
+            $service->setAttribute('public', $id->isPublic() ? 'true' : 'false');
         }
         $parent->appendChild($service);
     }
@@ -267,10 +258,8 @@ class XmlDumper extends Dumper
     /**
      * Converts parameters.
      *
-     * @param array       $parameters
-     * @param string      $type
-     * @param \DOMElement $parent
-     * @param string      $keyAttribute
+     * @param string $type
+     * @param string $keyAttribute
      */
     private function convertParameters(array $parameters, $type, \DOMElement $parent, $keyAttribute = 'key')
     {
@@ -281,20 +270,28 @@ class XmlDumper extends Dumper
                 $element->setAttribute($keyAttribute, $key);
             }
 
+            if ($value instanceof ServiceClosureArgument) {
+                $value = $value->getValues()[0];
+            }
             if (\is_array($value)) {
                 $element->setAttribute('type', 'collection');
                 $this->convertParameters($value, $type, $element, 'key');
+            } elseif ($value instanceof TaggedIteratorArgument) {
+                $element->setAttribute('type', 'tagged');
+                $element->setAttribute('tag', $value->getTag());
+            } elseif ($value instanceof IteratorArgument) {
+                $element->setAttribute('type', 'iterator');
+                $this->convertParameters($value->getValues(), $type, $element, 'key');
             } elseif ($value instanceof Reference) {
                 $element->setAttribute('type', 'service');
                 $element->setAttribute('id', (string) $value);
-                $behaviour = $value->getInvalidBehavior();
-                if (ContainerInterface::NULL_ON_INVALID_REFERENCE == $behaviour) {
+                $behavior = $value->getInvalidBehavior();
+                if (ContainerInterface::NULL_ON_INVALID_REFERENCE == $behavior) {
                     $element->setAttribute('on-invalid', 'null');
-                } elseif (ContainerInterface::IGNORE_ON_INVALID_REFERENCE == $behaviour) {
+                } elseif (ContainerInterface::IGNORE_ON_INVALID_REFERENCE == $behavior) {
                     $element->setAttribute('on-invalid', 'ignore');
-                }
-                if (!$value->isStrict(false)) {
-                    $element->setAttribute('strict', 'false');
+                } elseif (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE == $behavior) {
+                    $element->setAttribute('on-invalid', 'ignore_uninitialized');
                 }
             } elseif ($value instanceof Definition) {
                 $element->setAttribute('type', 'service');
@@ -304,9 +301,14 @@ class XmlDumper extends Dumper
                 $text = $this->document->createTextNode(self::phpToXml((string) $value));
                 $element->appendChild($text);
             } else {
-                if (\in_array($value, array('null', 'true', 'false'), true)) {
+                if (\in_array($value, ['null', 'true', 'false'], true)) {
                     $element->setAttribute('type', 'string');
                 }
+
+                if (\is_string($value) && (is_numeric($value) || preg_match('/^0b[01]*$/', $value) || preg_match('/^0x[0-9a-f]++$/i', $value))) {
+                    $element->setAttribute('type', 'string');
+                }
+
                 $text = $this->document->createTextNode(self::phpToXml($value));
                 $element->appendChild($text);
             }
@@ -321,7 +323,7 @@ class XmlDumper extends Dumper
      */
     private function escape(array $arguments)
     {
-        $args = array();
+        $args = [];
         foreach ($arguments as $k => $v) {
             if (\is_array($v)) {
                 $args[$k] = $this->escape($v);
diff --git a/civicrm/vendor/symfony/dependency-injection/Dumper/YamlDumper.php b/civicrm/vendor/symfony/dependency-injection/Dumper/YamlDumper.php
index 35d1218e253108fc48f415a9db34112a333743a9..1e795c7daf3bfe17afa9b70f9705b8d5e6bcf05b 100644
--- a/civicrm/vendor/symfony/dependency-injection/Dumper/YamlDumper.php
+++ b/civicrm/vendor/symfony/dependency-injection/Dumper/YamlDumper.php
@@ -12,6 +12,10 @@
 namespace Symfony\Component\DependencyInjection\Dumper;
 
 use Symfony\Component\DependencyInjection\Alias;
+use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
+use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
+use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
+use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\DependencyInjection\Definition;
 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
@@ -19,6 +23,9 @@ use Symfony\Component\DependencyInjection\Parameter;
 use Symfony\Component\DependencyInjection\Reference;
 use Symfony\Component\ExpressionLanguage\Expression;
 use Symfony\Component\Yaml\Dumper as YmlDumper;
+use Symfony\Component\Yaml\Parser;
+use Symfony\Component\Yaml\Tag\TaggedValue;
+use Symfony\Component\Yaml\Yaml;
 
 /**
  * YamlDumper dumps a service container as a YAML string.
@@ -34,7 +41,7 @@ class YamlDumper extends Dumper
      *
      * @return string A YAML string representing of the service container
      */
-    public function dump(array $options = array())
+    public function dump(array $options = [])
     {
         if (!class_exists('Symfony\Component\Yaml\Dumper')) {
             throw new RuntimeException('Unable to dump the container as the Symfony Yaml Component is not installed.');
@@ -44,14 +51,13 @@ class YamlDumper extends Dumper
             $this->dumper = new YmlDumper();
         }
 
-        return $this->addParameters()."\n".$this->addServices();
+        return $this->container->resolveEnvPlaceholders($this->addParameters()."\n".$this->addServices());
     }
 
     /**
      * Adds a service.
      *
-     * @param string     $id
-     * @param Definition $definition
+     * @param string $id
      *
      * @return string
      */
@@ -66,14 +72,14 @@ class YamlDumper extends Dumper
             $code .= sprintf("        class: %s\n", $this->dumper->dump($class));
         }
 
-        if (!$definition->isPublic()) {
-            $code .= "        public: false\n";
+        if (!$definition->isPrivate()) {
+            $code .= sprintf("        public: %s\n", $definition->isPublic() ? 'true' : 'false');
         }
 
         $tagsCode = '';
         foreach ($definition->getTags() as $name => $tags) {
             foreach ($tags as $attributes) {
-                $att = array();
+                $att = [];
                 foreach ($attributes as $key => $value) {
                     $att[] = sprintf('%s: %s', $this->dumper->dump($key), $this->dumper->dump($value));
                 }
@@ -94,10 +100,6 @@ class YamlDumper extends Dumper
             $code .= "        synthetic: true\n";
         }
 
-        if ($definition->isSynchronized(false)) {
-            $code .= "        synchronized: true\n";
-        }
-
         if ($definition->isDeprecated()) {
             $code .= sprintf("        deprecated: %s\n", $this->dumper->dump($definition->getDeprecationMessage('%service_id%')));
         }
@@ -107,15 +109,15 @@ class YamlDumper extends Dumper
         }
 
         $autowiringTypesCode = '';
-        foreach ($definition->getAutowiringTypes() as $autowiringType) {
+        foreach ($definition->getAutowiringTypes(false) as $autowiringType) {
             $autowiringTypesCode .= sprintf("            - %s\n", $this->dumper->dump($autowiringType));
         }
         if ($autowiringTypesCode) {
             $code .= sprintf("        autowiring_types:\n%s", $autowiringTypesCode);
         }
 
-        if ($definition->getFactoryClass(false)) {
-            $code .= sprintf("        factory_class: %s\n", $this->dumper->dump($definition->getFactoryClass(false)));
+        if ($definition->isAutoconfigured()) {
+            $code .= "        autoconfigure: true\n";
         }
 
         if ($definition->isAbstract()) {
@@ -126,14 +128,6 @@ class YamlDumper extends Dumper
             $code .= "        lazy: true\n";
         }
 
-        if ($definition->getFactoryMethod(false)) {
-            $code .= sprintf("        factory_method: %s\n", $this->dumper->dump($definition->getFactoryMethod(false)));
-        }
-
-        if ($definition->getFactoryService(false)) {
-            $code .= sprintf("        factory_service: %s\n", $this->dumper->dump($definition->getFactoryService(false)));
-        }
-
         if ($definition->getArguments()) {
             $code .= sprintf("        arguments: %s\n", $this->dumper->dump($this->dumpValue($definition->getArguments()), 0));
         }
@@ -150,10 +144,6 @@ class YamlDumper extends Dumper
             $code .= "        shared: false\n";
         }
 
-        if (ContainerInterface::SCOPE_CONTAINER !== $scope = $definition->getScope(false)) {
-            $code .= sprintf("        scope: %s\n", $this->dumper->dump($scope));
-        }
-
         if (null !== $decorated = $definition->getDecoratedService()) {
             list($decorated, $renamedId, $priority) = $decorated;
             $code .= sprintf("        decorates: %s\n", $decorated);
@@ -180,17 +170,16 @@ class YamlDumper extends Dumper
      * Adds a service alias.
      *
      * @param string $alias
-     * @param Alias  $id
      *
      * @return string
      */
     private function addServiceAlias($alias, Alias $id)
     {
-        if ($id->isPublic()) {
+        if ($id->isPrivate()) {
             return sprintf("    %s: '@%s'\n", $alias, $id);
         }
 
-        return sprintf("    %s:\n        alias: %s\n        public: false\n", $alias, $id);
+        return sprintf("    %s:\n        alias: %s\n        public: %s\n", $alias, $id, $id->isPublic() ? 'true' : 'false');
     }
 
     /**
@@ -231,25 +220,23 @@ class YamlDumper extends Dumper
             return '';
         }
 
-        $parameters = $this->prepareParameters($this->container->getParameterBag()->all(), $this->container->isFrozen());
+        $parameters = $this->prepareParameters($this->container->getParameterBag()->all(), $this->container->isCompiled());
 
-        return $this->dumper->dump(array('parameters' => $parameters), 2);
+        return $this->dumper->dump(['parameters' => $parameters], 2);
     }
 
     /**
      * Dumps callable to YAML format.
      *
-     * @param callable $callable
-     *
-     * @return callable
+     * @param mixed $callable
      */
     private function dumpCallable($callable)
     {
         if (\is_array($callable)) {
             if ($callable[0] instanceof Reference) {
-                $callable = array($this->getServiceCall((string) $callable[0], $callable[0]), $callable[1]);
+                $callable = [$this->getServiceCall((string) $callable[0], $callable[0]), $callable[1]];
             } else {
-                $callable = array($callable[0], $callable[1]);
+                $callable = [$callable[0], $callable[1]];
             }
         }
 
@@ -267,8 +254,24 @@ class YamlDumper extends Dumper
      */
     private function dumpValue($value)
     {
+        if ($value instanceof ServiceClosureArgument) {
+            $value = $value->getValues()[0];
+        }
+        if ($value instanceof ArgumentInterface) {
+            if ($value instanceof TaggedIteratorArgument) {
+                return new TaggedValue('tagged', $value->getTag());
+            }
+            if ($value instanceof IteratorArgument) {
+                $tag = 'iterator';
+            } else {
+                throw new RuntimeException(sprintf('Unspecified Yaml tag for type "%s".', \get_class($value)));
+            }
+
+            return new TaggedValue($tag, $this->dumpValue($value->getValues()));
+        }
+
         if (\is_array($value)) {
-            $code = array();
+            $code = [];
             foreach ($value as $k => $v) {
                 $code[$k] = $this->dumpValue($v);
             }
@@ -280,6 +283,8 @@ class YamlDumper extends Dumper
             return $this->getParameterCall((string) $value);
         } elseif ($value instanceof Expression) {
             return $this->getExpressionCall((string) $value);
+        } elseif ($value instanceof Definition) {
+            return new TaggedValue('service', (new Parser())->parse("_:\n".$this->addService('_', $value), Yaml::PARSE_CUSTOM_TAGS)['_']['_']);
         } elseif (\is_object($value) || \is_resource($value)) {
             throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.');
         }
@@ -297,8 +302,12 @@ class YamlDumper extends Dumper
      */
     private function getServiceCall($id, Reference $reference = null)
     {
-        if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) {
-            return sprintf('@?%s', $id);
+        if (null !== $reference) {
+            switch ($reference->getInvalidBehavior()) {
+                case ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE: break;
+                case ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE: return sprintf('@!%s', $id);
+                default: return sprintf('@?%s', $id);
+            }
         }
 
         return sprintf('@%s', $id);
@@ -324,14 +333,13 @@ class YamlDumper extends Dumper
     /**
      * Prepares parameters.
      *
-     * @param array $parameters
-     * @param bool  $escape
+     * @param bool $escape
      *
      * @return array
      */
     private function prepareParameters(array $parameters, $escape = true)
     {
-        $filtered = array();
+        $filtered = [];
         foreach ($parameters as $key => $value) {
             if (\is_array($value)) {
                 $value = $this->prepareParameters($value, $escape);
@@ -352,7 +360,7 @@ class YamlDumper extends Dumper
      */
     private function escape(array $arguments)
     {
-        $args = array();
+        $args = [];
         foreach ($arguments as $k => $v) {
             if (\is_array($v)) {
                 $args[$k] = $this->escape($v);
diff --git a/civicrm/vendor/symfony/dependency-injection/EnvVarProcessor.php b/civicrm/vendor/symfony/dependency-injection/EnvVarProcessor.php
new file mode 100644
index 0000000000000000000000000000000000000000..3410103764e52344355ce623d81e4d9c365322c5
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/EnvVarProcessor.php
@@ -0,0 +1,163 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection;
+
+use Symfony\Component\Config\Util\XmlUtils;
+use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException;
+use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class EnvVarProcessor implements EnvVarProcessorInterface
+{
+    private $container;
+
+    public function __construct(ContainerInterface $container)
+    {
+        $this->container = $container;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public static function getProvidedTypes()
+    {
+        return [
+            'base64' => 'string',
+            'bool' => 'bool',
+            'const' => 'bool|int|float|string|array',
+            'file' => 'string',
+            'float' => 'float',
+            'int' => 'int',
+            'json' => 'array',
+            'resolve' => 'string',
+            'string' => 'string',
+        ];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getEnv($prefix, $name, \Closure $getEnv)
+    {
+        $i = strpos($name, ':');
+
+        if ('file' === $prefix) {
+            if (!is_scalar($file = $getEnv($name))) {
+                throw new RuntimeException(sprintf('Invalid file name: env var "%s" is non-scalar.', $name));
+            }
+            if (!file_exists($file)) {
+                throw new RuntimeException(sprintf('Env "file:%s" not found: "%s" does not exist.', $name, $file));
+            }
+
+            return file_get_contents($file);
+        }
+
+        if (false !== $i || 'string' !== $prefix) {
+            if (null === $env = $getEnv($name)) {
+                return null;
+            }
+        } elseif (isset($_ENV[$name])) {
+            $env = $_ENV[$name];
+        } elseif (isset($_SERVER[$name]) && 0 !== strpos($name, 'HTTP_')) {
+            $env = $_SERVER[$name];
+        } elseif (false === ($env = getenv($name)) || null === $env) { // null is a possible value because of thread safety issues
+            if (!$this->container->hasParameter("env($name)")) {
+                throw new EnvNotFoundException($name);
+            }
+
+            if (null === $env = $this->container->getParameter("env($name)")) {
+                return null;
+            }
+        }
+
+        if (!is_scalar($env)) {
+            throw new RuntimeException(sprintf('Non-scalar env var "%s" cannot be cast to "%s".', $name, $prefix));
+        }
+
+        if ('string' === $prefix) {
+            return (string) $env;
+        }
+
+        if ('bool' === $prefix) {
+            return (bool) self::phpize($env);
+        }
+
+        if ('int' === $prefix) {
+            if (!is_numeric($env = self::phpize($env))) {
+                throw new RuntimeException(sprintf('Non-numeric env var "%s" cannot be cast to int.', $name));
+            }
+
+            return (int) $env;
+        }
+
+        if ('float' === $prefix) {
+            if (!is_numeric($env = self::phpize($env))) {
+                throw new RuntimeException(sprintf('Non-numeric env var "%s" cannot be cast to float.', $name));
+            }
+
+            return (float) $env;
+        }
+
+        if ('const' === $prefix) {
+            if (!\defined($env)) {
+                throw new RuntimeException(sprintf('Env var "%s" maps to undefined constant "%s".', $name, $env));
+            }
+
+            return \constant($env);
+        }
+
+        if ('base64' === $prefix) {
+            return base64_decode($env);
+        }
+
+        if ('json' === $prefix) {
+            $env = json_decode($env, true);
+
+            if (JSON_ERROR_NONE !== json_last_error()) {
+                throw new RuntimeException(sprintf('Invalid JSON in env var "%s": '.json_last_error_msg(), $name));
+            }
+
+            if (!\is_array($env)) {
+                throw new RuntimeException(sprintf('Invalid JSON env var "%s": array expected, "%s" given.', $name, \gettype($env)));
+            }
+
+            return $env;
+        }
+
+        if ('resolve' === $prefix) {
+            return preg_replace_callback('/%%|%([^%\s]+)%/', function ($match) use ($name) {
+                if (!isset($match[1])) {
+                    return '%';
+                }
+                $value = $this->container->getParameter($match[1]);
+                if (!is_scalar($value)) {
+                    throw new RuntimeException(sprintf('Parameter "%s" found when resolving env var "%s" must be scalar, "%s" given.', $match[1], $name, \gettype($value)));
+                }
+
+                return $value;
+            }, $env);
+        }
+
+        throw new RuntimeException(sprintf('Unsupported env var prefix "%s".', $prefix));
+    }
+
+    private static function phpize($value)
+    {
+        if (!class_exists(XmlUtils::class)) {
+            throw new RuntimeException('The Symfony Config component is required to cast env vars to "bool", "int" or "float".');
+        }
+
+        return XmlUtils::phpize($value);
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/EnvVarProcessorInterface.php b/civicrm/vendor/symfony/dependency-injection/EnvVarProcessorInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..654fe55e982fc60e77bf05730e843b7fd406474f
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/EnvVarProcessorInterface.php
@@ -0,0 +1,40 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+
+/**
+ * The EnvVarProcessorInterface is implemented by objects that manage environment-like variables.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+interface EnvVarProcessorInterface
+{
+    /**
+     * Returns the value of the given variable as managed by the current instance.
+     *
+     * @param string   $prefix The namespace of the variable
+     * @param string   $name   The name of the variable within the namespace
+     * @param \Closure $getEnv A closure that allows fetching more env vars
+     *
+     * @return mixed
+     *
+     * @throws RuntimeException on error
+     */
+    public function getEnv($prefix, $name, \Closure $getEnv);
+
+    /**
+     * @return string[] The PHP-types managed by getEnv(), keyed by prefixes
+     */
+    public static function getProvidedTypes();
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Exception/AutowiringFailedException.php b/civicrm/vendor/symfony/dependency-injection/Exception/AutowiringFailedException.php
new file mode 100644
index 0000000000000000000000000000000000000000..145cd8cbdcf2424d082e7f43c6b564167807e420
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Exception/AutowiringFailedException.php
@@ -0,0 +1,32 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Exception;
+
+/**
+ * Thrown when a definition cannot be autowired.
+ */
+class AutowiringFailedException extends RuntimeException
+{
+    private $serviceId;
+
+    public function __construct($serviceId, $message = '', $code = 0, \Exception $previous = null)
+    {
+        $this->serviceId = $serviceId;
+
+        parent::__construct($message, $code, $previous);
+    }
+
+    public function getServiceId()
+    {
+        return $this->serviceId;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Exception/EnvNotFoundException.php b/civicrm/vendor/symfony/dependency-injection/Exception/EnvNotFoundException.php
new file mode 100644
index 0000000000000000000000000000000000000000..577095e88b493062e71e6a8bb33b24812fe9650c
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Exception/EnvNotFoundException.php
@@ -0,0 +1,25 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Exception;
+
+/**
+ * This exception is thrown when an environment variable is not found.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class EnvNotFoundException extends InvalidArgumentException
+{
+    public function __construct($name)
+    {
+        parent::__construct(sprintf('Environment variable not found: "%s".', $name));
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Exception/EnvParameterException.php b/civicrm/vendor/symfony/dependency-injection/Exception/EnvParameterException.php
new file mode 100644
index 0000000000000000000000000000000000000000..3839a4633be4057c9f18691dd21a2c1d3c80c765
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Exception/EnvParameterException.php
@@ -0,0 +1,25 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Exception;
+
+/**
+ * This exception wraps exceptions whose messages contain a reference to an env parameter.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class EnvParameterException extends InvalidArgumentException
+{
+    public function __construct(array $envs, \Exception $previous = null, $message = 'Incompatible use of dynamic environment variables "%s" found in parameters.')
+    {
+        parent::__construct(sprintf($message, implode('", "', $envs)), 0, $previous);
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Exception/ExceptionInterface.php b/civicrm/vendor/symfony/dependency-injection/Exception/ExceptionInterface.php
index f5e9099f11199d2ab47d8b5b2aab6f2beddc6538..5bec478695f6f2d4f546d84a6c9cfafd7f40dd5c 100644
--- a/civicrm/vendor/symfony/dependency-injection/Exception/ExceptionInterface.php
+++ b/civicrm/vendor/symfony/dependency-injection/Exception/ExceptionInterface.php
@@ -11,12 +11,14 @@
 
 namespace Symfony\Component\DependencyInjection\Exception;
 
+use Psr\Container\ContainerExceptionInterface;
+
 /**
  * Base ExceptionInterface for Dependency Injection component.
  *
  * @author Fabien Potencier <fabien@symfony.com>
  * @author Bulat Shakirzyanov <bulat@theopenskyproject.com>
  */
-interface ExceptionInterface
+interface ExceptionInterface extends ContainerExceptionInterface
 {
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Exception/InactiveScopeException.php b/civicrm/vendor/symfony/dependency-injection/Exception/InactiveScopeException.php
deleted file mode 100644
index 6b3dd3ebb1acd65e9ba5ac296e985cdc60a6ed69..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/dependency-injection/Exception/InactiveScopeException.php
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\DependencyInjection\Exception;
-
-/**
- * This exception is thrown when you try to create a service of an inactive scope.
- *
- * @author Johannes M. Schmitt <schmittjoh@gmail.com>
- */
-class InactiveScopeException extends RuntimeException
-{
-    private $serviceId;
-    private $scope;
-
-    public function __construct($serviceId, $scope, \Exception $previous = null)
-    {
-        parent::__construct(sprintf('You cannot create a service ("%s") of an inactive scope ("%s").', $serviceId, $scope), 0, $previous);
-
-        $this->serviceId = $serviceId;
-        $this->scope = $scope;
-    }
-
-    public function getServiceId()
-    {
-        return $this->serviceId;
-    }
-
-    public function getScope()
-    {
-        return $this->scope;
-    }
-}
diff --git a/civicrm/vendor/symfony/dependency-injection/Exception/ParameterNotFoundException.php b/civicrm/vendor/symfony/dependency-injection/Exception/ParameterNotFoundException.php
index 1d97830f4a9ba571d2928489fab331fa271ac17c..b08f2e8559776c18cb63ce75574f75f062982671 100644
--- a/civicrm/vendor/symfony/dependency-injection/Exception/ParameterNotFoundException.php
+++ b/civicrm/vendor/symfony/dependency-injection/Exception/ParameterNotFoundException.php
@@ -22,20 +22,23 @@ class ParameterNotFoundException extends InvalidArgumentException
     private $sourceId;
     private $sourceKey;
     private $alternatives;
+    private $nonNestedAlternative;
 
     /**
-     * @param string     $key          The requested parameter key
-     * @param string     $sourceId     The service id that references the non-existent parameter
-     * @param string     $sourceKey    The parameter key that references the non-existent parameter
-     * @param \Exception $previous     The previous exception
-     * @param string[]   $alternatives Some parameter name alternatives
+     * @param string      $key                  The requested parameter key
+     * @param string      $sourceId             The service id that references the non-existent parameter
+     * @param string      $sourceKey            The parameter key that references the non-existent parameter
+     * @param \Exception  $previous             The previous exception
+     * @param string[]    $alternatives         Some parameter name alternatives
+     * @param string|null $nonNestedAlternative The alternative parameter name when the user expected dot notation for nested parameters
      */
-    public function __construct($key, $sourceId = null, $sourceKey = null, \Exception $previous = null, array $alternatives = array())
+    public function __construct($key, $sourceId = null, $sourceKey = null, \Exception $previous = null, array $alternatives = [], $nonNestedAlternative = null)
     {
         $this->key = $key;
         $this->sourceId = $sourceId;
         $this->sourceKey = $sourceKey;
         $this->alternatives = $alternatives;
+        $this->nonNestedAlternative = $nonNestedAlternative;
 
         parent::__construct('', 0, $previous);
 
@@ -59,6 +62,8 @@ class ParameterNotFoundException extends InvalidArgumentException
                 $this->message .= ' Did you mean one of these: "';
             }
             $this->message .= implode('", "', $this->alternatives).'"?';
+        } elseif (null !== $this->nonNestedAlternative) {
+            $this->message .= ' You cannot access nested array items, do you want to inject "'.$this->nonNestedAlternative.'" instead?';
         }
     }
 
diff --git a/civicrm/vendor/symfony/dependency-injection/Exception/ScopeCrossingInjectionException.php b/civicrm/vendor/symfony/dependency-injection/Exception/ScopeCrossingInjectionException.php
deleted file mode 100644
index 661fbab3697f867391a0aa3e0d86edd058722325..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/dependency-injection/Exception/ScopeCrossingInjectionException.php
+++ /dev/null
@@ -1,65 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\DependencyInjection\Exception;
-
-/**
- * This exception is thrown when the a scope crossing injection is detected.
- *
- * @author Johannes M. Schmitt <schmittjoh@gmail.com>
- */
-class ScopeCrossingInjectionException extends RuntimeException
-{
-    private $sourceServiceId;
-    private $sourceScope;
-    private $destServiceId;
-    private $destScope;
-
-    public function __construct($sourceServiceId, $sourceScope, $destServiceId, $destScope, \Exception $previous = null)
-    {
-        parent::__construct(sprintf(
-            'Scope Crossing Injection detected: The definition "%s" references the service "%s" which belongs to another scope hierarchy. '
-           .'This service might not be available consistently. Generally, it is safer to either move the definition "%s" to scope "%s", or '
-           .'declare "%s" as a child scope of "%s". If you can be sure that the other scope is always active, you can set the reference to strict=false to get rid of this error.',
-           $sourceServiceId,
-           $destServiceId,
-           $sourceServiceId,
-           $destScope,
-           $sourceScope,
-           $destScope
-        ), 0, $previous);
-
-        $this->sourceServiceId = $sourceServiceId;
-        $this->sourceScope = $sourceScope;
-        $this->destServiceId = $destServiceId;
-        $this->destScope = $destScope;
-    }
-
-    public function getSourceServiceId()
-    {
-        return $this->sourceServiceId;
-    }
-
-    public function getSourceScope()
-    {
-        return $this->sourceScope;
-    }
-
-    public function getDestServiceId()
-    {
-        return $this->destServiceId;
-    }
-
-    public function getDestScope()
-    {
-        return $this->destScope;
-    }
-}
diff --git a/civicrm/vendor/symfony/dependency-injection/Exception/ScopeWideningInjectionException.php b/civicrm/vendor/symfony/dependency-injection/Exception/ScopeWideningInjectionException.php
deleted file mode 100644
index 86a668419510f8949250b846581720cf7a631ccd..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/dependency-injection/Exception/ScopeWideningInjectionException.php
+++ /dev/null
@@ -1,64 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\DependencyInjection\Exception;
-
-/**
- * Thrown when a scope widening injection is detected.
- *
- * @author Johannes M. Schmitt <schmittjoh@gmail.com>
- */
-class ScopeWideningInjectionException extends RuntimeException
-{
-    private $sourceServiceId;
-    private $sourceScope;
-    private $destServiceId;
-    private $destScope;
-
-    public function __construct($sourceServiceId, $sourceScope, $destServiceId, $destScope, \Exception $previous = null)
-    {
-        parent::__construct(sprintf(
-            'Scope Widening Injection detected: The definition "%s" references the service "%s" which belongs to a narrower scope. '
-           .'Generally, it is safer to either move "%s" to scope "%s" or alternatively rely on the provider pattern by injecting the container itself, and requesting the service "%s" each time it is needed. '
-           .'In rare, special cases however that might not be necessary, then you can set the reference to strict=false to get rid of this error.',
-           $sourceServiceId,
-           $destServiceId,
-           $sourceServiceId,
-           $destScope,
-           $destServiceId
-        ), 0, $previous);
-
-        $this->sourceServiceId = $sourceServiceId;
-        $this->sourceScope = $sourceScope;
-        $this->destServiceId = $destServiceId;
-        $this->destScope = $destScope;
-    }
-
-    public function getSourceServiceId()
-    {
-        return $this->sourceServiceId;
-    }
-
-    public function getSourceScope()
-    {
-        return $this->sourceScope;
-    }
-
-    public function getDestServiceId()
-    {
-        return $this->destServiceId;
-    }
-
-    public function getDestScope()
-    {
-        return $this->destScope;
-    }
-}
diff --git a/civicrm/vendor/symfony/dependency-injection/Exception/ServiceNotFoundException.php b/civicrm/vendor/symfony/dependency-injection/Exception/ServiceNotFoundException.php
index 620ed98e68e76742ea35acab81b0d3983fae4deb..280dabf33fc512933e3d12e346fe06f79702cfa7 100644
--- a/civicrm/vendor/symfony/dependency-injection/Exception/ServiceNotFoundException.php
+++ b/civicrm/vendor/symfony/dependency-injection/Exception/ServiceNotFoundException.php
@@ -11,19 +11,24 @@
 
 namespace Symfony\Component\DependencyInjection\Exception;
 
+use Psr\Container\NotFoundExceptionInterface;
+
 /**
  * This exception is thrown when a non-existent service is requested.
  *
  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  */
-class ServiceNotFoundException extends InvalidArgumentException
+class ServiceNotFoundException extends InvalidArgumentException implements NotFoundExceptionInterface
 {
     private $id;
     private $sourceId;
+    private $alternatives;
 
-    public function __construct($id, $sourceId = null, \Exception $previous = null, array $alternatives = array())
+    public function __construct($id, $sourceId = null, \Exception $previous = null, array $alternatives = [], $msg = null)
     {
-        if (null === $sourceId) {
+        if (null !== $msg) {
+            // no-op
+        } elseif (null === $sourceId) {
             $msg = sprintf('You have requested a non-existent service "%s".', $id);
         } else {
             $msg = sprintf('The service "%s" has a dependency on a non-existent service "%s".', $sourceId, $id);
@@ -42,6 +47,7 @@ class ServiceNotFoundException extends InvalidArgumentException
 
         $this->id = $id;
         $this->sourceId = $sourceId;
+        $this->alternatives = $alternatives;
     }
 
     public function getId()
@@ -53,4 +59,9 @@ class ServiceNotFoundException extends InvalidArgumentException
     {
         return $this->sourceId;
     }
+
+    public function getAlternatives()
+    {
+        return $this->alternatives;
+    }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/ExpressionLanguage.php b/civicrm/vendor/symfony/dependency-injection/ExpressionLanguage.php
index acc97bcf4973dffabf5bb74007bdb5a4b7592b3d..0c1780b8b1aacfbf5b0f7b633efb1f099a508c64 100644
--- a/civicrm/vendor/symfony/dependency-injection/ExpressionLanguage.php
+++ b/civicrm/vendor/symfony/dependency-injection/ExpressionLanguage.php
@@ -12,7 +12,6 @@
 namespace Symfony\Component\DependencyInjection;
 
 use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage;
-use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface;
 
 /**
  * Adds some function to the default ExpressionLanguage.
@@ -23,10 +22,13 @@ use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface;
  */
 class ExpressionLanguage extends BaseExpressionLanguage
 {
-    public function __construct(ParserCacheInterface $cache = null, array $providers = array())
+    /**
+     * {@inheritdoc}
+     */
+    public function __construct($cache = null, array $providers = [], callable $serviceCompiler = null)
     {
         // prepend the default provider to let users override it easily
-        array_unshift($providers, new ExpressionLanguageProvider());
+        array_unshift($providers, new ExpressionLanguageProvider($serviceCompiler));
 
         parent::__construct($cache, $providers);
     }
diff --git a/civicrm/vendor/symfony/dependency-injection/ExpressionLanguageProvider.php b/civicrm/vendor/symfony/dependency-injection/ExpressionLanguageProvider.php
index ce6d69522e19127631ad4ae534e441119e4f6a5c..9198ca0a40a9a9acdad6fc021adea59587904ebe 100644
--- a/civicrm/vendor/symfony/dependency-injection/ExpressionLanguageProvider.php
+++ b/civicrm/vendor/symfony/dependency-injection/ExpressionLanguageProvider.php
@@ -24,10 +24,17 @@ use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
  */
 class ExpressionLanguageProvider implements ExpressionFunctionProviderInterface
 {
+    private $serviceCompiler;
+
+    public function __construct(callable $serviceCompiler = null)
+    {
+        $this->serviceCompiler = $serviceCompiler;
+    }
+
     public function getFunctions()
     {
-        return array(
-            new ExpressionFunction('service', function ($arg) {
+        return [
+            new ExpressionFunction('service', $this->serviceCompiler ?: function ($arg) {
                 return sprintf('$this->get(%s)', $arg);
             }, function (array $variables, $value) {
                 return $variables['container']->get($value);
@@ -38,6 +45,6 @@ class ExpressionLanguageProvider implements ExpressionFunctionProviderInterface
             }, function (array $variables, $value) {
                 return $variables['container']->getParameter($value);
             }),
-        );
+        ];
     }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Extension/Extension.php b/civicrm/vendor/symfony/dependency-injection/Extension/Extension.php
index b56a2ccff00ca8a4bd368b28feebb6058f4eef78..00fa9dc8da500d0632f342f37559e36fed9292e5 100644
--- a/civicrm/vendor/symfony/dependency-injection/Extension/Extension.php
+++ b/civicrm/vendor/symfony/dependency-injection/Extension/Extension.php
@@ -13,7 +13,6 @@ namespace Symfony\Component\DependencyInjection\Extension;
 
 use Symfony\Component\Config\Definition\ConfigurationInterface;
 use Symfony\Component\Config\Definition\Processor;
-use Symfony\Component\Config\Resource\FileResource;
 use Symfony\Component\DependencyInjection\Container;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Exception\BadMethodCallException;
@@ -26,6 +25,8 @@ use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
  */
 abstract class Extension implements ExtensionInterface, ConfigurationExtensionInterface
 {
+    private $processedConfigs = [];
+
     /**
      * {@inheritdoc}
      */
@@ -64,7 +65,7 @@ abstract class Extension implements ExtensionInterface, ConfigurationExtensionIn
      */
     public function getAlias()
     {
-        $className = \get_class($this);
+        $className = static::class;
         if ('Extension' != substr($className, -9)) {
             throw new BadMethodCallException('This extension does not follow the naming convention; you must overwrite the getAlias() method.');
         }
@@ -78,25 +79,39 @@ abstract class Extension implements ExtensionInterface, ConfigurationExtensionIn
      */
     public function getConfiguration(array $config, ContainerBuilder $container)
     {
-        $reflected = new \ReflectionClass($this);
-        $namespace = $reflected->getNamespaceName();
-
-        $class = $namespace.'\\Configuration';
-        if (class_exists($class)) {
-            $r = new \ReflectionClass($class);
-            $container->addResource(new FileResource($r->getFileName()));
+        $class = static::class;
 
-            if (!method_exists($class, '__construct')) {
-                return new $class();
-            }
+        if (false !== strpos($class, "\0")) {
+            return null; // ignore anonymous classes
         }
+
+        $class = substr_replace($class, '\Configuration', strrpos($class, '\\'));
+        $class = $container->getReflectionClass($class);
+        $constructor = $class ? $class->getConstructor() : null;
+
+        return $class && (!$constructor || !$constructor->getNumberOfRequiredParameters()) ? $class->newInstance() : null;
     }
 
+    /**
+     * @return array
+     */
     final protected function processConfiguration(ConfigurationInterface $configuration, array $configs)
     {
         $processor = new Processor();
 
-        return $processor->processConfiguration($configuration, $configs);
+        return $this->processedConfigs[] = $processor->processConfiguration($configuration, $configs);
+    }
+
+    /**
+     * @internal
+     */
+    final public function getProcessedConfigs()
+    {
+        try {
+            return $this->processedConfigs;
+        } finally {
+            $this->processedConfigs = [];
+        }
     }
 
     /**
@@ -106,7 +121,7 @@ abstract class Extension implements ExtensionInterface, ConfigurationExtensionIn
      */
     protected function isConfigEnabled(ContainerBuilder $container, array $config)
     {
-        if (!array_key_exists('enabled', $config)) {
+        if (!\array_key_exists('enabled', $config)) {
             throw new InvalidArgumentException("The config array has no 'enabled' key.");
         }
 
diff --git a/civicrm/vendor/symfony/dependency-injection/Extension/ExtensionInterface.php b/civicrm/vendor/symfony/dependency-injection/Extension/ExtensionInterface.php
index 18de31272f32273d5a3fced54b8be2f398d56e9a..6a7a2cf0238191af61cabc88fed833d85fe64109 100644
--- a/civicrm/vendor/symfony/dependency-injection/Extension/ExtensionInterface.php
+++ b/civicrm/vendor/symfony/dependency-injection/Extension/ExtensionInterface.php
@@ -37,7 +37,7 @@ interface ExtensionInterface
     /**
      * Returns the base path for the XSD files.
      *
-     * @return string The XSD base path
+     * @return string|false
      */
     public function getXsdValidationBasePath();
 
diff --git a/civicrm/vendor/symfony/dependency-injection/IntrospectableContainerInterface.php b/civicrm/vendor/symfony/dependency-injection/IntrospectableContainerInterface.php
deleted file mode 100644
index 4aa0059992e5b5ce9e8f8f6967399ff96165899d..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/dependency-injection/IntrospectableContainerInterface.php
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\DependencyInjection;
-
-/**
- * IntrospectableContainerInterface defines additional introspection functionality
- * for containers, allowing logic to be implemented based on a Container's state.
- *
- * @author Evan Villemez <evillemez@gmail.com>
- *
- * @deprecated since version 2.8, to be merged with ContainerInterface in 3.0.
- */
-interface IntrospectableContainerInterface extends ContainerInterface
-{
-    /**
-     * Check for whether or not a service has been initialized.
-     *
-     * @param string $id
-     *
-     * @return bool true if the service has been initialized, false otherwise
-     */
-    public function initialized($id);
-}
diff --git a/civicrm/vendor/symfony/dependency-injection/LICENSE b/civicrm/vendor/symfony/dependency-injection/LICENSE
index 21d7fb9e2f29b50caca3a76f0647e94e2cc8ddc1..9e936ec0448b8549e5edf08e5ac5f01491a8bfc8 100644
--- a/civicrm/vendor/symfony/dependency-injection/LICENSE
+++ b/civicrm/vendor/symfony/dependency-injection/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2018 Fabien Potencier
+Copyright (c) 2004-2020 Fabien Potencier
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/civicrm/vendor/symfony/dependency-injection/LazyProxy/Instantiator/RealServiceInstantiator.php b/civicrm/vendor/symfony/dependency-injection/LazyProxy/Instantiator/RealServiceInstantiator.php
index 3b0b57ef0f6b9edab6eb039f45e9bd83cebc027d..532e768684f30ccbd4430508aeb966e3b8b1a84d 100644
--- a/civicrm/vendor/symfony/dependency-injection/LazyProxy/Instantiator/RealServiceInstantiator.php
+++ b/civicrm/vendor/symfony/dependency-injection/LazyProxy/Instantiator/RealServiceInstantiator.php
@@ -17,7 +17,7 @@ use Symfony\Component\DependencyInjection\Definition;
 /**
  * {@inheritdoc}
  *
- * Noop proxy instantiator - simply produces the real service instead of a proxy instance.
+ * Noop proxy instantiator - produces the real service instead of a proxy instance.
  *
  * @author Marco Pivetta <ocramius@gmail.com>
  */
diff --git a/civicrm/vendor/symfony/dependency-injection/LazyProxy/PhpDumper/DumperInterface.php b/civicrm/vendor/symfony/dependency-injection/LazyProxy/PhpDumper/DumperInterface.php
index 502c72abafc26ed3af03d7cc08714f396a6644c4..3946eafafde00c1ecf92225277254b3e6408813a 100644
--- a/civicrm/vendor/symfony/dependency-injection/LazyProxy/PhpDumper/DumperInterface.php
+++ b/civicrm/vendor/symfony/dependency-injection/LazyProxy/PhpDumper/DumperInterface.php
@@ -30,12 +30,11 @@ interface DumperInterface
     /**
      * Generates the code to be used to instantiate a proxy in the dumped factory code.
      *
-     * @param Definition $definition
-     * @param string     $id         Service identifier
+     * @param string $id Service identifier
      *
      * @return string
      */
-    public function getProxyFactoryCode(Definition $definition, $id);
+    public function getProxyFactoryCode(Definition $definition, $id/**, $factoryCode = null */);
 
     /**
      * Generates the code for the lazy proxy.
diff --git a/civicrm/vendor/symfony/dependency-injection/LazyProxy/PhpDumper/NullDumper.php b/civicrm/vendor/symfony/dependency-injection/LazyProxy/PhpDumper/NullDumper.php
index 30911d3a5e83af4a4e3bc09ad3753e668f912567..67f9fae94dbf8ca659d6f46995082a2e68400759 100644
--- a/civicrm/vendor/symfony/dependency-injection/LazyProxy/PhpDumper/NullDumper.php
+++ b/civicrm/vendor/symfony/dependency-injection/LazyProxy/PhpDumper/NullDumper.php
@@ -17,6 +17,8 @@ use Symfony\Component\DependencyInjection\Definition;
  * Null dumper, negates any proxy code generation for any given service definition.
  *
  * @author Marco Pivetta <ocramius@gmail.com>
+ *
+ * @final since version 3.3
  */
 class NullDumper implements DumperInterface
 {
@@ -31,7 +33,7 @@ class NullDumper implements DumperInterface
     /**
      * {@inheritdoc}
      */
-    public function getProxyFactoryCode(Definition $definition, $id)
+    public function getProxyFactoryCode(Definition $definition, $id, $factoryCode = null)
     {
         return '';
     }
diff --git a/civicrm/vendor/symfony/dependency-injection/LazyProxy/ProxyHelper.php b/civicrm/vendor/symfony/dependency-injection/LazyProxy/ProxyHelper.php
new file mode 100644
index 0000000000000000000000000000000000000000..cb19c729c100c7e865d00dde19f8ded462150ac4
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/LazyProxy/ProxyHelper.php
@@ -0,0 +1,64 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\LazyProxy;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ *
+ * @internal
+ */
+class ProxyHelper
+{
+    /**
+     * @return string|null The FQCN or builtin name of the type hint, or null when the type hint references an invalid self|parent context
+     */
+    public static function getTypeHint(\ReflectionFunctionAbstract $r, \ReflectionParameter $p = null, $noBuiltin = false)
+    {
+        if ($p instanceof \ReflectionParameter) {
+            if (method_exists($p, 'getType')) {
+                $type = $p->getType();
+            } elseif (preg_match('/^(?:[^ ]++ ){4}([a-zA-Z_\x7F-\xFF][^ ]++)/', $p, $type)) {
+                $name = $type = $type[1];
+
+                if ('callable' === $name || 'array' === $name) {
+                    return $noBuiltin ? null : $name;
+                }
+            }
+        } else {
+            $type = method_exists($r, 'getReturnType') ? $r->getReturnType() : null;
+        }
+        if (!$type) {
+            return null;
+        }
+        if (!\is_string($type)) {
+            $name = $type instanceof \ReflectionNamedType ? $type->getName() : $type->__toString();
+
+            if ($type->isBuiltin()) {
+                return $noBuiltin ? null : $name;
+            }
+        }
+        $lcName = strtolower($name);
+        $prefix = $noBuiltin ? '' : '\\';
+
+        if ('self' !== $lcName && 'parent' !== $lcName) {
+            return $prefix.$name;
+        }
+        if (!$r instanceof \ReflectionMethod) {
+            return null;
+        }
+        if ('self' === $lcName) {
+            return $prefix.$r->getDeclaringClass()->name;
+        }
+
+        return ($parent = $r->getDeclaringClass()->getParentClass()) ? $prefix.$parent->name : null;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/AbstractConfigurator.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/AbstractConfigurator.php
new file mode 100644
index 0000000000000000000000000000000000000000..428683ef4cec83ebebd636f4cdec6142696e709a
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/AbstractConfigurator.php
@@ -0,0 +1,87 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
+use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+use Symfony\Component\DependencyInjection\Parameter;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\ExpressionLanguage\Expression;
+
+abstract class AbstractConfigurator
+{
+    const FACTORY = 'unknown';
+
+    /** @internal */
+    protected $definition;
+
+    public function __call($method, $args)
+    {
+        if (method_exists($this, 'set'.$method)) {
+            return \call_user_func_array([$this, 'set'.$method], $args);
+        }
+
+        throw new \BadMethodCallException(sprintf('Call to undefined method "%s::%s()".', static::class, $method));
+    }
+
+    /**
+     * Checks that a value is valid, optionally replacing Definition and Reference configurators by their configure value.
+     *
+     * @param mixed $value
+     * @param bool  $allowServices whether Definition and Reference are allowed; by default, only scalars and arrays are
+     *
+     * @return mixed the value, optionally cast to a Definition/Reference
+     */
+    public static function processValue($value, $allowServices = false)
+    {
+        if (\is_array($value)) {
+            foreach ($value as $k => $v) {
+                $value[$k] = static::processValue($v, $allowServices);
+            }
+
+            return $value;
+        }
+
+        if ($value instanceof ReferenceConfigurator) {
+            return new Reference($value->id, $value->invalidBehavior);
+        }
+
+        if ($value instanceof InlineServiceConfigurator) {
+            $def = $value->definition;
+            $value->definition = null;
+
+            return $def;
+        }
+
+        if ($value instanceof self) {
+            throw new InvalidArgumentException(sprintf('"%s()" can be used only at the root of service configuration files.', $value::FACTORY));
+        }
+
+        switch (true) {
+            case null === $value:
+            case is_scalar($value):
+                return $value;
+
+            case $value instanceof ArgumentInterface:
+            case $value instanceof Definition:
+            case $value instanceof Expression:
+            case $value instanceof Parameter:
+            case $value instanceof Reference:
+                if ($allowServices) {
+                    return $value;
+                }
+        }
+
+        throw new InvalidArgumentException(sprintf('Cannot use values of type "%s" in service configuration files.', \is_object($value) ? \get_class($value) : \gettype($value)));
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/AbstractServiceConfigurator.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/AbstractServiceConfigurator.php
new file mode 100644
index 0000000000000000000000000000000000000000..0a565787fdf10e01986dd60c3e633f2d3d466874
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/AbstractServiceConfigurator.php
@@ -0,0 +1,117 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
+
+abstract class AbstractServiceConfigurator extends AbstractConfigurator
+{
+    protected $parent;
+    protected $id;
+    private $defaultTags = [];
+
+    public function __construct(ServicesConfigurator $parent, Definition $definition, $id = null, array $defaultTags = [])
+    {
+        $this->parent = $parent;
+        $this->definition = $definition;
+        $this->id = $id;
+        $this->defaultTags = $defaultTags;
+    }
+
+    public function __destruct()
+    {
+        // default tags should be added last
+        foreach ($this->defaultTags as $name => $attributes) {
+            foreach ($attributes as $attributes) {
+                $this->definition->addTag($name, $attributes);
+            }
+        }
+        $this->defaultTags = [];
+    }
+
+    /**
+     * Registers a service.
+     *
+     * @param string      $id
+     * @param string|null $class
+     *
+     * @return ServiceConfigurator
+     */
+    final public function set($id, $class = null)
+    {
+        $this->__destruct();
+
+        return $this->parent->set($id, $class);
+    }
+
+    /**
+     * Creates an alias.
+     *
+     * @param string $id
+     * @param string $referencedId
+     *
+     * @return AliasConfigurator
+     */
+    final public function alias($id, $referencedId)
+    {
+        $this->__destruct();
+
+        return $this->parent->alias($id, $referencedId);
+    }
+
+    /**
+     * Registers a PSR-4 namespace using a glob pattern.
+     *
+     * @param string $namespace
+     * @param string $resource
+     *
+     * @return PrototypeConfigurator
+     */
+    final public function load($namespace, $resource)
+    {
+        $this->__destruct();
+
+        return $this->parent->load($namespace, $resource);
+    }
+
+    /**
+     * Gets an already defined service definition.
+     *
+     * @param string $id
+     *
+     * @return ServiceConfigurator
+     *
+     * @throws ServiceNotFoundException if the service definition does not exist
+     */
+    final public function get($id)
+    {
+        $this->__destruct();
+
+        return $this->parent->get($id);
+    }
+
+    /**
+     * Registers a service.
+     *
+     * @param string      $id
+     * @param string|null $class
+     *
+     * @return ServiceConfigurator
+     */
+    final public function __invoke($id, $class = null)
+    {
+        $this->__destruct();
+
+        return $this->parent->set($id, $class);
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/AliasConfigurator.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/AliasConfigurator.php
new file mode 100644
index 0000000000000000000000000000000000000000..cb00f58c049d45e98dc2fb26c9207db02ca2538e
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/AliasConfigurator.php
@@ -0,0 +1,30 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
+use Symfony\Component\DependencyInjection\Alias;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class AliasConfigurator extends AbstractServiceConfigurator
+{
+    const FACTORY = 'alias';
+
+    use Traits\PublicTrait;
+
+    public function __construct(ServicesConfigurator $parent, Alias $alias)
+    {
+        $this->parent = $parent;
+        $this->definition = $alias;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php
new file mode 100644
index 0000000000000000000000000000000000000000..f2e5ccf8b03b5f157bd4eee09d4053707e0b9545
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php
@@ -0,0 +1,135 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
+use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
+use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
+use Symfony\Component\ExpressionLanguage\Expression;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class ContainerConfigurator extends AbstractConfigurator
+{
+    const FACTORY = 'container';
+
+    private $container;
+    private $loader;
+    private $instanceof;
+    private $path;
+    private $file;
+
+    public function __construct(ContainerBuilder $container, PhpFileLoader $loader, array &$instanceof, $path, $file)
+    {
+        $this->container = $container;
+        $this->loader = $loader;
+        $this->instanceof = &$instanceof;
+        $this->path = $path;
+        $this->file = $file;
+    }
+
+    final public function extension($namespace, array $config)
+    {
+        if (!$this->container->hasExtension($namespace)) {
+            $extensions = array_filter(array_map(function ($ext) { return $ext->getAlias(); }, $this->container->getExtensions()));
+            throw new InvalidArgumentException(sprintf('There is no extension able to load the configuration for "%s" (in "%s"). Looked for namespace "%s", found "%s".', $namespace, $this->file, $namespace, $extensions ? implode('", "', $extensions) : 'none'));
+        }
+
+        $this->container->loadFromExtension($namespace, static::processValue($config));
+    }
+
+    final public function import($resource, $type = null, $ignoreErrors = false)
+    {
+        $this->loader->setCurrentDir(\dirname($this->path));
+        $this->loader->import($resource, $type, $ignoreErrors, $this->file);
+    }
+
+    /**
+     * @return ParametersConfigurator
+     */
+    final public function parameters()
+    {
+        return new ParametersConfigurator($this->container);
+    }
+
+    /**
+     * @return ServicesConfigurator
+     */
+    final public function services()
+    {
+        return new ServicesConfigurator($this->container, $this->loader, $this->instanceof);
+    }
+}
+
+/**
+ * Creates a service reference.
+ *
+ * @param string $id
+ *
+ * @return ReferenceConfigurator
+ */
+function ref($id)
+{
+    return new ReferenceConfigurator($id);
+}
+
+/**
+ * Creates an inline service.
+ *
+ * @param string|null $class
+ *
+ * @return InlineServiceConfigurator
+ */
+function inline($class = null)
+{
+    return new InlineServiceConfigurator(new Definition($class));
+}
+
+/**
+ * Creates a lazy iterator.
+ *
+ * @param ReferenceConfigurator[] $values
+ *
+ * @return IteratorArgument
+ */
+function iterator(array $values)
+{
+    return new IteratorArgument(AbstractConfigurator::processValue($values, true));
+}
+
+/**
+ * Creates a lazy iterator by tag name.
+ *
+ * @param string $tag
+ *
+ * @return TaggedIteratorArgument
+ */
+function tagged($tag)
+{
+    return new TaggedIteratorArgument($tag);
+}
+
+/**
+ * Creates an expression.
+ *
+ * @param string $expression an expression
+ *
+ * @return Expression
+ */
+function expr($expression)
+{
+    return new Expression($expression);
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/DefaultsConfigurator.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/DefaultsConfigurator.php
new file mode 100644
index 0000000000000000000000000000000000000000..662ba95d1ba6d36a9559efb454ba39971678ed9a
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/DefaultsConfigurator.php
@@ -0,0 +1,68 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ *
+ * @method InstanceofConfigurator instanceof(string $fqcn)
+ */
+class DefaultsConfigurator extends AbstractServiceConfigurator
+{
+    const FACTORY = 'defaults';
+
+    use Traits\AutoconfigureTrait;
+    use Traits\AutowireTrait;
+    use Traits\BindTrait;
+    use Traits\PublicTrait;
+
+    /**
+     * Adds a tag for this definition.
+     *
+     * @param string $name       The tag name
+     * @param array  $attributes An array of attributes
+     *
+     * @return $this
+     *
+     * @throws InvalidArgumentException when an invalid tag name or attribute is provided
+     */
+    final public function tag($name, array $attributes = [])
+    {
+        if (!\is_string($name) || '' === $name) {
+            throw new InvalidArgumentException('The tag name in "_defaults" must be a non-empty string.');
+        }
+
+        foreach ($attributes as $attribute => $value) {
+            if (!is_scalar($value) && null !== $value) {
+                throw new InvalidArgumentException(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type.', $name, $attribute));
+            }
+        }
+
+        $this->definition->addTag($name, $attributes);
+
+        return $this;
+    }
+
+    /**
+     * Defines an instanceof-conditional to be applied to following service definitions.
+     *
+     * @param string $fqcn
+     *
+     * @return InstanceofConfigurator
+     */
+    final protected function setInstanceof($fqcn)
+    {
+        return $this->parent->instanceof($fqcn);
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/InlineServiceConfigurator.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/InlineServiceConfigurator.php
new file mode 100644
index 0000000000000000000000000000000000000000..362b374e5597072df302f7479fcc518949a2dc40
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/InlineServiceConfigurator.php
@@ -0,0 +1,36 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
+use Symfony\Component\DependencyInjection\Definition;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class InlineServiceConfigurator extends AbstractConfigurator
+{
+    const FACTORY = 'inline';
+
+    use Traits\ArgumentTrait;
+    use Traits\AutowireTrait;
+    use Traits\BindTrait;
+    use Traits\FactoryTrait;
+    use Traits\FileTrait;
+    use Traits\LazyTrait;
+    use Traits\ParentTrait;
+    use Traits\TagTrait;
+
+    public function __construct(Definition $definition)
+    {
+        $this->definition = $definition;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/InstanceofConfigurator.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/InstanceofConfigurator.php
new file mode 100644
index 0000000000000000000000000000000000000000..629874d19cd14b89996bdbff25fab1db14860e30
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/InstanceofConfigurator.php
@@ -0,0 +1,43 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ *
+ * @method InstanceofConfigurator instanceof(string $fqcn)
+ */
+class InstanceofConfigurator extends AbstractServiceConfigurator
+{
+    const FACTORY = 'instanceof';
+
+    use Traits\AutowireTrait;
+    use Traits\CallTrait;
+    use Traits\ConfiguratorTrait;
+    use Traits\LazyTrait;
+    use Traits\PropertyTrait;
+    use Traits\PublicTrait;
+    use Traits\ShareTrait;
+    use Traits\TagTrait;
+
+    /**
+     * Defines an instanceof-conditional to be applied to following service definitions.
+     *
+     * @param string $fqcn
+     *
+     * @return self
+     */
+    final protected function setInstanceof($fqcn)
+    {
+        return $this->parent->instanceof($fqcn);
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/ParametersConfigurator.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/ParametersConfigurator.php
new file mode 100644
index 0000000000000000000000000000000000000000..9585b1a4b5c34e6eed6f5df11130321ffe0de743
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/ParametersConfigurator.php
@@ -0,0 +1,57 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class ParametersConfigurator extends AbstractConfigurator
+{
+    const FACTORY = 'parameters';
+
+    private $container;
+
+    public function __construct(ContainerBuilder $container)
+    {
+        $this->container = $container;
+    }
+
+    /**
+     * Creates a parameter.
+     *
+     * @param string $name
+     * @param mixed  $value
+     *
+     * @return $this
+     */
+    final public function set($name, $value)
+    {
+        $this->container->setParameter($name, static::processValue($value, true));
+
+        return $this;
+    }
+
+    /**
+     * Creates a parameter.
+     *
+     * @param string $name
+     * @param mixed  $value
+     *
+     * @return $this
+     */
+    final public function __invoke($name, $value)
+    {
+        return $this->set($name, $value);
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/PrototypeConfigurator.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/PrototypeConfigurator.php
new file mode 100644
index 0000000000000000000000000000000000000000..3d844798d431a9bd6f5b9498b294e77a31daed9b
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/PrototypeConfigurator.php
@@ -0,0 +1,87 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class PrototypeConfigurator extends AbstractServiceConfigurator
+{
+    const FACTORY = 'load';
+
+    use Traits\AbstractTrait;
+    use Traits\ArgumentTrait;
+    use Traits\AutoconfigureTrait;
+    use Traits\AutowireTrait;
+    use Traits\BindTrait;
+    use Traits\CallTrait;
+    use Traits\ConfiguratorTrait;
+    use Traits\DeprecateTrait;
+    use Traits\FactoryTrait;
+    use Traits\LazyTrait;
+    use Traits\ParentTrait;
+    use Traits\PropertyTrait;
+    use Traits\PublicTrait;
+    use Traits\ShareTrait;
+    use Traits\TagTrait;
+
+    private $loader;
+    private $resource;
+    private $exclude;
+    private $allowParent;
+
+    public function __construct(ServicesConfigurator $parent, PhpFileLoader $loader, Definition $defaults, $namespace, $resource, $allowParent)
+    {
+        $definition = new Definition();
+        if (!$defaults->isPublic() || !$defaults->isPrivate()) {
+            $definition->setPublic($defaults->isPublic());
+        }
+        $definition->setAutowired($defaults->isAutowired());
+        $definition->setAutoconfigured($defaults->isAutoconfigured());
+        // deep clone, to avoid multiple process of the same instance in the passes
+        $definition->setBindings(unserialize(serialize($defaults->getBindings())));
+        $definition->setChanges([]);
+
+        $this->loader = $loader;
+        $this->resource = $resource;
+        $this->allowParent = $allowParent;
+
+        parent::__construct($parent, $definition, $namespace, $defaults->getTags());
+    }
+
+    public function __destruct()
+    {
+        parent::__destruct();
+
+        if ($this->loader) {
+            $this->loader->registerClasses($this->definition, $this->id, $this->resource, $this->exclude);
+        }
+        $this->loader = null;
+    }
+
+    /**
+     * Excludes files from registration using a glob pattern.
+     *
+     * @param string $exclude
+     *
+     * @return $this
+     */
+    final public function exclude($exclude)
+    {
+        $this->exclude = $exclude;
+
+        return $this;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/ReferenceConfigurator.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/ReferenceConfigurator.php
new file mode 100644
index 0000000000000000000000000000000000000000..1585c0872a69407014b7f34920e42262ab8770d5
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/ReferenceConfigurator.php
@@ -0,0 +1,66 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class ReferenceConfigurator extends AbstractConfigurator
+{
+    /** @internal */
+    protected $id;
+
+    /** @internal */
+    protected $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
+
+    public function __construct($id)
+    {
+        $this->id = $id;
+    }
+
+    /**
+     * @return $this
+     */
+    final public function ignoreOnInvalid()
+    {
+        $this->invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
+
+        return $this;
+    }
+
+    /**
+     * @return $this
+     */
+    final public function nullOnInvalid()
+    {
+        $this->invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE;
+
+        return $this;
+    }
+
+    /**
+     * @return $this
+     */
+    final public function ignoreOnUninitialized()
+    {
+        $this->invalidBehavior = ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE;
+
+        return $this;
+    }
+
+    public function __toString()
+    {
+        return $this->id;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/ServiceConfigurator.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/ServiceConfigurator.php
new file mode 100644
index 0000000000000000000000000000000000000000..897dedaac58e107645a901d17eb82137d3c5cb11
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/ServiceConfigurator.php
@@ -0,0 +1,70 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
+use Symfony\Component\DependencyInjection\ChildDefinition;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class ServiceConfigurator extends AbstractServiceConfigurator
+{
+    const FACTORY = 'services';
+
+    use Traits\AbstractTrait;
+    use Traits\ArgumentTrait;
+    use Traits\AutoconfigureTrait;
+    use Traits\AutowireTrait;
+    use Traits\BindTrait;
+    use Traits\CallTrait;
+    use Traits\ClassTrait;
+    use Traits\ConfiguratorTrait;
+    use Traits\DecorateTrait;
+    use Traits\DeprecateTrait;
+    use Traits\FactoryTrait;
+    use Traits\FileTrait;
+    use Traits\LazyTrait;
+    use Traits\ParentTrait;
+    use Traits\PropertyTrait;
+    use Traits\PublicTrait;
+    use Traits\ShareTrait;
+    use Traits\SyntheticTrait;
+    use Traits\TagTrait;
+
+    private $container;
+    private $instanceof;
+    private $allowParent;
+
+    public function __construct(ContainerBuilder $container, array $instanceof, $allowParent, ServicesConfigurator $parent, Definition $definition, $id, array $defaultTags)
+    {
+        $this->container = $container;
+        $this->instanceof = $instanceof;
+        $this->allowParent = $allowParent;
+
+        parent::__construct($parent, $definition, $id, $defaultTags);
+    }
+
+    public function __destruct()
+    {
+        parent::__destruct();
+
+        $this->container->removeBindings($this->id);
+
+        if (!$this->definition instanceof ChildDefinition) {
+            $this->container->setDefinition($this->id, $this->definition->setInstanceofConditionals($this->instanceof));
+        } else {
+            $this->container->setDefinition($this->id, $this->definition);
+        }
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/ServicesConfigurator.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/ServicesConfigurator.php
new file mode 100644
index 0000000000000000000000000000000000000000..b6ccbc63b4e81223dce7dd36c2431a8202bc0e46
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/ServicesConfigurator.php
@@ -0,0 +1,160 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
+use Symfony\Component\DependencyInjection\Alias;
+use Symfony\Component\DependencyInjection\ChildDefinition;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
+use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ *
+ * @method InstanceofConfigurator instanceof($fqcn)
+ */
+class ServicesConfigurator extends AbstractConfigurator
+{
+    const FACTORY = 'services';
+
+    private $defaults;
+    private $container;
+    private $loader;
+    private $instanceof;
+
+    public function __construct(ContainerBuilder $container, PhpFileLoader $loader, array &$instanceof)
+    {
+        $this->defaults = new Definition();
+        $this->container = $container;
+        $this->loader = $loader;
+        $this->instanceof = &$instanceof;
+        $instanceof = [];
+    }
+
+    /**
+     * Defines a set of defaults for following service definitions.
+     *
+     * @return DefaultsConfigurator
+     */
+    final public function defaults()
+    {
+        return new DefaultsConfigurator($this, $this->defaults = new Definition());
+    }
+
+    /**
+     * Defines an instanceof-conditional to be applied to following service definitions.
+     *
+     * @param string $fqcn
+     *
+     * @return InstanceofConfigurator
+     */
+    final protected function setInstanceof($fqcn)
+    {
+        $this->instanceof[$fqcn] = $definition = new ChildDefinition('');
+
+        return new InstanceofConfigurator($this, $definition, $fqcn);
+    }
+
+    /**
+     * Registers a service.
+     *
+     * @param string      $id
+     * @param string|null $class
+     *
+     * @return ServiceConfigurator
+     */
+    final public function set($id, $class = null)
+    {
+        $defaults = $this->defaults;
+        $allowParent = !$defaults->getChanges() && empty($this->instanceof);
+
+        $definition = new Definition();
+        if (!$defaults->isPublic() || !$defaults->isPrivate()) {
+            $definition->setPublic($defaults->isPublic() && !$defaults->isPrivate());
+        }
+        $definition->setAutowired($defaults->isAutowired());
+        $definition->setAutoconfigured($defaults->isAutoconfigured());
+        // deep clone, to avoid multiple process of the same instance in the passes
+        $definition->setBindings(unserialize(serialize($defaults->getBindings())));
+        $definition->setChanges([]);
+
+        $configurator = new ServiceConfigurator($this->container, $this->instanceof, $allowParent, $this, $definition, $id, $defaults->getTags());
+
+        return null !== $class ? $configurator->class($class) : $configurator;
+    }
+
+    /**
+     * Creates an alias.
+     *
+     * @param string $id
+     * @param string $referencedId
+     *
+     * @return AliasConfigurator
+     */
+    final public function alias($id, $referencedId)
+    {
+        $ref = static::processValue($referencedId, true);
+        $alias = new Alias((string) $ref);
+        if (!$this->defaults->isPublic() || !$this->defaults->isPrivate()) {
+            $alias->setPublic($this->defaults->isPublic());
+        }
+        $this->container->setAlias($id, $alias);
+
+        return new AliasConfigurator($this, $alias);
+    }
+
+    /**
+     * Registers a PSR-4 namespace using a glob pattern.
+     *
+     * @param string $namespace
+     * @param string $resource
+     *
+     * @return PrototypeConfigurator
+     */
+    final public function load($namespace, $resource)
+    {
+        $allowParent = !$this->defaults->getChanges() && empty($this->instanceof);
+
+        return new PrototypeConfigurator($this, $this->loader, $this->defaults, $namespace, $resource, $allowParent);
+    }
+
+    /**
+     * Gets an already defined service definition.
+     *
+     * @param string $id
+     *
+     * @return ServiceConfigurator
+     *
+     * @throws ServiceNotFoundException if the service definition does not exist
+     */
+    final public function get($id)
+    {
+        $allowParent = !$this->defaults->getChanges() && empty($this->instanceof);
+        $definition = $this->container->getDefinition($id);
+
+        return new ServiceConfigurator($this->container, $definition->getInstanceofConditionals(), $allowParent, $this, $definition, $id, []);
+    }
+
+    /**
+     * Registers a service.
+     *
+     * @param string      $id
+     * @param string|null $class
+     *
+     * @return ServiceConfigurator
+     */
+    final public function __invoke($id, $class = null)
+    {
+        return $this->set($id, $class);
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AbstractTrait.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AbstractTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..f69a7a5be109da2383f238b0af49ff1c5eccc084
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AbstractTrait.php
@@ -0,0 +1,33 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits;
+
+/**
+ * @method $this abstract(bool $abstract = true)
+ */
+trait AbstractTrait
+{
+    /**
+     * Whether this definition is abstract, that means it merely serves as a
+     * template for other definitions.
+     *
+     * @param bool $abstract
+     *
+     * @return $this
+     */
+    final protected function setAbstract($abstract = true)
+    {
+        $this->definition->setAbstract($abstract);
+
+        return $this;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ArgumentTrait.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ArgumentTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..7ec8c51d478e87973de9aca17c7ebc408f5c1939
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ArgumentTrait.php
@@ -0,0 +1,44 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits;
+
+trait ArgumentTrait
+{
+    /**
+     * Sets the arguments to pass to the service constructor/factory method.
+     *
+     * @param array $arguments An array of arguments
+     *
+     * @return $this
+     */
+    final public function args(array $arguments)
+    {
+        $this->definition->setArguments(static::processValue($arguments, true));
+
+        return $this;
+    }
+
+    /**
+     * Sets one argument to pass to the service constructor/factory method.
+     *
+     * @param string|int $key
+     * @param mixed      $value
+     *
+     * @return $this
+     */
+    final public function arg($key, $value)
+    {
+        $this->definition->setArgument($key, static::processValue($value, true));
+
+        return $this;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AutoconfigureTrait.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AutoconfigureTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..42a692353e9dc5dfd4bdcc6d323983be00129758
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AutoconfigureTrait.php
@@ -0,0 +1,37 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits;
+
+use Symfony\Component\DependencyInjection\ChildDefinition;
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+
+trait AutoconfigureTrait
+{
+    /**
+     * Sets whether or not instanceof conditionals should be prepended with a global set.
+     *
+     * @param bool $autoconfigured
+     *
+     * @return $this
+     *
+     * @throws InvalidArgumentException when a parent is already set
+     */
+    final public function autoconfigure($autoconfigured = true)
+    {
+        if ($autoconfigured && $this->definition instanceof ChildDefinition) {
+            throw new InvalidArgumentException(sprintf('The service "%s" cannot have a "parent" and also have "autoconfigure". Try disabling autoconfiguration for the service.', $this->id));
+        }
+        $this->definition->setAutoconfigured($autoconfigured);
+
+        return $this;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AutowireTrait.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AutowireTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..3d4b2e854b4c7eb7afb9ec01649bfd3825863bc8
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AutowireTrait.php
@@ -0,0 +1,29 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits;
+
+trait AutowireTrait
+{
+    /**
+     * Enables/disables autowiring.
+     *
+     * @param bool $autowired
+     *
+     * @return $this
+     */
+    final public function autowire($autowired = true)
+    {
+        $this->definition->setAutowired($autowired);
+
+        return $this;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/BindTrait.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/BindTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..4511ed659d4e51ff50dd7f206110fae640c18f90
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/BindTrait.php
@@ -0,0 +1,43 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits;
+
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+use Symfony\Component\DependencyInjection\Reference;
+
+trait BindTrait
+{
+    /**
+     * Sets bindings.
+     *
+     * Bindings map $named or FQCN arguments to values that should be
+     * injected in the matching parameters (of the constructor, of methods
+     * called and of controller actions).
+     *
+     * @param string $nameOrFqcn A parameter name with its "$" prefix, or a FQCN
+     * @param mixed  $valueOrRef The value or reference to bind
+     *
+     * @return $this
+     */
+    final public function bind($nameOrFqcn, $valueOrRef)
+    {
+        $valueOrRef = static::processValue($valueOrRef, true);
+        if (isset($nameOrFqcn[0]) && '$' !== $nameOrFqcn[0] && !$valueOrRef instanceof Reference) {
+            throw new InvalidArgumentException(sprintf('Invalid binding for service "%s": named arguments must start with a "$", and FQCN must map to references. Neither applies to binding "%s".', $this->id, $nameOrFqcn));
+        }
+        $bindings = $this->definition->getBindings();
+        $bindings[$nameOrFqcn] = $valueOrRef;
+        $this->definition->setBindings($bindings);
+
+        return $this;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/CallTrait.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/CallTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..8e6b17a19d289325a70ac92f02bb39828ae3382a
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/CallTrait.php
@@ -0,0 +1,34 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits;
+
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+
+trait CallTrait
+{
+    /**
+     * Adds a method to call after service initialization.
+     *
+     * @param string $method    The method name to call
+     * @param array  $arguments An array of arguments to pass to the method call
+     *
+     * @return $this
+     *
+     * @throws InvalidArgumentException on empty $method param
+     */
+    final public function call($method, array $arguments = [])
+    {
+        $this->definition->addMethodCall($method, static::processValue($arguments, true));
+
+        return $this;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ClassTrait.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ClassTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..ae5b1c0a3d0c582302553c399708c86ea396bbaa
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ClassTrait.php
@@ -0,0 +1,32 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits;
+
+/**
+ * @method $this class(string $class)
+ */
+trait ClassTrait
+{
+    /**
+     * Sets the service class.
+     *
+     * @param string $class The service class
+     *
+     * @return $this
+     */
+    final protected function setClass($class)
+    {
+        $this->definition->setClass($class);
+
+        return $this;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ConfiguratorTrait.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ConfiguratorTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..a38283b0e739b29770dc4e989ff5c9b17aa6bfe4
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ConfiguratorTrait.php
@@ -0,0 +1,29 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits;
+
+trait ConfiguratorTrait
+{
+    /**
+     * Sets a configurator to call after the service is fully initialized.
+     *
+     * @param string|array $configurator A PHP callable reference
+     *
+     * @return $this
+     */
+    final public function configurator($configurator)
+    {
+        $this->definition->setConfigurator(static::processValue($configurator, true));
+
+        return $this;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/DecorateTrait.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/DecorateTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..173ad15f0695113fbc0449913a9b4a5b613d6080
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/DecorateTrait.php
@@ -0,0 +1,35 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits;
+
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+
+trait DecorateTrait
+{
+    /**
+     * Sets the service that this service is decorating.
+     *
+     * @param string|null $id        The decorated service id, use null to remove decoration
+     * @param string|null $renamedId The new decorated service id
+     * @param int         $priority  The priority of decoration
+     *
+     * @return $this
+     *
+     * @throws InvalidArgumentException in case the decorated service id and the new decorated service id are equals
+     */
+    final public function decorate($id, $renamedId = null, $priority = 0)
+    {
+        $this->definition->setDecoratedService($id, $renamedId, $priority);
+
+        return $this;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/DeprecateTrait.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/DeprecateTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..b14a6557eee96fd5b2ad772063de5181a2e89a54
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/DeprecateTrait.php
@@ -0,0 +1,33 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits;
+
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+
+trait DeprecateTrait
+{
+    /**
+     * Whether this definition is deprecated, that means it should not be called anymore.
+     *
+     * @param string $template Template message to use if the definition is deprecated
+     *
+     * @return $this
+     *
+     * @throws InvalidArgumentException when the message template is invalid
+     */
+    final public function deprecate($template = null)
+    {
+        $this->definition->setDeprecated(true, $template);
+
+        return $this;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/FactoryTrait.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/FactoryTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..0d50fb747f77e643b1bafc986851234c03246442
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/FactoryTrait.php
@@ -0,0 +1,37 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits;
+
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+
+trait FactoryTrait
+{
+    /**
+     * Sets a factory.
+     *
+     * @param string|array $factory A PHP callable reference
+     *
+     * @return $this
+     */
+    final public function factory($factory)
+    {
+        if (\is_string($factory) && 1 === substr_count($factory, ':')) {
+            $factoryParts = explode(':', $factory);
+
+            throw new InvalidArgumentException(sprintf('Invalid factory "%s": the `service:method` notation is not available when using PHP-based DI configuration. Use "[ref(\'%s\'), \'%s\']" instead.', $factory, $factoryParts[0], $factoryParts[1]));
+        }
+
+        $this->definition->setFactory(static::processValue($factory, true));
+
+        return $this;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/FileTrait.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/FileTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..895f5304c3e74baad667ad4c8041b39cd8e36ee7
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/FileTrait.php
@@ -0,0 +1,29 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits;
+
+trait FileTrait
+{
+    /**
+     * Sets a file to require before creating the service.
+     *
+     * @param string $file A full pathname to include
+     *
+     * @return $this
+     */
+    final public function file($file)
+    {
+        $this->definition->setFile($file);
+
+        return $this;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/LazyTrait.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/LazyTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..d7ea8b27f5ea38b30b1cf88973d038afa6e2f756
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/LazyTrait.php
@@ -0,0 +1,29 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits;
+
+trait LazyTrait
+{
+    /**
+     * Sets the lazy flag of this service.
+     *
+     * @param bool $lazy
+     *
+     * @return $this
+     */
+    final public function lazy($lazy = true)
+    {
+        $this->definition->setLazy($lazy);
+
+        return $this;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ParentTrait.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ParentTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..43f1223e324a646300f2bc3b6fd84b244ccca577
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ParentTrait.php
@@ -0,0 +1,55 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits;
+
+use Symfony\Component\DependencyInjection\ChildDefinition;
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+
+/**
+ * @method $this parent(string $parent)
+ */
+trait ParentTrait
+{
+    /**
+     * Sets the Definition to inherit from.
+     *
+     * @param string $parent
+     *
+     * @return $this
+     *
+     * @throws InvalidArgumentException when parent cannot be set
+     */
+    final protected function setParent($parent)
+    {
+        if (!$this->allowParent) {
+            throw new InvalidArgumentException(sprintf('A parent cannot be defined when either "_instanceof" or "_defaults" are also defined for service prototype "%s".', $this->id));
+        }
+
+        if ($this->definition instanceof ChildDefinition) {
+            $this->definition->setParent($parent);
+        } elseif ($this->definition->isAutoconfigured()) {
+            throw new InvalidArgumentException(sprintf('The service "%s" cannot have a "parent" and also have "autoconfigure". Try disabling autoconfiguration for the service.', $this->id));
+        } elseif ($this->definition->getBindings()) {
+            throw new InvalidArgumentException(sprintf('The service "%s" cannot have a "parent" and also "bind" arguments.', $this->id));
+        } else {
+            // cast Definition to ChildDefinition
+            $definition = serialize($this->definition);
+            $definition = substr_replace($definition, '53', 2, 2);
+            $definition = substr_replace($definition, 'Child', 44, 0);
+            $definition = unserialize($definition);
+
+            $this->definition = $definition->setParent($parent);
+        }
+
+        return $this;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/PropertyTrait.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/PropertyTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..d5d938708e0a85a696c2fdbe9a2b5d828dcb5a76
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/PropertyTrait.php
@@ -0,0 +1,30 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits;
+
+trait PropertyTrait
+{
+    /**
+     * Sets a specific property.
+     *
+     * @param string $name
+     * @param mixed  $value
+     *
+     * @return $this
+     */
+    final public function property($name, $value)
+    {
+        $this->definition->setProperty($name, static::processValue($value, true));
+
+        return $this;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/PublicTrait.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/PublicTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..8f7f79f1cc21846664fd19f85f614055606feb71
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/PublicTrait.php
@@ -0,0 +1,39 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits;
+
+/**
+ * @method $this public()
+ * @method $this private()
+ */
+trait PublicTrait
+{
+    /**
+     * @return $this
+     */
+    final protected function setPublic()
+    {
+        $this->definition->setPublic(true);
+
+        return $this;
+    }
+
+    /**
+     * @return $this
+     */
+    final protected function setPrivate()
+    {
+        $this->definition->setPublic(false);
+
+        return $this;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ShareTrait.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ShareTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..1c2f97b59761cacce1c9616cc4c41c95b21a5ccd
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ShareTrait.php
@@ -0,0 +1,29 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits;
+
+trait ShareTrait
+{
+    /**
+     * Sets if the service must be shared or not.
+     *
+     * @param bool $shared Whether the service must be shared or not
+     *
+     * @return $this
+     */
+    final public function share($shared = true)
+    {
+        $this->definition->setShared($shared);
+
+        return $this;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/SyntheticTrait.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/SyntheticTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..81eceff43d86d004a8e3e44de1d5ab71ea5cd1fb
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/SyntheticTrait.php
@@ -0,0 +1,30 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits;
+
+trait SyntheticTrait
+{
+    /**
+     * Sets whether this definition is synthetic, that is not constructed by the
+     * container, but dynamically injected.
+     *
+     * @param bool $synthetic
+     *
+     * @return $this
+     */
+    final public function synthetic($synthetic = true)
+    {
+        $this->definition->setSynthetic($synthetic);
+
+        return $this;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/TagTrait.php b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/TagTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..d17339f88b692840884ea6c145707f19e0e8b637
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/Configurator/Traits/TagTrait.php
@@ -0,0 +1,42 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits;
+
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+
+trait TagTrait
+{
+    /**
+     * Adds a tag for this definition.
+     *
+     * @param string $name       The tag name
+     * @param array  $attributes An array of attributes
+     *
+     * @return $this
+     */
+    final public function tag($name, array $attributes = [])
+    {
+        if (!\is_string($name) || '' === $name) {
+            throw new InvalidArgumentException(sprintf('The tag name for service "%s" must be a non-empty string.', $this->id));
+        }
+
+        foreach ($attributes as $attribute => $value) {
+            if (!is_scalar($value) && null !== $value) {
+                throw new InvalidArgumentException(sprintf('A tag attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s".', $this->id, $name, $attribute));
+            }
+        }
+
+        $this->definition->addTag($name, $attributes);
+
+        return $this;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/DirectoryLoader.php b/civicrm/vendor/symfony/dependency-injection/Loader/DirectoryLoader.php
index 05a3ba2b5c3a9f88b6642b1be21c17baa6e72bd3..a57cac3b5ee9977f946d0cfc97f817cd3b5ae0c5 100644
--- a/civicrm/vendor/symfony/dependency-injection/Loader/DirectoryLoader.php
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/DirectoryLoader.php
@@ -11,8 +11,6 @@
 
 namespace Symfony\Component\DependencyInjection\Loader;
 
-use Symfony\Component\Config\Resource\DirectoryResource;
-
 /**
  * DirectoryLoader is a recursive loader to go through directories.
  *
@@ -27,7 +25,7 @@ class DirectoryLoader extends FileLoader
     {
         $file = rtrim($file, '/');
         $path = $this->locator->locate($file);
-        $this->container->addResource(new DirectoryResource($path));
+        $this->container->fileExists($path, false);
 
         foreach (scandir($path) as $dir) {
             if ('.' !== $dir[0]) {
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/FileLoader.php b/civicrm/vendor/symfony/dependency-injection/Loader/FileLoader.php
index 9fc297bab41e6daf58d970813f9347ca0203955f..749dd4d06b9f42a18d10fd1c32a7d26cbc12e2c9 100644
--- a/civicrm/vendor/symfony/dependency-injection/Loader/FileLoader.php
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/FileLoader.php
@@ -13,7 +13,11 @@ namespace Symfony\Component\DependencyInjection\Loader;
 
 use Symfony\Component\Config\FileLocatorInterface;
 use Symfony\Component\Config\Loader\FileLoader as BaseFileLoader;
+use Symfony\Component\Config\Resource\GlobResource;
+use Symfony\Component\DependencyInjection\ChildDefinition;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
 
 /**
  * FileLoader is the abstract class used by all built-in loaders that are file based.
@@ -23,6 +27,8 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
 abstract class FileLoader extends BaseFileLoader
 {
     protected $container;
+    protected $isLoadingInstanceof = false;
+    protected $instanceof = [];
 
     public function __construct(ContainerBuilder $container, FileLocatorInterface $locator)
     {
@@ -30,4 +36,141 @@ abstract class FileLoader extends BaseFileLoader
 
         parent::__construct($locator);
     }
+
+    /**
+     * Registers a set of classes as services using PSR-4 for discovery.
+     *
+     * @param Definition $prototype A definition to use as template
+     * @param string     $namespace The namespace prefix of classes in the scanned directory
+     * @param string     $resource  The directory to look for classes, glob-patterns allowed
+     * @param string     $exclude   A globed path of files to exclude
+     */
+    public function registerClasses(Definition $prototype, $namespace, $resource, $exclude = null)
+    {
+        if ('\\' !== substr($namespace, -1)) {
+            throw new InvalidArgumentException(sprintf('Namespace prefix must end with a "\\": "%s".', $namespace));
+        }
+        if (!preg_match('/^(?:[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+\\\\)++$/', $namespace)) {
+            throw new InvalidArgumentException(sprintf('Namespace is not a valid PSR-4 prefix: "%s".', $namespace));
+        }
+
+        $classes = $this->findClasses($namespace, $resource, $exclude);
+        // prepare for deep cloning
+        $serializedPrototype = serialize($prototype);
+        $interfaces = [];
+        $singlyImplemented = [];
+
+        foreach ($classes as $class => $errorMessage) {
+            if (interface_exists($class, false)) {
+                $interfaces[] = $class;
+            } else {
+                $this->setDefinition($class, $definition = unserialize($serializedPrototype));
+                if (null !== $errorMessage) {
+                    $definition->addError($errorMessage);
+
+                    continue;
+                }
+                foreach (class_implements($class, false) as $interface) {
+                    $singlyImplemented[$interface] = isset($singlyImplemented[$interface]) ? false : $class;
+                }
+            }
+        }
+        foreach ($interfaces as $interface) {
+            if (!empty($singlyImplemented[$interface])) {
+                $this->container->setAlias($interface, $singlyImplemented[$interface])
+                    ->setPublic(false);
+            }
+        }
+    }
+
+    /**
+     * Registers a definition in the container with its instanceof-conditionals.
+     *
+     * @param string $id
+     */
+    protected function setDefinition($id, Definition $definition)
+    {
+        $this->container->removeBindings($id);
+
+        if ($this->isLoadingInstanceof) {
+            if (!$definition instanceof ChildDefinition) {
+                throw new InvalidArgumentException(sprintf('Invalid type definition "%s": ChildDefinition expected, "%s" given.', $id, \get_class($definition)));
+            }
+            $this->instanceof[$id] = $definition;
+        } else {
+            $this->container->setDefinition($id, $definition instanceof ChildDefinition ? $definition : $definition->setInstanceofConditionals($this->instanceof));
+        }
+    }
+
+    private function findClasses($namespace, $pattern, $excludePattern)
+    {
+        $parameterBag = $this->container->getParameterBag();
+
+        $excludePaths = [];
+        $excludePrefix = null;
+        if ($excludePattern) {
+            $excludePattern = $parameterBag->unescapeValue($parameterBag->resolveValue($excludePattern));
+            foreach ($this->glob($excludePattern, true, $resource, true) as $path => $info) {
+                if (null === $excludePrefix) {
+                    $excludePrefix = $resource->getPrefix();
+                }
+
+                // normalize Windows slashes
+                $excludePaths[str_replace('\\', '/', $path)] = true;
+            }
+        }
+
+        $pattern = $parameterBag->unescapeValue($parameterBag->resolveValue($pattern));
+        $classes = [];
+        $extRegexp = \defined('HHVM_VERSION') ? '/\\.(?:php|hh)$/' : '/\\.php$/';
+        $prefixLen = null;
+        foreach ($this->glob($pattern, true, $resource) as $path => $info) {
+            if (null === $prefixLen) {
+                $prefixLen = \strlen($resource->getPrefix());
+
+                if ($excludePrefix && 0 !== strpos($excludePrefix, $resource->getPrefix())) {
+                    throw new InvalidArgumentException(sprintf('Invalid "exclude" pattern when importing classes for "%s": make sure your "exclude" pattern (%s) is a subset of the "resource" pattern (%s).', $namespace, $excludePattern, $pattern));
+                }
+            }
+
+            if (isset($excludePaths[str_replace('\\', '/', $path)])) {
+                continue;
+            }
+
+            if (!preg_match($extRegexp, $path, $m) || !$info->isReadable()) {
+                continue;
+            }
+            $class = $namespace.ltrim(str_replace('/', '\\', substr($path, $prefixLen, -\strlen($m[0]))), '\\');
+
+            if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+$/', $class)) {
+                continue;
+            }
+
+            try {
+                $r = $this->container->getReflectionClass($class);
+            } catch (\ReflectionException $e) {
+                $classes[$class] = $e->getMessage();
+                continue;
+            }
+            // check to make sure the expected class exists
+            if (!$r) {
+                throw new InvalidArgumentException(sprintf('Expected to find class "%s" in file "%s" while importing services from resource "%s", but it was not found! Check the namespace prefix used with the resource.', $class, $path, $pattern));
+            }
+
+            if ($r->isInstantiable() || $r->isInterface()) {
+                $classes[$class] = null;
+            }
+        }
+
+        // track only for new & removed files
+        if ($resource instanceof GlobResource) {
+            $this->container->addResource($resource);
+        } else {
+            foreach ($resource as $path) {
+                $this->container->fileExists($path, false);
+            }
+        }
+
+        return $classes;
+    }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/GlobFileLoader.php b/civicrm/vendor/symfony/dependency-injection/Loader/GlobFileLoader.php
new file mode 100644
index 0000000000000000000000000000000000000000..4b25610efe48e5d086ddca77888e994f01f13bfd
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/GlobFileLoader.php
@@ -0,0 +1,40 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader;
+
+/**
+ * GlobFileLoader loads files from a glob pattern.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class GlobFileLoader extends FileLoader
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function load($resource, $type = null)
+    {
+        foreach ($this->glob($resource, false, $globResource) as $path => $info) {
+            $this->import($path);
+        }
+
+        $this->container->addResource($globResource);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function supports($resource, $type = null)
+    {
+        return 'glob' === $type;
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/IniFileLoader.php b/civicrm/vendor/symfony/dependency-injection/Loader/IniFileLoader.php
index fa8cbbf2875fd41229928b63b62674090233d68f..307a3eefbbe562dfbfe7e89caf30d42026f9f922 100644
--- a/civicrm/vendor/symfony/dependency-injection/Loader/IniFileLoader.php
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/IniFileLoader.php
@@ -11,7 +11,7 @@
 
 namespace Symfony\Component\DependencyInjection\Loader;
 
-use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Config\Util\XmlUtils;
 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
 
 /**
@@ -28,16 +28,20 @@ class IniFileLoader extends FileLoader
     {
         $path = $this->locator->locate($resource);
 
-        $this->container->addResource(new FileResource($path));
+        $this->container->fileExists($path);
 
+        // first pass to catch parsing errors
         $result = parse_ini_file($path, true);
-        if (false === $result || array() === $result) {
+        if (false === $result || [] === $result) {
             throw new InvalidArgumentException(sprintf('The "%s" file is not valid.', $resource));
         }
 
+        // real raw parsing
+        $result = parse_ini_file($path, true, INI_SCANNER_RAW);
+
         if (isset($result['parameters']) && \is_array($result['parameters'])) {
             foreach ($result['parameters'] as $key => $value) {
-                $this->container->setParameter($key, $value);
+                $this->container->setParameter($key, $this->phpize($value));
             }
         }
     }
@@ -47,6 +51,45 @@ class IniFileLoader extends FileLoader
      */
     public function supports($resource, $type = null)
     {
-        return \is_string($resource) && 'ini' === pathinfo($resource, PATHINFO_EXTENSION);
+        if (!\is_string($resource)) {
+            return false;
+        }
+
+        if (null === $type && 'ini' === pathinfo($resource, PATHINFO_EXTENSION)) {
+            return true;
+        }
+
+        return 'ini' === $type;
+    }
+
+    /**
+     * Note that the following features are not supported:
+     *  * strings with escaped quotes are not supported "foo\"bar";
+     *  * string concatenation ("foo" "bar").
+     */
+    private function phpize($value)
+    {
+        // trim on the right as comments removal keep whitespaces
+        if ($value !== $v = rtrim($value)) {
+            $value = '""' === substr_replace($v, '', 1, -1) ? substr($v, 1, -1) : $v;
+        }
+        $lowercaseValue = strtolower($value);
+
+        switch (true) {
+            case \defined($value):
+                return \constant($value);
+            case 'yes' === $lowercaseValue || 'on' === $lowercaseValue:
+                return true;
+            case 'no' === $lowercaseValue || 'off' === $lowercaseValue || 'none' === $lowercaseValue:
+                return false;
+            case isset($value[1]) && (
+                ("'" === $value[0] && "'" === $value[\strlen($value) - 1]) ||
+                ('"' === $value[0] && '"' === $value[\strlen($value) - 1])
+            ):
+                // quoted string
+                return substr($value, 1, -1);
+            default:
+                return XmlUtils::phpize($value);
+        }
     }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/PhpFileLoader.php b/civicrm/vendor/symfony/dependency-injection/Loader/PhpFileLoader.php
index c52bd8c98ccae918e295debcdbdf11d23605951e..ff8df43f8408f02c1f6d50b0a0dc246fc5e7e231 100644
--- a/civicrm/vendor/symfony/dependency-injection/Loader/PhpFileLoader.php
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/PhpFileLoader.php
@@ -11,7 +11,7 @@
 
 namespace Symfony\Component\DependencyInjection\Loader;
 
-use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
 
 /**
  * PhpFileLoader loads service definitions from a PHP file.
@@ -34,9 +34,18 @@ class PhpFileLoader extends FileLoader
 
         $path = $this->locator->locate($resource);
         $this->setCurrentDir(\dirname($path));
-        $this->container->addResource(new FileResource($path));
+        $this->container->fileExists($path);
 
-        include $path;
+        // the closure forbids access to the private scope in the included file
+        $load = \Closure::bind(function ($path) use ($container, $loader, $resource, $type) {
+            return include $path;
+        }, $this, ProtectedPhpFileLoader::class);
+
+        $callback = $load($path);
+
+        if ($callback instanceof \Closure) {
+            $callback(new ContainerConfigurator($this->container, $this, $this->instanceof, $path, $resource), $this->container, $this);
+        }
     }
 
     /**
@@ -44,6 +53,21 @@ class PhpFileLoader extends FileLoader
      */
     public function supports($resource, $type = null)
     {
-        return \is_string($resource) && 'php' === pathinfo($resource, PATHINFO_EXTENSION);
+        if (!\is_string($resource)) {
+            return false;
+        }
+
+        if (null === $type && 'php' === pathinfo($resource, PATHINFO_EXTENSION)) {
+            return true;
+        }
+
+        return 'php' === $type;
     }
 }
+
+/**
+ * @internal
+ */
+final class ProtectedPhpFileLoader extends PhpFileLoader
+{
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/XmlFileLoader.php b/civicrm/vendor/symfony/dependency-injection/Loader/XmlFileLoader.php
index ecebe0125ec28321baee2e020d60e4c4c2365f66..6ccb66a421eae44f152d41160b17727486f5faa5 100644
--- a/civicrm/vendor/symfony/dependency-injection/Loader/XmlFileLoader.php
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/XmlFileLoader.php
@@ -11,12 +11,15 @@
 
 namespace Symfony\Component\DependencyInjection\Loader;
 
-use Symfony\Component\Config\Resource\FileResource;
 use Symfony\Component\Config\Util\XmlUtils;
 use Symfony\Component\DependencyInjection\Alias;
+use Symfony\Component\DependencyInjection\Argument\BoundArgument;
+use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
+use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
+use Symfony\Component\DependencyInjection\ChildDefinition;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\DependencyInjection\Definition;
-use Symfony\Component\DependencyInjection\DefinitionDecorator;
 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
 use Symfony\Component\DependencyInjection\Reference;
@@ -40,22 +43,28 @@ class XmlFileLoader extends FileLoader
 
         $xml = $this->parseFileToDOM($path);
 
-        $this->container->addResource(new FileResource($path));
+        $this->container->fileExists($path);
+
+        $defaults = $this->getServiceDefaults($xml, $path);
 
         // anonymous services
-        $this->processAnonymousServices($xml, $path);
+        $this->processAnonymousServices($xml, $path, $defaults);
 
         // imports
         $this->parseImports($xml, $path);
 
         // parameters
-        $this->parseParameters($xml);
+        $this->parseParameters($xml, $path);
 
         // extensions
         $this->loadFromExtensions($xml);
 
         // services
-        $this->parseDefinitions($xml, $path);
+        try {
+            $this->parseDefinitions($xml, $path, $defaults);
+        } finally {
+            $this->instanceof = [];
+        }
     }
 
     /**
@@ -63,26 +72,33 @@ class XmlFileLoader extends FileLoader
      */
     public function supports($resource, $type = null)
     {
-        return \is_string($resource) && 'xml' === pathinfo($resource, PATHINFO_EXTENSION);
+        if (!\is_string($resource)) {
+            return false;
+        }
+
+        if (null === $type && 'xml' === pathinfo($resource, PATHINFO_EXTENSION)) {
+            return true;
+        }
+
+        return 'xml' === $type;
     }
 
     /**
      * Parses parameters.
      *
-     * @param \DOMDocument $xml
+     * @param string $file
      */
-    private function parseParameters(\DOMDocument $xml)
+    private function parseParameters(\DOMDocument $xml, $file)
     {
         if ($parameters = $this->getChildren($xml->documentElement, 'parameters')) {
-            $this->container->getParameterBag()->add($this->getArgumentsAsPhp($parameters[0], 'parameter'));
+            $this->container->getParameterBag()->add($this->getArgumentsAsPhp($parameters[0], 'parameter', $file));
         }
     }
 
     /**
      * Parses imports.
      *
-     * @param \DOMDocument $xml
-     * @param string       $file
+     * @param string $file
      */
     private function parseImports(\DOMDocument $xml, $file)
     {
@@ -96,64 +112,148 @@ class XmlFileLoader extends FileLoader
         $defaultDirectory = \dirname($file);
         foreach ($imports as $import) {
             $this->setCurrentDir($defaultDirectory);
-            $this->import($import->getAttribute('resource'), null, (bool) XmlUtils::phpize($import->getAttribute('ignore-errors')), $file);
+            $this->import($import->getAttribute('resource'), XmlUtils::phpize($import->getAttribute('type')) ?: null, (bool) XmlUtils::phpize($import->getAttribute('ignore-errors')), $file);
         }
     }
 
     /**
      * Parses multiple definitions.
      *
-     * @param \DOMDocument $xml
-     * @param string       $file
+     * @param string $file
      */
-    private function parseDefinitions(\DOMDocument $xml, $file)
+    private function parseDefinitions(\DOMDocument $xml, $file, $defaults)
     {
         $xpath = new \DOMXPath($xml);
         $xpath->registerNamespace('container', self::NS);
 
-        if (false === $services = $xpath->query('//container:services/container:service')) {
+        if (false === $services = $xpath->query('//container:services/container:service|//container:services/container:prototype')) {
             return;
         }
+        $this->setCurrentDir(\dirname($file));
 
+        $this->instanceof = [];
+        $this->isLoadingInstanceof = true;
+        $instanceof = $xpath->query('//container:services/container:instanceof');
+        foreach ($instanceof as $service) {
+            $this->setDefinition((string) $service->getAttribute('id'), $this->parseDefinition($service, $file, []));
+        }
+
+        $this->isLoadingInstanceof = false;
         foreach ($services as $service) {
-            if (null !== $definition = $this->parseDefinition($service, $file)) {
-                $this->container->setDefinition((string) $service->getAttribute('id'), $definition);
+            if (null !== $definition = $this->parseDefinition($service, $file, $defaults)) {
+                if ('prototype' === $service->tagName) {
+                    $this->registerClasses($definition, (string) $service->getAttribute('namespace'), (string) $service->getAttribute('resource'), (string) $service->getAttribute('exclude'));
+                } else {
+                    $this->setDefinition((string) $service->getAttribute('id'), $definition);
+                }
             }
         }
     }
 
+    /**
+     * Get service defaults.
+     *
+     * @return array
+     */
+    private function getServiceDefaults(\DOMDocument $xml, $file)
+    {
+        $xpath = new \DOMXPath($xml);
+        $xpath->registerNamespace('container', self::NS);
+
+        if (null === $defaultsNode = $xpath->query('//container:services/container:defaults')->item(0)) {
+            return [];
+        }
+        $defaults = [
+            'tags' => $this->getChildren($defaultsNode, 'tag'),
+            'bind' => array_map(function ($v) { return new BoundArgument($v); }, $this->getArgumentsAsPhp($defaultsNode, 'bind', $file)),
+        ];
+
+        foreach ($defaults['tags'] as $tag) {
+            if ('' === $tag->getAttribute('name')) {
+                throw new InvalidArgumentException(sprintf('The tag name for tag "<defaults>" in "%s" must be a non-empty string.', $file));
+            }
+        }
+
+        if ($defaultsNode->hasAttribute('autowire')) {
+            $defaults['autowire'] = XmlUtils::phpize($defaultsNode->getAttribute('autowire'));
+        }
+        if ($defaultsNode->hasAttribute('public')) {
+            $defaults['public'] = XmlUtils::phpize($defaultsNode->getAttribute('public'));
+        }
+        if ($defaultsNode->hasAttribute('autoconfigure')) {
+            $defaults['autoconfigure'] = XmlUtils::phpize($defaultsNode->getAttribute('autoconfigure'));
+        }
+
+        return $defaults;
+    }
+
     /**
      * Parses an individual Definition.
      *
-     * @param \DOMElement $service
-     * @param string      $file
+     * @param string $file
      *
      * @return Definition|null
      */
-    private function parseDefinition(\DOMElement $service, $file)
+    private function parseDefinition(\DOMElement $service, $file, array $defaults)
     {
         if ($alias = $service->getAttribute('alias')) {
-            $public = true;
+            $this->validateAlias($service, $file);
+
+            $this->container->setAlias((string) $service->getAttribute('id'), $alias = new Alias($alias));
             if ($publicAttr = $service->getAttribute('public')) {
-                $public = XmlUtils::phpize($publicAttr);
+                $alias->setPublic(XmlUtils::phpize($publicAttr));
+            } elseif (isset($defaults['public'])) {
+                $alias->setPublic($defaults['public']);
             }
-            $this->container->setAlias((string) $service->getAttribute('id'), new Alias($alias, $public));
 
-            return;
+            return null;
         }
 
-        if ($parent = $service->getAttribute('parent')) {
-            $definition = new DefinitionDecorator($parent);
+        if ($this->isLoadingInstanceof) {
+            $definition = new ChildDefinition('');
+        } elseif ($parent = $service->getAttribute('parent')) {
+            if (!empty($this->instanceof)) {
+                throw new InvalidArgumentException(sprintf('The service "%s" cannot use the "parent" option in the same file where "instanceof" configuration is defined as using both is not supported. Move your child definitions to a separate file.', $service->getAttribute('id')));
+            }
+
+            foreach ($defaults as $k => $v) {
+                if ('tags' === $k) {
+                    // since tags are never inherited from parents, there is no confusion
+                    // thus we can safely add them as defaults to ChildDefinition
+                    continue;
+                }
+                if ('bind' === $k) {
+                    if ($defaults['bind']) {
+                        throw new InvalidArgumentException(sprintf('Bound values on service "%s" cannot be inherited from "defaults" when a "parent" is set. Move your child definitions to a separate file.', $service->getAttribute('id')));
+                    }
+
+                    continue;
+                }
+                if (!$service->hasAttribute($k)) {
+                    throw new InvalidArgumentException(sprintf('Attribute "%s" on service "%s" cannot be inherited from "defaults" when a "parent" is set. Move your child definitions to a separate file or define this attribute explicitly.', $k, $service->getAttribute('id')));
+                }
+            }
+
+            $definition = new ChildDefinition($parent);
         } else {
             $definition = new Definition();
+
+            if (isset($defaults['public'])) {
+                $definition->setPublic($defaults['public']);
+            }
+            if (isset($defaults['autowire'])) {
+                $definition->setAutowired($defaults['autowire']);
+            }
+            if (isset($defaults['autoconfigure'])) {
+                $definition->setAutoconfigured($defaults['autoconfigure']);
+            }
+
+            $definition->setChanges([]);
         }
 
-        foreach (array('class', 'shared', 'public', 'factory-class', 'factory-method', 'factory-service', 'synthetic', 'lazy', 'abstract') as $key) {
+        foreach (['class', 'public', 'shared', 'synthetic', 'lazy', 'abstract'] as $key) {
             if ($value = $service->getAttribute($key)) {
-                if (\in_array($key, array('factory-class', 'factory-method', 'factory-service'))) {
-                    @trigger_error(sprintf('The "%s" attribute of service "%s" in file "%s" is deprecated since Symfony 2.6 and will be removed in 3.0. Use the "factory" element instead.', $key, (string) $service->getAttribute('id'), $file), E_USER_DEPRECATED);
-                }
-                $method = 'set'.str_replace('-', '', $key);
+                $method = 'set'.$key;
                 $definition->$method(XmlUtils::phpize($value));
             }
         }
@@ -162,24 +262,12 @@ class XmlFileLoader extends FileLoader
             $definition->setAutowired(XmlUtils::phpize($value));
         }
 
-        if ($value = $service->getAttribute('scope')) {
-            $triggerDeprecation = 'request' !== (string) $service->getAttribute('id');
-
-            if ($triggerDeprecation) {
-                @trigger_error(sprintf('The "scope" attribute of service "%s" in file "%s" is deprecated since Symfony 2.8 and will be removed in 3.0.', (string) $service->getAttribute('id'), $file), E_USER_DEPRECATED);
-            }
-
-            $definition->setScope(XmlUtils::phpize($value), false);
-        }
-
-        if ($value = $service->getAttribute('synchronized')) {
-            $triggerDeprecation = 'request' !== (string) $service->getAttribute('id');
-
-            if ($triggerDeprecation) {
-                @trigger_error(sprintf('The "synchronized" attribute of service "%s" in file "%s" is deprecated since Symfony 2.7 and will be removed in 3.0.', (string) $service->getAttribute('id'), $file), E_USER_DEPRECATED);
+        if ($value = $service->getAttribute('autoconfigure')) {
+            if (!$definition instanceof ChildDefinition) {
+                $definition->setAutoconfigured(XmlUtils::phpize($value));
+            } elseif ($value = XmlUtils::phpize($value)) {
+                throw new InvalidArgumentException(sprintf('The service "%s" cannot have a "parent" and also have "autoconfigure". Try setting autoconfigure="false" for the service.', $service->getAttribute('id')));
             }
-
-            $definition->setSynchronized(XmlUtils::phpize($value), $triggerDeprecation);
         }
 
         if ($files = $this->getChildren($service, 'file')) {
@@ -190,25 +278,21 @@ class XmlFileLoader extends FileLoader
             $definition->setDeprecated(true, $deprecated[0]->nodeValue ?: null);
         }
 
-        $definition->setArguments($this->getArgumentsAsPhp($service, 'argument'));
-        $definition->setProperties($this->getArgumentsAsPhp($service, 'property'));
+        $definition->setArguments($this->getArgumentsAsPhp($service, 'argument', $file, $definition instanceof ChildDefinition));
+        $definition->setProperties($this->getArgumentsAsPhp($service, 'property', $file));
 
         if ($factories = $this->getChildren($service, 'factory')) {
             $factory = $factories[0];
             if ($function = $factory->getAttribute('function')) {
                 $definition->setFactory($function);
             } else {
-                $factoryService = $this->getChildren($factory, 'service');
-
-                if (isset($factoryService[0])) {
-                    $class = $this->parseDefinition($factoryService[0], $file);
-                } elseif ($childService = $factory->getAttribute('service')) {
-                    $class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false);
+                if ($childService = $factory->getAttribute('service')) {
+                    $class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE);
                 } else {
-                    $class = $factory->getAttribute('class');
+                    $class = $factory->hasAttribute('class') ? $factory->getAttribute('class') : null;
                 }
 
-                $definition->setFactory(array($class, $factory->getAttribute('method')));
+                $definition->setFactory([$class, $factory->getAttribute('method')]);
             }
         }
 
@@ -217,40 +301,42 @@ class XmlFileLoader extends FileLoader
             if ($function = $configurator->getAttribute('function')) {
                 $definition->setConfigurator($function);
             } else {
-                $configuratorService = $this->getChildren($configurator, 'service');
-
-                if (isset($configuratorService[0])) {
-                    $class = $this->parseDefinition($configuratorService[0], $file);
-                } elseif ($childService = $configurator->getAttribute('service')) {
-                    $class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false);
+                if ($childService = $configurator->getAttribute('service')) {
+                    $class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE);
                 } else {
                     $class = $configurator->getAttribute('class');
                 }
 
-                $definition->setConfigurator(array($class, $configurator->getAttribute('method')));
+                $definition->setConfigurator([$class, $configurator->getAttribute('method')]);
             }
         }
 
         foreach ($this->getChildren($service, 'call') as $call) {
-            $definition->addMethodCall($call->getAttribute('method'), $this->getArgumentsAsPhp($call, 'argument'));
+            $definition->addMethodCall($call->getAttribute('method'), $this->getArgumentsAsPhp($call, 'argument', $file));
+        }
+
+        $tags = $this->getChildren($service, 'tag');
+
+        if (!empty($defaults['tags'])) {
+            $tags = array_merge($tags, $defaults['tags']);
         }
 
-        foreach ($this->getChildren($service, 'tag') as $tag) {
-            $parameters = array();
+        foreach ($tags as $tag) {
+            $parameters = [];
             foreach ($tag->attributes as $name => $node) {
                 if ('name' === $name) {
                     continue;
                 }
 
-                if (false !== strpos($name, '-') && false === strpos($name, '_') && !array_key_exists($normalizedName = str_replace('-', '_', $name), $parameters)) {
+                if (false !== strpos($name, '-') && false === strpos($name, '_') && !\array_key_exists($normalizedName = str_replace('-', '_', $name), $parameters)) {
                     $parameters[$normalizedName] = XmlUtils::phpize($node->nodeValue);
                 }
-                // keep not normalized key for BC too
+                // keep not normalized key
                 $parameters[$name] = XmlUtils::phpize($node->nodeValue);
             }
 
             if ('' === $tag->getAttribute('name')) {
-                throw new InvalidArgumentException(sprintf('The tag name for service "%s" in %s must be a non-empty string.', (string) $service->getAttribute('id'), $file));
+                throw new InvalidArgumentException(sprintf('The tag name for service "%s" in "%s" must be a non-empty string.', (string) $service->getAttribute('id'), $file));
             }
 
             $definition->addTag($tag->getAttribute('name'), $parameters);
@@ -260,6 +346,15 @@ class XmlFileLoader extends FileLoader
             $definition->addAutowiringType($type->textContent);
         }
 
+        $bindings = $this->getArgumentsAsPhp($service, 'bind', $file);
+        if (isset($defaults['bind'])) {
+            // deep clone, to avoid multiple process of the same instance in the passes
+            $bindings = array_merge(unserialize(serialize($defaults['bind'])), $bindings);
+        }
+        if ($bindings) {
+            $definition->setBindings($bindings);
+        }
+
         if ($value = $service->getAttribute('decorates')) {
             $renameId = $service->hasAttribute('decoration-inner-name') ? $service->getAttribute('decoration-inner-name') : null;
             $priority = $service->hasAttribute('decoration-priority') ? $service->getAttribute('decoration-priority') : 0;
@@ -281,9 +376,9 @@ class XmlFileLoader extends FileLoader
     private function parseFileToDOM($file)
     {
         try {
-            $dom = XmlUtils::loadFile($file, array($this, 'validateSchema'));
+            $dom = XmlUtils::loadFile($file, [$this, 'validateSchema']);
         } catch (\InvalidArgumentException $e) {
-            throw new InvalidArgumentException(sprintf('Unable to parse file "%s".', $file), $e->getCode(), $e);
+            throw new InvalidArgumentException(sprintf('Unable to parse file "%s": "%s".', $file, $e->getMessage()), $e->getCode(), $e);
         }
 
         $this->validateExtensions($dom, $file);
@@ -294,26 +389,28 @@ class XmlFileLoader extends FileLoader
     /**
      * Processes anonymous services.
      *
-     * @param \DOMDocument $xml
-     * @param string       $file
+     * @param string $file
+     * @param array  $defaults
      */
-    private function processAnonymousServices(\DOMDocument $xml, $file)
+    private function processAnonymousServices(\DOMDocument $xml, $file, $defaults)
     {
-        $definitions = array();
+        $definitions = [];
         $count = 0;
+        $suffix = '~'.ContainerBuilder::hash($file);
 
         $xpath = new \DOMXPath($xml);
         $xpath->registerNamespace('container', self::NS);
 
         // anonymous services as arguments/properties
-        if (false !== $nodes = $xpath->query('//container:argument[@type="service"][not(@id)]|//container:property[@type="service"][not(@id)]')) {
+        if (false !== $nodes = $xpath->query('//container:argument[@type="service"][not(@id)]|//container:property[@type="service"][not(@id)]|//container:bind[not(@id)]|//container:factory[not(@service)]|//container:configurator[not(@service)]')) {
             foreach ($nodes as $node) {
-                // give it a unique name
-                $id = sprintf('%s_%d', hash('sha256', $file), ++$count);
-                $node->setAttribute('id', $id);
-
                 if ($services = $this->getChildren($node, 'service')) {
-                    $definitions[$id] = array($services[0], $file, false);
+                    // give it a unique name
+                    $id = sprintf('%d_%s', ++$count, preg_replace('/^.*\\\\/', '', $services[0]->getAttribute('class')).$suffix);
+                    $node->setAttribute('id', $id);
+                    $node->setAttribute('service', $id);
+
+                    $definitions[$id] = [$services[0], $file, false];
                     $services[0]->setAttribute('id', $id);
 
                     // anonymous services are always private
@@ -326,30 +423,26 @@ class XmlFileLoader extends FileLoader
         // anonymous services "in the wild"
         if (false !== $nodes = $xpath->query('//container:services/container:service[not(@id)]')) {
             foreach ($nodes as $node) {
+                @trigger_error(sprintf('Top-level anonymous services are deprecated since Symfony 3.4, the "id" attribute will be required in version 4.0 in %s at line %d.', $file, $node->getLineNo()), E_USER_DEPRECATED);
+
                 // give it a unique name
-                $id = sprintf('%s_%d', hash('sha256', $file), ++$count);
+                $id = sprintf('%d_%s', ++$count, preg_replace('/^.*\\\\/', '', $node->getAttribute('class')).$suffix);
                 $node->setAttribute('id', $id);
-                $definitions[$id] = array($node, $file, true);
+                $definitions[$id] = [$node, $file, true];
             }
         }
 
         // resolve definitions
-        krsort($definitions);
-        foreach ($definitions as $id => $def) {
-            list($domElement, $file, $wild) = $def;
-
-            if (null !== $definition = $this->parseDefinition($domElement, $file)) {
-                $this->container->setDefinition($id, $definition);
+        uksort($definitions, 'strnatcmp');
+        foreach (array_reverse($definitions) as $id => list($domElement, $file, $wild)) {
+            if (null !== $definition = $this->parseDefinition($domElement, $file, $wild ? $defaults : [])) {
+                $this->setDefinition($id, $definition);
             }
 
             if (true === $wild) {
                 $tmpDomElement = new \DOMElement('_services', null, self::NS);
                 $domElement->parentNode->replaceChild($tmpDomElement, $domElement);
                 $tmpDomElement->setAttribute('id', $id);
-            } else {
-                if (null !== $domElement->parentNode) {
-                    $domElement->parentNode->removeChild($domElement);
-                }
             }
         }
     }
@@ -357,24 +450,23 @@ class XmlFileLoader extends FileLoader
     /**
      * Returns arguments as valid php types.
      *
-     * @param \DOMElement $node
-     * @param string      $name
-     * @param bool        $lowercase
+     * @param string $name
+     * @param string $file
      *
      * @return mixed
      */
-    private function getArgumentsAsPhp(\DOMElement $node, $name, $lowercase = true)
+    private function getArgumentsAsPhp(\DOMElement $node, $name, $file, $isChildDefinition = false)
     {
-        $arguments = array();
+        $arguments = [];
         foreach ($this->getChildren($node, $name) as $arg) {
             if ($arg->hasAttribute('name')) {
                 $arg->setAttribute('key', $arg->getAttribute('name'));
             }
 
-            // this is used by DefinitionDecorator to overwrite a specific
+            // this is used by ChildDefinition to overwrite a specific
             // argument of the parent definition
             if ($arg->hasAttribute('index')) {
-                $key = 'index_'.$arg->getAttribute('index');
+                $key = ($isChildDefinition ? 'index_' : '').$arg->getAttribute('index');
             } elseif (!$arg->hasAttribute('key')) {
                 // Append an empty argument, then fetch its key to overwrite it later
                 $arguments[] = null;
@@ -382,36 +474,52 @@ class XmlFileLoader extends FileLoader
                 $key = array_pop($keys);
             } else {
                 $key = $arg->getAttribute('key');
+            }
 
-                // parameter keys are case insensitive
-                if ('parameter' == $name && $lowercase) {
-                    $key = strtolower($key);
-                }
+            $onInvalid = $arg->getAttribute('on-invalid');
+            $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
+            if ('ignore' == $onInvalid) {
+                $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
+            } elseif ('ignore_uninitialized' == $onInvalid) {
+                $invalidBehavior = ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE;
+            } elseif ('null' == $onInvalid) {
+                $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE;
             }
 
             switch ($arg->getAttribute('type')) {
                 case 'service':
-                    $onInvalid = $arg->getAttribute('on-invalid');
-                    $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
-                    if ('ignore' == $onInvalid) {
-                        $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
-                    } elseif ('null' == $onInvalid) {
-                        $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE;
+                    if ('' === $arg->getAttribute('id')) {
+                        throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="service" has no or empty "id" attribute in "%s".', $name, $file));
                     }
-
-                    if ($strict = $arg->getAttribute('strict')) {
-                        $strict = XmlUtils::phpize($strict);
-                    } else {
-                        $strict = true;
+                    if ($arg->hasAttribute('strict')) {
+                        @trigger_error(sprintf('The "strict" attribute used when referencing the "%s" service is deprecated since Symfony 3.3 and will be removed in 4.0.', $arg->getAttribute('id')), E_USER_DEPRECATED);
                     }
 
-                    $arguments[$key] = new Reference($arg->getAttribute('id'), $invalidBehavior, $strict);
+                    $arguments[$key] = new Reference($arg->getAttribute('id'), $invalidBehavior);
                     break;
                 case 'expression':
+                    if (!class_exists(Expression::class)) {
+                        throw new \LogicException(sprintf('The type="expression" attribute cannot be used without the ExpressionLanguage component. Try running "composer require symfony/expression-language".'));
+                    }
+
                     $arguments[$key] = new Expression($arg->nodeValue);
                     break;
                 case 'collection':
-                    $arguments[$key] = $this->getArgumentsAsPhp($arg, $name, false);
+                    $arguments[$key] = $this->getArgumentsAsPhp($arg, $name, $file);
+                    break;
+                case 'iterator':
+                    $arg = $this->getArgumentsAsPhp($arg, $name, $file);
+                    try {
+                        $arguments[$key] = new IteratorArgument($arg);
+                    } catch (InvalidArgumentException $e) {
+                        throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="iterator" only accepts collections of type="service" references in "%s".', $name, $file));
+                    }
+                    break;
+                case 'tagged':
+                    if (!$arg->getAttribute('tag')) {
+                        throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="tagged" has no or empty "tag" attribute in "%s".', $name, $file));
+                    }
+                    $arguments[$key] = new TaggedIteratorArgument($arg->getAttribute('tag'));
                     break;
                 case 'string':
                     $arguments[$key] = $arg->nodeValue;
@@ -430,14 +538,13 @@ class XmlFileLoader extends FileLoader
     /**
      * Get child elements by name.
      *
-     * @param \DOMNode $node
-     * @param mixed    $name
+     * @param mixed $name
      *
-     * @return array
+     * @return \DOMElement[]
      */
     private function getChildren(\DOMNode $node, $name)
     {
-        $children = array();
+        $children = [];
         foreach ($node->childNodes as $child) {
             if ($child instanceof \DOMElement && $child->localName === $name && self::NS === $child->namespaceURI) {
                 $children[] = $child;
@@ -450,15 +557,13 @@ class XmlFileLoader extends FileLoader
     /**
      * Validates a documents XML schema.
      *
-     * @param \DOMDocument $dom
-     *
      * @return bool
      *
      * @throws RuntimeException When extension references a non-existent XSD file
      */
     public function validateSchema(\DOMDocument $dom)
     {
-        $schemaLocations = array('http://symfony.com/schema/dic/services' => str_replace('\\', '/', __DIR__.'/schema/dic/services/services-1.0.xsd'));
+        $schemaLocations = ['http://symfony.com/schema/dic/services' => str_replace('\\', '/', __DIR__.'/schema/dic/services/services-1.0.xsd')];
 
         if ($element = $dom->documentElement->getAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'schemaLocation')) {
             $items = preg_split('/\s+/', $element);
@@ -468,10 +573,11 @@ class XmlFileLoader extends FileLoader
                 }
 
                 if (($extension = $this->container->getExtension($items[$i])) && false !== $extension->getXsdValidationBasePath()) {
-                    $path = str_replace($extension->getNamespace(), str_replace('\\', '/', $extension->getXsdValidationBasePath()).'/', $items[$i + 1]);
+                    $ns = $extension->getNamespace();
+                    $path = str_replace([$ns, str_replace('http://', 'https://', $ns)], str_replace('\\', '/', $extension->getXsdValidationBasePath()).'/', $items[$i + 1]);
 
                     if (!is_file($path)) {
-                        throw new RuntimeException(sprintf('Extension "%s" references a non-existent XSD file "%s"', \get_class($extension), $path));
+                        throw new RuntimeException(sprintf('Extension "%s" references a non-existent XSD file "%s".', \get_class($extension), $path));
                     }
 
                     $schemaLocations[$items[$i]] = $path;
@@ -479,13 +585,13 @@ class XmlFileLoader extends FileLoader
             }
         }
 
-        $tmpfiles = array();
+        $tmpfiles = [];
         $imports = '';
         foreach ($schemaLocations as $namespace => $location) {
             $parts = explode('/', $location);
             $locationstart = 'file:///';
             if (0 === stripos($location, 'phar://')) {
-                $tmpfile = tempnam(sys_get_temp_dir(), 'sf2');
+                $tmpfile = tempnam(sys_get_temp_dir(), 'symfony');
                 if ($tmpfile) {
                     copy($location, $tmpfile);
                     $tmpfiles[] = $tmpfile;
@@ -525,11 +631,30 @@ EOF
         return $valid;
     }
 
+    /**
+     * Validates an alias.
+     *
+     * @param string $file
+     */
+    private function validateAlias(\DOMElement $alias, $file)
+    {
+        foreach ($alias->attributes as $name => $node) {
+            if (!\in_array($name, ['alias', 'id', 'public'])) {
+                @trigger_error(sprintf('Using the attribute "%s" is deprecated for the service "%s" which is defined as an alias in "%s". Allowed attributes for service aliases are "alias", "id" and "public". The XmlFileLoader will raise an exception in Symfony 4.0, instead of silently ignoring unsupported attributes.', $name, $alias->getAttribute('id'), $file), E_USER_DEPRECATED);
+            }
+        }
+
+        foreach ($alias->childNodes as $child) {
+            if ($child instanceof \DOMElement && self::NS === $child->namespaceURI) {
+                @trigger_error(sprintf('Using the element "%s" is deprecated for the service "%s" which is defined as an alias in "%s". The XmlFileLoader will raise an exception in Symfony 4.0, instead of silently ignoring unsupported elements.', $child->localName, $alias->getAttribute('id'), $file), E_USER_DEPRECATED);
+            }
+        }
+    }
+
     /**
      * Validates an extension.
      *
-     * @param \DOMDocument $dom
-     * @param string       $file
+     * @param string $file
      *
      * @throws InvalidArgumentException When no extension is found corresponding to a tag
      */
@@ -543,15 +668,13 @@ EOF
             // can it be handled by an extension?
             if (!$this->container->hasExtension($node->namespaceURI)) {
                 $extensionNamespaces = array_filter(array_map(function ($ext) { return $ext->getNamespace(); }, $this->container->getExtensions()));
-                throw new InvalidArgumentException(sprintf('There is no extension able to load the configuration for "%s" (in %s). Looked for namespace "%s", found %s', $node->tagName, $file, $node->namespaceURI, $extensionNamespaces ? sprintf('"%s"', implode('", "', $extensionNamespaces)) : 'none'));
+                throw new InvalidArgumentException(sprintf('There is no extension able to load the configuration for "%s" (in "%s"). Looked for namespace "%s", found "%s".', $node->tagName, $file, $node->namespaceURI, $extensionNamespaces ? sprintf('"%s"', implode('", "', $extensionNamespaces)) : 'none'));
             }
         }
     }
 
     /**
      * Loads from an extension.
-     *
-     * @param \DOMDocument $xml
      */
     private function loadFromExtensions(\DOMDocument $xml)
     {
@@ -562,7 +685,7 @@ EOF
 
             $values = static::convertDomElementToArray($node);
             if (!\is_array($values)) {
-                $values = array();
+                $values = [];
             }
 
             $this->container->loadFromExtension($node->namespaceURI, $values);
@@ -586,7 +709,7 @@ EOF
      *
      * @param \DOMElement $element A \DOMElement instance
      *
-     * @return array A PHP array
+     * @return mixed
      */
     public static function convertDomElementToArray(\DOMElement $element)
     {
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/YamlFileLoader.php b/civicrm/vendor/symfony/dependency-injection/Loader/YamlFileLoader.php
index d25f654709b877146df6a79de9550d4b8a3ae212..bc0c55e94df4142fe439b9ee5880f0f9ecb295e7 100644
--- a/civicrm/vendor/symfony/dependency-injection/Loader/YamlFileLoader.php
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/YamlFileLoader.php
@@ -11,29 +11,102 @@
 
 namespace Symfony\Component\DependencyInjection\Loader;
 
-use Symfony\Component\Config\Resource\FileResource;
 use Symfony\Component\DependencyInjection\Alias;
+use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
+use Symfony\Component\DependencyInjection\Argument\BoundArgument;
+use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
+use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
+use Symfony\Component\DependencyInjection\ChildDefinition;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\DependencyInjection\Definition;
-use Symfony\Component\DependencyInjection\DefinitionDecorator;
 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
 use Symfony\Component\DependencyInjection\Reference;
 use Symfony\Component\ExpressionLanguage\Expression;
 use Symfony\Component\Yaml\Exception\ParseException;
 use Symfony\Component\Yaml\Parser as YamlParser;
+use Symfony\Component\Yaml\Tag\TaggedValue;
+use Symfony\Component\Yaml\Yaml;
 
 /**
  * YamlFileLoader loads YAML files service definitions.
  *
- * The YAML format does not support anonymous services (cf. the XML loader).
- *
  * @author Fabien Potencier <fabien@symfony.com>
  */
 class YamlFileLoader extends FileLoader
 {
+    private static $serviceKeywords = [
+        'alias' => 'alias',
+        'parent' => 'parent',
+        'class' => 'class',
+        'shared' => 'shared',
+        'synthetic' => 'synthetic',
+        'lazy' => 'lazy',
+        'public' => 'public',
+        'abstract' => 'abstract',
+        'deprecated' => 'deprecated',
+        'factory' => 'factory',
+        'file' => 'file',
+        'arguments' => 'arguments',
+        'properties' => 'properties',
+        'configurator' => 'configurator',
+        'calls' => 'calls',
+        'tags' => 'tags',
+        'decorates' => 'decorates',
+        'decoration_inner_name' => 'decoration_inner_name',
+        'decoration_priority' => 'decoration_priority',
+        'autowire' => 'autowire',
+        'autowiring_types' => 'autowiring_types',
+        'autoconfigure' => 'autoconfigure',
+        'bind' => 'bind',
+    ];
+
+    private static $prototypeKeywords = [
+        'resource' => 'resource',
+        'namespace' => 'namespace',
+        'exclude' => 'exclude',
+        'parent' => 'parent',
+        'shared' => 'shared',
+        'lazy' => 'lazy',
+        'public' => 'public',
+        'abstract' => 'abstract',
+        'deprecated' => 'deprecated',
+        'factory' => 'factory',
+        'arguments' => 'arguments',
+        'properties' => 'properties',
+        'configurator' => 'configurator',
+        'calls' => 'calls',
+        'tags' => 'tags',
+        'autowire' => 'autowire',
+        'autoconfigure' => 'autoconfigure',
+        'bind' => 'bind',
+    ];
+
+    private static $instanceofKeywords = [
+        'shared' => 'shared',
+        'lazy' => 'lazy',
+        'public' => 'public',
+        'properties' => 'properties',
+        'configurator' => 'configurator',
+        'calls' => 'calls',
+        'tags' => 'tags',
+        'autowire' => 'autowire',
+    ];
+
+    private static $defaultsKeywords = [
+        'public' => 'public',
+        'tags' => 'tags',
+        'autowire' => 'autowire',
+        'autoconfigure' => 'autoconfigure',
+        'bind' => 'bind',
+    ];
+
     private $yamlParser;
 
+    private $anonymousServicesCount;
+    private $anonymousServicesSuffix;
+
     /**
      * {@inheritdoc}
      */
@@ -43,7 +116,7 @@ class YamlFileLoader extends FileLoader
 
         $content = $this->loadFile($path);
 
-        $this->container->addResource(new FileResource($path));
+        $this->container->fileExists($path);
 
         // empty file
         if (null === $content) {
@@ -56,11 +129,11 @@ class YamlFileLoader extends FileLoader
         // parameters
         if (isset($content['parameters'])) {
             if (!\is_array($content['parameters'])) {
-                throw new InvalidArgumentException(sprintf('The "parameters" key should contain an array in %s. Check your YAML syntax.', $resource));
+                throw new InvalidArgumentException(sprintf('The "parameters" key should contain an array in "%s". Check your YAML syntax.', $path));
             }
 
             foreach ($content['parameters'] as $key => $value) {
-                $this->container->setParameter($key, $this->resolveServices($value));
+                $this->container->setParameter($key, $this->resolveServices($value, $path, true));
             }
         }
 
@@ -68,7 +141,14 @@ class YamlFileLoader extends FileLoader
         $this->loadFromExtensions($content);
 
         // services
-        $this->parseDefinitions($content, $resource);
+        $this->anonymousServicesCount = 0;
+        $this->anonymousServicesSuffix = '~'.ContainerBuilder::hash($path);
+        $this->setCurrentDir(\dirname($path));
+        try {
+            $this->parseDefinitions($content, $path);
+        } finally {
+            $this->instanceof = [];
+        }
     }
 
     /**
@@ -76,13 +156,20 @@ class YamlFileLoader extends FileLoader
      */
     public function supports($resource, $type = null)
     {
-        return \is_string($resource) && \in_array(pathinfo($resource, PATHINFO_EXTENSION), array('yml', 'yaml'), true);
+        if (!\is_string($resource)) {
+            return false;
+        }
+
+        if (null === $type && \in_array(pathinfo($resource, PATHINFO_EXTENSION), ['yaml', 'yml'], true)) {
+            return true;
+        }
+
+        return \in_array($type, ['yaml', 'yml'], true);
     }
 
     /**
      * Parses all imports.
      *
-     * @param array  $content
      * @param string $file
      */
     private function parseImports(array $content, $file)
@@ -92,24 +179,26 @@ class YamlFileLoader extends FileLoader
         }
 
         if (!\is_array($content['imports'])) {
-            throw new InvalidArgumentException(sprintf('The "imports" key should contain an array in %s. Check your YAML syntax.', $file));
+            throw new InvalidArgumentException(sprintf('The "imports" key should contain an array in "%s". Check your YAML syntax.', $file));
         }
 
         $defaultDirectory = \dirname($file);
         foreach ($content['imports'] as $import) {
             if (!\is_array($import)) {
-                throw new InvalidArgumentException(sprintf('The values in the "imports" key should be arrays in %s. Check your YAML syntax.', $file));
+                $import = ['resource' => $import];
+            }
+            if (!isset($import['resource'])) {
+                throw new InvalidArgumentException(sprintf('An import should provide a resource in "%s". Check your YAML syntax.', $file));
             }
 
             $this->setCurrentDir($defaultDirectory);
-            $this->import($import['resource'], null, isset($import['ignore_errors']) ? (bool) $import['ignore_errors'] : false, $file);
+            $this->import($import['resource'], isset($import['type']) ? $import['type'] : null, isset($import['ignore_errors']) ? (bool) $import['ignore_errors'] : false, $file);
         }
     }
 
     /**
      * Parses definitions.
      *
-     * @param array  $content
      * @param string $file
      */
     private function parseDefinitions(array $content, $file)
@@ -119,14 +208,114 @@ class YamlFileLoader extends FileLoader
         }
 
         if (!\is_array($content['services'])) {
-            throw new InvalidArgumentException(sprintf('The "services" key should contain an array in %s. Check your YAML syntax.', $file));
+            throw new InvalidArgumentException(sprintf('The "services" key should contain an array in "%s". Check your YAML syntax.', $file));
         }
 
+        if (\array_key_exists('_instanceof', $content['services'])) {
+            $instanceof = $content['services']['_instanceof'];
+            unset($content['services']['_instanceof']);
+
+            if (!\is_array($instanceof)) {
+                throw new InvalidArgumentException(sprintf('Service "_instanceof" key must be an array, "%s" given in "%s".', \gettype($instanceof), $file));
+            }
+            $this->instanceof = [];
+            $this->isLoadingInstanceof = true;
+            foreach ($instanceof as $id => $service) {
+                if (!$service || !\is_array($service)) {
+                    throw new InvalidArgumentException(sprintf('Type definition "%s" must be a non-empty array within "_instanceof" in "%s". Check your YAML syntax.', $id, $file));
+                }
+                if (\is_string($service) && 0 === strpos($service, '@')) {
+                    throw new InvalidArgumentException(sprintf('Type definition "%s" cannot be an alias within "_instanceof" in "%s". Check your YAML syntax.', $id, $file));
+                }
+                $this->parseDefinition($id, $service, $file, []);
+            }
+        }
+
+        $this->isLoadingInstanceof = false;
+        $defaults = $this->parseDefaults($content, $file);
         foreach ($content['services'] as $id => $service) {
-            $this->parseDefinition($id, $service, $file);
+            $this->parseDefinition($id, $service, $file, $defaults);
         }
     }
 
+    /**
+     * @param string $file
+     *
+     * @return array
+     *
+     * @throws InvalidArgumentException
+     */
+    private function parseDefaults(array &$content, $file)
+    {
+        if (!\array_key_exists('_defaults', $content['services'])) {
+            return [];
+        }
+        $defaults = $content['services']['_defaults'];
+        unset($content['services']['_defaults']);
+
+        if (!\is_array($defaults)) {
+            throw new InvalidArgumentException(sprintf('Service "_defaults" key must be an array, "%s" given in "%s".', \gettype($defaults), $file));
+        }
+
+        foreach ($defaults as $key => $default) {
+            if (!isset(self::$defaultsKeywords[$key])) {
+                throw new InvalidArgumentException(sprintf('The configuration key "%s" cannot be used to define a default value in "%s". Allowed keys are "%s".', $key, $file, implode('", "', self::$defaultsKeywords)));
+            }
+        }
+
+        if (isset($defaults['tags'])) {
+            if (!\is_array($tags = $defaults['tags'])) {
+                throw new InvalidArgumentException(sprintf('Parameter "tags" in "_defaults" must be an array in "%s". Check your YAML syntax.', $file));
+            }
+
+            foreach ($tags as $tag) {
+                if (!\is_array($tag)) {
+                    $tag = ['name' => $tag];
+                }
+
+                if (!isset($tag['name'])) {
+                    throw new InvalidArgumentException(sprintf('A "tags" entry in "_defaults" is missing a "name" key in "%s".', $file));
+                }
+                $name = $tag['name'];
+                unset($tag['name']);
+
+                if (!\is_string($name) || '' === $name) {
+                    throw new InvalidArgumentException(sprintf('The tag name in "_defaults" must be a non-empty string in "%s".', $file));
+                }
+
+                foreach ($tag as $attribute => $value) {
+                    if (!is_scalar($value) && null !== $value) {
+                        throw new InvalidArgumentException(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type in "%s". Check your YAML syntax.', $name, $attribute, $file));
+                    }
+                }
+            }
+        }
+
+        if (isset($defaults['bind'])) {
+            if (!\is_array($defaults['bind'])) {
+                throw new InvalidArgumentException(sprintf('Parameter "bind" in "_defaults" must be an array in "%s". Check your YAML syntax.', $file));
+            }
+
+            $defaults['bind'] = array_map(function ($v) { return new BoundArgument($v); }, $this->resolveServices($defaults['bind'], $file));
+        }
+
+        return $defaults;
+    }
+
+    /**
+     * @return bool
+     */
+    private function isUsingShortSyntax(array $service)
+    {
+        foreach ($service as $key => $value) {
+            if (\is_string($key) && ('' === $key || '$' !== $key[0])) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
     /**
      * Parses a definition.
      *
@@ -136,29 +325,87 @@ class YamlFileLoader extends FileLoader
      *
      * @throws InvalidArgumentException When tags are invalid
      */
-    private function parseDefinition($id, $service, $file)
+    private function parseDefinition($id, $service, $file, array $defaults)
     {
+        if (preg_match('/^_[a-zA-Z0-9_]*$/', $id)) {
+            @trigger_error(sprintf('Service names that start with an underscore are deprecated since Symfony 3.3 and will be reserved in 4.0. Rename the "%s" service or define it in XML instead.', $id), E_USER_DEPRECATED);
+        }
         if (\is_string($service) && 0 === strpos($service, '@')) {
-            $this->container->setAlias($id, substr($service, 1));
+            $this->container->setAlias($id, $alias = new Alias(substr($service, 1)));
+            if (isset($defaults['public'])) {
+                $alias->setPublic($defaults['public']);
+            }
 
             return;
         }
 
+        if (\is_array($service) && $this->isUsingShortSyntax($service)) {
+            $service = ['arguments' => $service];
+        }
+
+        if (null === $service) {
+            $service = [];
+        }
+
         if (!\is_array($service)) {
-            throw new InvalidArgumentException(sprintf('A service definition must be an array or a string starting with "@" but %s found for service "%s" in %s. Check your YAML syntax.', \gettype($service), $id, $file));
+            throw new InvalidArgumentException(sprintf('A service definition must be an array or a string starting with "@" but "%s" found for service "%s" in "%s". Check your YAML syntax.', \gettype($service), $id, $file));
         }
 
+        $this->checkDefinition($id, $service, $file);
+
         if (isset($service['alias'])) {
-            $public = !array_key_exists('public', $service) || (bool) $service['public'];
-            $this->container->setAlias($id, new Alias($service['alias'], $public));
+            $this->container->setAlias($id, $alias = new Alias($service['alias']));
+            if (isset($service['public'])) {
+                $alias->setPublic($service['public']);
+            } elseif (isset($defaults['public'])) {
+                $alias->setPublic($defaults['public']);
+            }
+
+            foreach ($service as $key => $value) {
+                if (!\in_array($key, ['alias', 'public'])) {
+                    @trigger_error(sprintf('The configuration key "%s" is unsupported for the service "%s" which is defined as an alias in "%s". Allowed configuration keys for service aliases are "alias" and "public". The YamlFileLoader will raise an exception in Symfony 4.0, instead of silently ignoring unsupported attributes.', $key, $id, $file), E_USER_DEPRECATED);
+                }
+            }
 
             return;
         }
 
-        if (isset($service['parent'])) {
-            $definition = new DefinitionDecorator($service['parent']);
+        if ($this->isLoadingInstanceof) {
+            $definition = new ChildDefinition('');
+        } elseif (isset($service['parent'])) {
+            if (!empty($this->instanceof)) {
+                throw new InvalidArgumentException(sprintf('The service "%s" cannot use the "parent" option in the same file where "_instanceof" configuration is defined as using both is not supported. Move your child definitions to a separate file.', $id));
+            }
+
+            foreach ($defaults as $k => $v) {
+                if ('tags' === $k) {
+                    // since tags are never inherited from parents, there is no confusion
+                    // thus we can safely add them as defaults to ChildDefinition
+                    continue;
+                }
+                if ('bind' === $k) {
+                    throw new InvalidArgumentException(sprintf('Attribute "bind" on service "%s" cannot be inherited from "_defaults" when a "parent" is set. Move your child definitions to a separate file.', $id));
+                }
+                if (!isset($service[$k])) {
+                    throw new InvalidArgumentException(sprintf('Attribute "%s" on service "%s" cannot be inherited from "_defaults" when a "parent" is set. Move your child definitions to a separate file or define this attribute explicitly.', $k, $id));
+                }
+            }
+
+            $definition = new ChildDefinition($service['parent']);
         } else {
             $definition = new Definition();
+
+            if (isset($defaults['public'])) {
+                $definition->setPublic($defaults['public']);
+            }
+            if (isset($defaults['autowire'])) {
+                $definition->setAutowired($defaults['autowire']);
+            }
+            if (isset($defaults['autoconfigure'])) {
+                $definition->setAutoconfigured($defaults['autoconfigure']);
+            }
+
+            $definition->setChanges([]);
         }
 
         if (isset($service['class'])) {
@@ -169,22 +416,10 @@ class YamlFileLoader extends FileLoader
             $definition->setShared($service['shared']);
         }
 
-        if (isset($service['scope'])) {
-            if ('request' !== $id) {
-                @trigger_error(sprintf('The "scope" key of service "%s" in file "%s" is deprecated since Symfony 2.8 and will be removed in 3.0.', $id, $file), E_USER_DEPRECATED);
-            }
-            $definition->setScope($service['scope'], false);
-        }
-
         if (isset($service['synthetic'])) {
             $definition->setSynthetic($service['synthetic']);
         }
 
-        if (isset($service['synchronized'])) {
-            @trigger_error(sprintf('The "synchronized" key of service "%s" in file "%s" is deprecated since Symfony 2.7 and will be removed in 3.0.', $id, $file), E_USER_DEPRECATED);
-            $definition->setSynchronized($service['synchronized'], 'request' !== $id);
-        }
-
         if (isset($service['lazy'])) {
             $definition->setLazy($service['lazy']);
         }
@@ -197,36 +432,12 @@ class YamlFileLoader extends FileLoader
             $definition->setAbstract($service['abstract']);
         }
 
-        if (array_key_exists('deprecated', $service)) {
+        if (\array_key_exists('deprecated', $service)) {
             $definition->setDeprecated(true, $service['deprecated']);
         }
 
         if (isset($service['factory'])) {
-            if (\is_string($service['factory'])) {
-                if (false !== strpos($service['factory'], ':') && false === strpos($service['factory'], '::')) {
-                    $parts = explode(':', $service['factory']);
-                    $definition->setFactory(array($this->resolveServices('@'.$parts[0]), $parts[1]));
-                } else {
-                    $definition->setFactory($service['factory']);
-                }
-            } else {
-                $definition->setFactory(array($this->resolveServices($service['factory'][0]), $service['factory'][1]));
-            }
-        }
-
-        if (isset($service['factory_class'])) {
-            @trigger_error(sprintf('The "factory_class" key of service "%s" in file "%s" is deprecated since Symfony 2.6 and will be removed in 3.0. Use "factory" instead.', $id, $file), E_USER_DEPRECATED);
-            $definition->setFactoryClass($service['factory_class']);
-        }
-
-        if (isset($service['factory_method'])) {
-            @trigger_error(sprintf('The "factory_method" key of service "%s" in file "%s" is deprecated since Symfony 2.6 and will be removed in 3.0. Use "factory" instead.', $id, $file), E_USER_DEPRECATED);
-            $definition->setFactoryMethod($service['factory_method']);
-        }
-
-        if (isset($service['factory_service'])) {
-            @trigger_error(sprintf('The "factory_service" key of service "%s" in file "%s" is deprecated since Symfony 2.6 and will be removed in 3.0. Use "factory" instead.', $id, $file), E_USER_DEPRECATED);
-            $definition->setFactoryService($service['factory_service']);
+            $definition->setFactory($this->parseCallable($service['factory'], 'factory', $id, $file));
         }
 
         if (isset($service['file'])) {
@@ -234,68 +445,69 @@ class YamlFileLoader extends FileLoader
         }
 
         if (isset($service['arguments'])) {
-            $definition->setArguments($this->resolveServices($service['arguments']));
+            $definition->setArguments($this->resolveServices($service['arguments'], $file));
         }
 
         if (isset($service['properties'])) {
-            $definition->setProperties($this->resolveServices($service['properties']));
+            $definition->setProperties($this->resolveServices($service['properties'], $file));
         }
 
         if (isset($service['configurator'])) {
-            if (\is_string($service['configurator'])) {
-                $definition->setConfigurator($service['configurator']);
-            } else {
-                $definition->setConfigurator(array($this->resolveServices($service['configurator'][0]), $service['configurator'][1]));
-            }
+            $definition->setConfigurator($this->parseCallable($service['configurator'], 'configurator', $id, $file));
         }
 
         if (isset($service['calls'])) {
             if (!\is_array($service['calls'])) {
-                throw new InvalidArgumentException(sprintf('Parameter "calls" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file));
+                throw new InvalidArgumentException(sprintf('Parameter "calls" must be an array for service "%s" in "%s". Check your YAML syntax.', $id, $file));
             }
 
             foreach ($service['calls'] as $call) {
                 if (isset($call['method'])) {
                     $method = $call['method'];
-                    $args = isset($call['arguments']) ? $this->resolveServices($call['arguments']) : array();
+                    $args = isset($call['arguments']) ? $this->resolveServices($call['arguments'], $file) : [];
                 } else {
                     $method = $call[0];
-                    $args = isset($call[1]) ? $this->resolveServices($call[1]) : array();
+                    $args = isset($call[1]) ? $this->resolveServices($call[1], $file) : [];
                 }
 
+                if (!\is_array($args)) {
+                    throw new InvalidArgumentException(sprintf('The second parameter for function call "%s" must be an array of its arguments for service "%s" in "%s". Check your YAML syntax.', $method, $id, $file));
+                }
                 $definition->addMethodCall($method, $args);
             }
         }
 
-        if (isset($service['tags'])) {
-            if (!\is_array($service['tags'])) {
-                throw new InvalidArgumentException(sprintf('Parameter "tags" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file));
-            }
+        $tags = isset($service['tags']) ? $service['tags'] : [];
+        if (!\is_array($tags)) {
+            throw new InvalidArgumentException(sprintf('Parameter "tags" must be an array for service "%s" in "%s". Check your YAML syntax.', $id, $file));
+        }
 
-            foreach ($service['tags'] as $tag) {
-                if (!\is_array($tag)) {
-                    throw new InvalidArgumentException(sprintf('A "tags" entry must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file));
-                }
+        if (isset($defaults['tags'])) {
+            $tags = array_merge($tags, $defaults['tags']);
+        }
 
-                if (!isset($tag['name'])) {
-                    throw new InvalidArgumentException(sprintf('A "tags" entry is missing a "name" key for service "%s" in %s.', $id, $file));
-                }
+        foreach ($tags as $tag) {
+            if (!\is_array($tag)) {
+                $tag = ['name' => $tag];
+            }
 
-                if (!\is_string($tag['name']) || '' === $tag['name']) {
-                    throw new InvalidArgumentException(sprintf('The tag name for service "%s" in %s must be a non-empty string.', $id, $file));
-                }
+            if (!isset($tag['name'])) {
+                throw new InvalidArgumentException(sprintf('A "tags" entry is missing a "name" key for service "%s" in "%s".', $id, $file));
+            }
+            $name = $tag['name'];
+            unset($tag['name']);
 
-                $name = $tag['name'];
-                unset($tag['name']);
+            if (!\is_string($name) || '' === $name) {
+                throw new InvalidArgumentException(sprintf('The tag name for service "%s" in "%s" must be a non-empty string.', $id, $file));
+            }
 
-                foreach ($tag as $attribute => $value) {
-                    if (!is_scalar($value) && null !== $value) {
-                        throw new InvalidArgumentException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in %s. Check your YAML syntax.', $id, $name, $attribute, $file));
-                    }
+            foreach ($tag as $attribute => $value) {
+                if (!is_scalar($value) && null !== $value) {
+                    throw new InvalidArgumentException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in "%s". Check your YAML syntax.', $id, $name, $attribute, $file));
                 }
-
-                $definition->addTag($name, $tag);
             }
+
+            $definition->addTag($name, $tag);
         }
 
         if (isset($service['decorates'])) {
@@ -317,12 +529,12 @@ class YamlFileLoader extends FileLoader
                 $definition->addAutowiringType($service['autowiring_types']);
             } else {
                 if (!\is_array($service['autowiring_types'])) {
-                    throw new InvalidArgumentException(sprintf('Parameter "autowiring_types" must be a string or an array for service "%s" in %s. Check your YAML syntax.', $id, $file));
+                    throw new InvalidArgumentException(sprintf('Parameter "autowiring_types" must be a string or an array for service "%s" in "%s". Check your YAML syntax.', $id, $file));
                 }
 
                 foreach ($service['autowiring_types'] as $autowiringType) {
                     if (!\is_string($autowiringType)) {
-                        throw new InvalidArgumentException(sprintf('A "autowiring_types" attribute must be of type string for service "%s" in %s. Check your YAML syntax.', $id, $file));
+                        throw new InvalidArgumentException(sprintf('A "autowiring_types" attribute must be of type string for service "%s" in "%s". Check your YAML syntax.', $id, $file));
                     }
 
                     $definition->addAutowiringType($autowiringType);
@@ -330,7 +542,86 @@ class YamlFileLoader extends FileLoader
             }
         }
 
-        $this->container->setDefinition($id, $definition);
+        if (isset($defaults['bind']) || isset($service['bind'])) {
+            // deep clone, to avoid multiple process of the same instance in the passes
+            $bindings = isset($defaults['bind']) ? unserialize(serialize($defaults['bind'])) : [];
+
+            if (isset($service['bind'])) {
+                if (!\is_array($service['bind'])) {
+                    throw new InvalidArgumentException(sprintf('Parameter "bind" must be an array for service "%s" in "%s". Check your YAML syntax.', $id, $file));
+                }
+
+                $bindings = array_merge($bindings, $this->resolveServices($service['bind'], $file));
+            }
+
+            $definition->setBindings($bindings);
+        }
+
+        if (isset($service['autoconfigure'])) {
+            if (!$definition instanceof ChildDefinition) {
+                $definition->setAutoconfigured($service['autoconfigure']);
+            } elseif ($service['autoconfigure']) {
+                throw new InvalidArgumentException(sprintf('The service "%s" cannot have a "parent" and also have "autoconfigure". Try setting "autoconfigure: false" for the service.', $id));
+            }
+        }
+
+        if (\array_key_exists('namespace', $service) && !\array_key_exists('resource', $service)) {
+            throw new InvalidArgumentException(sprintf('A "resource" attribute must be set when the "namespace" attribute is set for service "%s" in "%s". Check your YAML syntax.', $id, $file));
+        }
+
+        if (\array_key_exists('resource', $service)) {
+            if (!\is_string($service['resource'])) {
+                throw new InvalidArgumentException(sprintf('A "resource" attribute must be of type string for service "%s" in "%s". Check your YAML syntax.', $id, $file));
+            }
+            $exclude = isset($service['exclude']) ? $service['exclude'] : null;
+            $namespace = isset($service['namespace']) ? $service['namespace'] : $id;
+            $this->registerClasses($definition, $namespace, $service['resource'], $exclude);
+        } else {
+            $this->setDefinition($id, $definition);
+        }
+    }
+
+    /**
+     * Parses a callable.
+     *
+     * @param string|array $callable  A callable
+     * @param string       $parameter A parameter (e.g. 'factory' or 'configurator')
+     * @param string       $id        A service identifier
+     * @param string       $file      A parsed file
+     *
+     * @throws InvalidArgumentException When errors occur
+     *
+     * @return string|array A parsed callable
+     */
+    private function parseCallable($callable, $parameter, $id, $file)
+    {
+        if (\is_string($callable)) {
+            if ('' !== $callable && '@' === $callable[0]) {
+                throw new InvalidArgumentException(sprintf('The value of the "%s" option for the "%s" service must be the id of the service without the "@" prefix (replace "%s" with "%s").', $parameter, $id, $callable, substr($callable, 1)));
+            }
+
+            if (false !== strpos($callable, ':') && false === strpos($callable, '::')) {
+                $parts = explode(':', $callable);
+
+                return [$this->resolveServices('@'.$parts[0], $file), $parts[1]];
+            }
+
+            return $callable;
+        }
+
+        if (\is_array($callable)) {
+            if (isset($callable[0]) && isset($callable[1])) {
+                return [$this->resolveServices($callable[0], $file), $callable[1]];
+            }
+
+            if ('factory' === $parameter && isset($callable[1]) && null === $callable[0]) {
+                return $callable;
+            }
+
+            throw new InvalidArgumentException(sprintf('Parameter "%s" must contain an array with two elements for service "%s" in "%s". Check your YAML syntax.', $parameter, $id, $file));
+        }
+
+        throw new InvalidArgumentException(sprintf('Parameter "%s" must be a string or an array for service "%s" in "%s". Check your YAML syntax.', $parameter, $id, $file));
     }
 
     /**
@@ -360,10 +651,18 @@ class YamlFileLoader extends FileLoader
             $this->yamlParser = new YamlParser();
         }
 
+        $prevErrorHandler = set_error_handler(function ($level, $message, $script, $line) use ($file, &$prevErrorHandler) {
+            $message = E_USER_DEPRECATED === $level ? preg_replace('/ on line \d+/', ' in "'.$file.'"$0', $message) : $message;
+
+            return $prevErrorHandler ? $prevErrorHandler($level, $message, $script, $line) : false;
+        });
+
         try {
-            $configuration = $this->yamlParser->parse(file_get_contents($file));
+            $configuration = $this->yamlParser->parseFile($file, Yaml::PARSE_CONSTANT | Yaml::PARSE_CUSTOM_TAGS);
         } catch (ParseException $e) {
-            throw new InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $file), 0, $e);
+            throw new InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML: '.$e->getMessage(), $file), 0, $e);
+        } finally {
+            restore_error_handler();
         }
 
         return $this->validate($configuration, $file);
@@ -390,13 +689,13 @@ class YamlFileLoader extends FileLoader
         }
 
         foreach ($content as $namespace => $data) {
-            if (\in_array($namespace, array('imports', 'parameters', 'services'))) {
+            if (\in_array($namespace, ['imports', 'parameters', 'services'])) {
                 continue;
             }
 
             if (!$this->container->hasExtension($namespace)) {
                 $extensionNamespaces = array_filter(array_map(function ($ext) { return $ext->getAlias(); }, $this->container->getExtensions()));
-                throw new InvalidArgumentException(sprintf('There is no extension able to load the configuration for "%s" (in %s). Looked for namespace "%s", found %s', $namespace, $file, $namespace, $extensionNamespaces ? sprintf('"%s"', implode('", "', $extensionNamespaces)) : 'none'));
+                throw new InvalidArgumentException(sprintf('There is no extension able to load the configuration for "%s" (in "%s"). Looked for namespace "%s", found "%s".', $namespace, $file, $namespace, $extensionNamespaces ? sprintf('"%s"', implode('", "', $extensionNamespaces)) : 'none'));
             }
         }
 
@@ -406,20 +705,79 @@ class YamlFileLoader extends FileLoader
     /**
      * Resolves services.
      *
-     * @param string|array $value
+     * @param mixed  $value
+     * @param string $file
+     * @param bool   $isParameter
      *
-     * @return array|string|Reference
+     * @return array|string|Reference|ArgumentInterface
      */
-    private function resolveServices($value)
+    private function resolveServices($value, $file, $isParameter = false)
     {
+        if ($value instanceof TaggedValue) {
+            $argument = $value->getValue();
+            if ('iterator' === $value->getTag()) {
+                if (!\is_array($argument)) {
+                    throw new InvalidArgumentException(sprintf('"!iterator" tag only accepts sequences in "%s".', $file));
+                }
+                $argument = $this->resolveServices($argument, $file, $isParameter);
+                try {
+                    return new IteratorArgument($argument);
+                } catch (InvalidArgumentException $e) {
+                    throw new InvalidArgumentException(sprintf('"!iterator" tag only accepts arrays of "@service" references in "%s".', $file));
+                }
+            }
+            if ('tagged' === $value->getTag()) {
+                if (!\is_string($argument) || !$argument) {
+                    throw new InvalidArgumentException(sprintf('"!tagged" tag only accepts non empty string in "%s".', $file));
+                }
+
+                return new TaggedIteratorArgument($argument);
+            }
+            if ('service' === $value->getTag()) {
+                if ($isParameter) {
+                    throw new InvalidArgumentException(sprintf('Using an anonymous service in a parameter is not allowed in "%s".', $file));
+                }
+
+                $isLoadingInstanceof = $this->isLoadingInstanceof;
+                $this->isLoadingInstanceof = false;
+                $instanceof = $this->instanceof;
+                $this->instanceof = [];
+
+                $id = sprintf('%d_%s', ++$this->anonymousServicesCount, preg_replace('/^.*\\\\/', '', isset($argument['class']) ? $argument['class'] : '').$this->anonymousServicesSuffix);
+                $this->parseDefinition($id, $argument, $file, []);
+
+                if (!$this->container->hasDefinition($id)) {
+                    throw new InvalidArgumentException(sprintf('Creating an alias using the tag "!service" is not allowed in "%s".', $file));
+                }
+
+                $this->container->getDefinition($id)->setPublic(false);
+
+                $this->isLoadingInstanceof = $isLoadingInstanceof;
+                $this->instanceof = $instanceof;
+
+                return new Reference($id);
+            }
+
+            throw new InvalidArgumentException(sprintf('Unsupported tag "!%s".', $value->getTag()));
+        }
+
         if (\is_array($value)) {
-            $value = array_map(array($this, 'resolveServices'), $value);
+            foreach ($value as $k => $v) {
+                $value[$k] = $this->resolveServices($v, $file, $isParameter);
+            }
         } elseif (\is_string($value) && 0 === strpos($value, '@=')) {
+            if (!class_exists(Expression::class)) {
+                throw new \LogicException(sprintf('The "@=" expression syntax cannot be used without the ExpressionLanguage component. Try running "composer require symfony/expression-language".'));
+            }
+
             return new Expression(substr($value, 2));
         } elseif (\is_string($value) && 0 === strpos($value, '@')) {
             if (0 === strpos($value, '@@')) {
                 $value = substr($value, 1);
                 $invalidBehavior = null;
+            } elseif (0 === strpos($value, '@!')) {
+                $value = substr($value, 2);
+                $invalidBehavior = ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE;
             } elseif (0 === strpos($value, '@?')) {
                 $value = substr($value, 2);
                 $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
@@ -429,14 +787,12 @@ class YamlFileLoader extends FileLoader
             }
 
             if ('=' === substr($value, -1)) {
+                @trigger_error(sprintf('The "=" suffix that used to disable strict references in Symfony 2.x is deprecated since Symfony 3.3 and will be unsupported in 4.0. Remove it in "%s".', $value), E_USER_DEPRECATED);
                 $value = substr($value, 0, -1);
-                $strict = false;
-            } else {
-                $strict = true;
             }
 
             if (null !== $invalidBehavior) {
-                $value = new Reference($value, $invalidBehavior, $strict);
+                $value = new Reference($value, $invalidBehavior);
             }
         }
 
@@ -449,15 +805,43 @@ class YamlFileLoader extends FileLoader
     private function loadFromExtensions(array $content)
     {
         foreach ($content as $namespace => $values) {
-            if (\in_array($namespace, array('imports', 'parameters', 'services'))) {
+            if (\in_array($namespace, ['imports', 'parameters', 'services'])) {
                 continue;
             }
 
             if (!\is_array($values) && null !== $values) {
-                $values = array();
+                $values = [];
             }
 
             $this->container->loadFromExtension($namespace, $values);
         }
     }
+
+    /**
+     * Checks the keywords used to define a service.
+     *
+     * @param string $id         The service name
+     * @param array  $definition The service definition to check
+     * @param string $file       The loaded YAML file
+     */
+    private function checkDefinition($id, array $definition, $file)
+    {
+        if ($throw = $this->isLoadingInstanceof) {
+            $keywords = self::$instanceofKeywords;
+        } elseif ($throw = (isset($definition['resource']) || isset($definition['namespace']))) {
+            $keywords = self::$prototypeKeywords;
+        } else {
+            $keywords = self::$serviceKeywords;
+        }
+
+        foreach ($definition as $key => $value) {
+            if (!isset($keywords[$key])) {
+                if ($throw) {
+                    throw new InvalidArgumentException(sprintf('The configuration key "%s" is unsupported for definition "%s" in "%s". Allowed configuration keys are "%s".', $key, $id, $file, implode('", "', $keywords)));
+                }
+
+                @trigger_error(sprintf('The configuration key "%s" is unsupported for service definition "%s" in "%s". Allowed configuration keys are "%s". The YamlFileLoader object will raise an exception instead in Symfony 4.0 when detecting an unsupported service configuration key.', $key, $id, $file, implode('", "', $keywords)), E_USER_DEPRECATED);
+            }
+        }
+    }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Loader/schema/dic/services/services-1.0.xsd b/civicrm/vendor/symfony/dependency-injection/Loader/schema/dic/services/services-1.0.xsd
index a8d7cf88df8f5cd8cdca202846a9e0e6aa709dfe..3a55a7df676c74c4d97075ba954425e0fc1de05a 100644
--- a/civicrm/vendor/symfony/dependency-injection/Loader/schema/dic/services/services-1.0.xsd
+++ b/civicrm/vendor/symfony/dependency-injection/Loader/schema/dic/services/services-1.0.xsd
@@ -52,8 +52,11 @@
         Enclosing element for the definition of all services
       ]]></xsd:documentation>
     </xsd:annotation>
-    <xsd:choice minOccurs="1" maxOccurs="unbounded">
-      <xsd:element name="service" type="service" />
+    <xsd:choice maxOccurs="unbounded">
+      <xsd:element name="service" type="service" minOccurs="1" />
+      <xsd:element name="prototype" type="prototype" minOccurs="0" />
+      <xsd:element name="defaults" type="defaults" minOccurs="0" maxOccurs="1" />
+      <xsd:element name="instanceof" type="instanceof" minOccurs="0" />
     </xsd:choice>
   </xsd:complexType>
 
@@ -76,19 +79,34 @@
     </xsd:annotation>
     <xsd:attribute name="resource" type="xsd:string" use="required" />
     <xsd:attribute name="ignore-errors" type="boolean" />
+    <xsd:attribute name="type" type="xsd:string" />
   </xsd:complexType>
 
   <xsd:complexType name="callable">
     <xsd:choice minOccurs="0" maxOccurs="1">
       <xsd:element name="service" type="service" minOccurs="0" maxOccurs="1" />
     </xsd:choice>
-    <xsd:attribute name="id" type="xsd:string" />
     <xsd:attribute name="service" type="xsd:string" />
     <xsd:attribute name="class" type="xsd:string" />
     <xsd:attribute name="method" type="xsd:string" />
     <xsd:attribute name="function" type="xsd:string" />
   </xsd:complexType>
 
+  <xsd:complexType name="defaults">
+    <xsd:annotation>
+      <xsd:documentation><![CDATA[
+        Enclosing element for the service definitions' defaults for the current file
+      ]]></xsd:documentation>
+    </xsd:annotation>
+    <xsd:choice maxOccurs="unbounded">
+      <xsd:element name="tag" type="tag" minOccurs="0" maxOccurs="unbounded" />
+      <xsd:element name="bind" type="bind" minOccurs="0" maxOccurs="unbounded" />
+    </xsd:choice>
+    <xsd:attribute name="public" type="boolean" />
+    <xsd:attribute name="autowire" type="boolean" />
+    <xsd:attribute name="autoconfigure" type="boolean" />
+  </xsd:complexType>
+
   <xsd:complexType name="service">
     <xsd:choice maxOccurs="unbounded">
       <xsd:element name="file" type="xsd:string" minOccurs="0" maxOccurs="1" />
@@ -100,25 +118,60 @@
       <xsd:element name="tag" type="tag" minOccurs="0" maxOccurs="unbounded" />
       <xsd:element name="property" type="property" minOccurs="0" maxOccurs="unbounded" />
       <xsd:element name="autowiring-type" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
+      <xsd:element name="bind" type="bind" minOccurs="0" maxOccurs="unbounded" />
     </xsd:choice>
     <xsd:attribute name="id" type="xsd:string" />
     <xsd:attribute name="class" type="xsd:string" />
     <xsd:attribute name="shared" type="boolean" />
-    <xsd:attribute name="scope" type="xsd:string" />
     <xsd:attribute name="public" type="boolean" />
     <xsd:attribute name="synthetic" type="boolean" />
-    <xsd:attribute name="synchronized" type="boolean" />
     <xsd:attribute name="lazy" type="boolean" />
     <xsd:attribute name="abstract" type="boolean" />
-    <xsd:attribute name="factory-class" type="xsd:string" />
-    <xsd:attribute name="factory-method" type="xsd:string" />
-    <xsd:attribute name="factory-service" type="xsd:string" />
     <xsd:attribute name="alias" type="xsd:string" />
     <xsd:attribute name="parent" type="xsd:string" />
     <xsd:attribute name="decorates" type="xsd:string" />
     <xsd:attribute name="decoration-inner-name" type="xsd:string" />
     <xsd:attribute name="decoration-priority" type="xsd:integer" />
     <xsd:attribute name="autowire" type="boolean" />
+    <xsd:attribute name="autoconfigure" type="boolean" />
+  </xsd:complexType>
+
+  <xsd:complexType name="instanceof">
+    <xsd:choice maxOccurs="unbounded">
+      <xsd:element name="configurator" type="callable" minOccurs="0" maxOccurs="1" />
+      <xsd:element name="call" type="call" minOccurs="0" maxOccurs="unbounded" />
+      <xsd:element name="tag" type="tag" minOccurs="0" maxOccurs="unbounded" />
+      <xsd:element name="property" type="property" minOccurs="0" maxOccurs="unbounded" />
+    </xsd:choice>
+    <xsd:attribute name="id" type="xsd:string" use="required" />
+    <xsd:attribute name="shared" type="boolean" />
+    <xsd:attribute name="public" type="boolean" />
+    <xsd:attribute name="lazy" type="boolean" />
+    <xsd:attribute name="autowire" type="boolean" />
+    <xsd:attribute name="autoconfigure" type="boolean" />
+  </xsd:complexType>
+
+  <xsd:complexType name="prototype">
+    <xsd:choice maxOccurs="unbounded">
+      <xsd:element name="argument" type="argument" minOccurs="0" maxOccurs="unbounded" />
+      <xsd:element name="configurator" type="callable" minOccurs="0" maxOccurs="1" />
+      <xsd:element name="factory" type="callable" minOccurs="0" maxOccurs="1" />
+      <xsd:element name="deprecated" type="xsd:string" minOccurs="0" maxOccurs="1" />
+      <xsd:element name="call" type="call" minOccurs="0" maxOccurs="unbounded" />
+      <xsd:element name="tag" type="tag" minOccurs="0" maxOccurs="unbounded" />
+      <xsd:element name="property" type="property" minOccurs="0" maxOccurs="unbounded" />
+      <xsd:element name="bind" type="bind" minOccurs="0" maxOccurs="unbounded" />
+    </xsd:choice>
+    <xsd:attribute name="namespace" type="xsd:string" use="required" />
+    <xsd:attribute name="resource" type="xsd:string" use="required" />
+    <xsd:attribute name="exclude" type="xsd:string" />
+    <xsd:attribute name="shared" type="boolean" />
+    <xsd:attribute name="public" type="boolean" />
+    <xsd:attribute name="lazy" type="boolean" />
+    <xsd:attribute name="abstract" type="boolean" />
+    <xsd:attribute name="parent" type="xsd:string" />
+    <xsd:attribute name="autowire" type="boolean" />
+    <xsd:attribute name="autoconfigure" type="boolean" />
   </xsd:complexType>
 
   <xsd:complexType name="tag">
@@ -155,6 +208,19 @@
     <xsd:attribute name="name" type="xsd:string" />
     <xsd:attribute name="on-invalid" type="invalid_sequence" />
     <xsd:attribute name="strict" type="boolean" />
+    <xsd:attribute name="tag" type="xsd:string" />
+  </xsd:complexType>
+
+  <xsd:complexType name="bind" mixed="true">
+    <xsd:choice maxOccurs="unbounded">
+      <xsd:element name="bind" type="argument" minOccurs="0" maxOccurs="unbounded" />
+      <xsd:element name="service" type="service" />
+    </xsd:choice>
+    <xsd:attribute name="type" type="argument_type" />
+    <xsd:attribute name="id" type="xsd:string" />
+    <xsd:attribute name="key" type="xsd:string" use="required" />
+    <xsd:attribute name="on-invalid" type="invalid_sequence" />
+    <xsd:attribute name="method" type="xsd:string" />
   </xsd:complexType>
 
   <xsd:complexType name="argument" mixed="true">
@@ -168,6 +234,7 @@
     <xsd:attribute name="index" type="xsd:integer" />
     <xsd:attribute name="on-invalid" type="invalid_sequence" />
     <xsd:attribute name="strict" type="boolean" />
+    <xsd:attribute name="tag" type="xsd:string" />
   </xsd:complexType>
 
   <xsd:complexType name="call">
@@ -192,6 +259,8 @@
       <xsd:enumeration value="expression" />
       <xsd:enumeration value="string" />
       <xsd:enumeration value="constant" />
+      <xsd:enumeration value="iterator" />
+      <xsd:enumeration value="tagged" />
     </xsd:restriction>
   </xsd:simpleType>
 
@@ -200,6 +269,7 @@
       <xsd:enumeration value="null" />
       <xsd:enumeration value="ignore" />
       <xsd:enumeration value="exception" />
+      <xsd:enumeration value="ignore_uninitialized" />
     </xsd:restriction>
   </xsd:simpleType>
 
diff --git a/civicrm/vendor/symfony/dependency-injection/ParameterBag/EnvPlaceholderParameterBag.php b/civicrm/vendor/symfony/dependency-injection/ParameterBag/EnvPlaceholderParameterBag.php
new file mode 100644
index 0000000000000000000000000000000000000000..c4e369b010d83b5cb6e13486905fcd3a563a0926
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/ParameterBag/EnvPlaceholderParameterBag.php
@@ -0,0 +1,123 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\ParameterBag;
+
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class EnvPlaceholderParameterBag extends ParameterBag
+{
+    private $envPlaceholders = [];
+    private $providedTypes = [];
+
+    /**
+     * {@inheritdoc}
+     */
+    public function get($name)
+    {
+        if (0 === strpos($name, 'env(') && ')' === substr($name, -1) && 'env()' !== $name) {
+            $env = substr($name, 4, -1);
+
+            if (isset($this->envPlaceholders[$env])) {
+                foreach ($this->envPlaceholders[$env] as $placeholder) {
+                    return $placeholder; // return first result
+                }
+            }
+            if (!preg_match('/^(?:\w++:)*+\w++$/', $env)) {
+                throw new InvalidArgumentException(sprintf('Invalid "%s" name: only "word" characters are allowed.', $name));
+            }
+
+            if ($this->has($name)) {
+                $defaultValue = parent::get($name);
+
+                if (null !== $defaultValue && !is_scalar($defaultValue)) {
+                    throw new RuntimeException(sprintf('The default value of an env() parameter must be scalar or null, but "%s" given to "%s".', \gettype($defaultValue), $name));
+                }
+            }
+
+            $uniqueName = md5($name.uniqid(mt_rand(), true));
+            $placeholder = sprintf('env_%s_%s', str_replace(':', '_', $env), $uniqueName);
+            $this->envPlaceholders[$env][$placeholder] = $placeholder;
+
+            return $placeholder;
+        }
+
+        return parent::get($name);
+    }
+
+    /**
+     * Returns the map of env vars used in the resolved parameter values to their placeholders.
+     *
+     * @return string[][] A map of env var names to their placeholders
+     */
+    public function getEnvPlaceholders()
+    {
+        return $this->envPlaceholders;
+    }
+
+    /**
+     * Merges the env placeholders of another EnvPlaceholderParameterBag.
+     */
+    public function mergeEnvPlaceholders(self $bag)
+    {
+        if ($newPlaceholders = $bag->getEnvPlaceholders()) {
+            $this->envPlaceholders += $newPlaceholders;
+
+            foreach ($newPlaceholders as $env => $placeholders) {
+                $this->envPlaceholders[$env] += $placeholders;
+            }
+        }
+    }
+
+    /**
+     * Maps env prefixes to their corresponding PHP types.
+     */
+    public function setProvidedTypes(array $providedTypes)
+    {
+        $this->providedTypes = $providedTypes;
+    }
+
+    /**
+     * Gets the PHP types corresponding to env() parameter prefixes.
+     *
+     * @return string[][]
+     */
+    public function getProvidedTypes()
+    {
+        return $this->providedTypes;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function resolve()
+    {
+        if ($this->resolved) {
+            return;
+        }
+        parent::resolve();
+
+        foreach ($this->envPlaceholders as $env => $placeholders) {
+            if (!$this->has($name = "env($env)")) {
+                continue;
+            }
+            if (is_numeric($default = $this->parameters[$name])) {
+                $this->parameters[$name] = (string) $default;
+            } elseif (null !== $default && !is_scalar($default)) {
+                throw new RuntimeException(sprintf('The default value of env parameter "%s" must be scalar or null, "%s" given.', $env, \gettype($default)));
+            }
+        }
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/ParameterBag/FrozenParameterBag.php b/civicrm/vendor/symfony/dependency-injection/ParameterBag/FrozenParameterBag.php
index ad65ad960f527b866a12f8f2527fe529e07a874e..a5199937e233fec391e6405cb051e25d10b07838 100644
--- a/civicrm/vendor/symfony/dependency-injection/ParameterBag/FrozenParameterBag.php
+++ b/civicrm/vendor/symfony/dependency-injection/ParameterBag/FrozenParameterBag.php
@@ -28,7 +28,7 @@ class FrozenParameterBag extends ParameterBag
      *
      * @param array $parameters An array of parameters
      */
-    public function __construct(array $parameters = array())
+    public function __construct(array $parameters = [])
     {
         $this->parameters = $parameters;
         $this->resolved = true;
diff --git a/civicrm/vendor/symfony/dependency-injection/ParameterBag/ParameterBag.php b/civicrm/vendor/symfony/dependency-injection/ParameterBag/ParameterBag.php
index c17029b3ef85175bf2ec6a50fd9ae69ea2365823..539d56616d6b42017ab5f1e3ec7a0d09868eadff 100644
--- a/civicrm/vendor/symfony/dependency-injection/ParameterBag/ParameterBag.php
+++ b/civicrm/vendor/symfony/dependency-injection/ParameterBag/ParameterBag.php
@@ -22,13 +22,15 @@ use Symfony\Component\DependencyInjection\Exception\RuntimeException;
  */
 class ParameterBag implements ParameterBagInterface
 {
-    protected $parameters = array();
+    protected $parameters = [];
     protected $resolved = false;
 
+    private $normalizedNames = [];
+
     /**
      * @param array $parameters An array of parameters
      */
-    public function __construct(array $parameters = array())
+    public function __construct(array $parameters = [])
     {
         $this->add($parameters);
     }
@@ -38,7 +40,7 @@ class ParameterBag implements ParameterBagInterface
      */
     public function clear()
     {
-        $this->parameters = array();
+        $this->parameters = [];
     }
 
     /**
@@ -49,7 +51,7 @@ class ParameterBag implements ParameterBagInterface
     public function add(array $parameters)
     {
         foreach ($parameters as $key => $value) {
-            $this->parameters[strtolower($key)] = $value;
+            $this->set($key, $value);
         }
     }
 
@@ -66,14 +68,14 @@ class ParameterBag implements ParameterBagInterface
      */
     public function get($name)
     {
-        $name = strtolower($name);
+        $name = $this->normalizeName($name);
 
-        if (!array_key_exists($name, $this->parameters)) {
+        if (!\array_key_exists($name, $this->parameters)) {
             if (!$name) {
                 throw new ParameterNotFoundException($name);
             }
 
-            $alternatives = array();
+            $alternatives = [];
             foreach ($this->parameters as $key => $parameterValue) {
                 $lev = levenshtein($name, $key);
                 if ($lev <= \strlen($name) / 3 || false !== strpos($key, $name)) {
@@ -81,7 +83,23 @@ class ParameterBag implements ParameterBagInterface
                 }
             }
 
-            throw new ParameterNotFoundException($name, null, null, null, $alternatives);
+            $nonNestedAlternative = null;
+            if (!\count($alternatives) && false !== strpos($name, '.')) {
+                $namePartsLength = array_map('strlen', explode('.', $name));
+                $key = substr($name, 0, -1 * (1 + array_pop($namePartsLength)));
+                while (\count($namePartsLength)) {
+                    if ($this->has($key)) {
+                        if (\is_array($this->get($key))) {
+                            $nonNestedAlternative = $key;
+                        }
+                        break;
+                    }
+
+                    $key = substr($key, 0, -1 * (1 + array_pop($namePartsLength)));
+                }
+            }
+
+            throw new ParameterNotFoundException($name, null, null, null, $alternatives, $nonNestedAlternative);
         }
 
         return $this->parameters[$name];
@@ -95,7 +113,7 @@ class ParameterBag implements ParameterBagInterface
      */
     public function set($name, $value)
     {
-        $this->parameters[strtolower($name)] = $value;
+        $this->parameters[$this->normalizeName($name)] = $value;
     }
 
     /**
@@ -103,7 +121,7 @@ class ParameterBag implements ParameterBagInterface
      */
     public function has($name)
     {
-        return array_key_exists(strtolower($name), $this->parameters);
+        return \array_key_exists($this->normalizeName($name), $this->parameters);
     }
 
     /**
@@ -113,7 +131,7 @@ class ParameterBag implements ParameterBagInterface
      */
     public function remove($name)
     {
-        unset($this->parameters[strtolower($name)]);
+        unset($this->parameters[$this->normalizeName($name)]);
     }
 
     /**
@@ -125,7 +143,7 @@ class ParameterBag implements ParameterBagInterface
             return;
         }
 
-        $parameters = array();
+        $parameters = [];
         foreach ($this->parameters as $key => $value) {
             try {
                 $value = $this->resolveValue($value);
@@ -153,18 +171,18 @@ class ParameterBag implements ParameterBagInterface
      * @throws ParameterCircularReferenceException if a circular reference if detected
      * @throws RuntimeException                    when a given parameter has a type problem
      */
-    public function resolveValue($value, array $resolving = array())
+    public function resolveValue($value, array $resolving = [])
     {
         if (\is_array($value)) {
-            $args = array();
+            $args = [];
             foreach ($value as $k => $v) {
-                $args[$this->resolveValue($k, $resolving)] = $this->resolveValue($v, $resolving);
+                $args[\is_string($k) ? $this->resolveValue($k, $resolving) : $k] = $this->resolveValue($v, $resolving);
             }
 
             return $args;
         }
 
-        if (!\is_string($value)) {
+        if (!\is_string($value) || 2 > \strlen($value)) {
             return $value;
         }
 
@@ -177,52 +195,52 @@ class ParameterBag implements ParameterBagInterface
      * @param string $value     The string to resolve
      * @param array  $resolving An array of keys that are being resolved (used internally to detect circular references)
      *
-     * @return string The resolved string
+     * @return mixed The resolved string
      *
      * @throws ParameterNotFoundException          if a placeholder references a parameter that does not exist
      * @throws ParameterCircularReferenceException if a circular reference if detected
      * @throws RuntimeException                    when a given parameter has a type problem
      */
-    public function resolveString($value, array $resolving = array())
+    public function resolveString($value, array $resolving = [])
     {
         // we do this to deal with non string values (Boolean, integer, ...)
         // as the preg_replace_callback throw an exception when trying
         // a non-string in a parameter value
         if (preg_match('/^%([^%\s]+)%$/', $value, $match)) {
-            $key = strtolower($match[1]);
+            $key = $match[1];
+            $lcKey = strtolower($key); // strtolower() to be removed in 4.0
 
-            if (isset($resolving[$key])) {
+            if (isset($resolving[$lcKey])) {
                 throw new ParameterCircularReferenceException(array_keys($resolving));
             }
 
-            $resolving[$key] = true;
+            $resolving[$lcKey] = true;
 
             return $this->resolved ? $this->get($key) : $this->resolveValue($this->get($key), $resolving);
         }
 
-        $self = $this;
-
-        return preg_replace_callback('/%%|%([^%\s]+)%/', function ($match) use ($self, $resolving, $value) {
+        return preg_replace_callback('/%%|%([^%\s]+)%/', function ($match) use ($resolving, $value) {
             // skip %%
             if (!isset($match[1])) {
                 return '%%';
             }
 
-            $key = strtolower($match[1]);
-            if (isset($resolving[$key])) {
+            $key = $match[1];
+            $lcKey = strtolower($key); // strtolower() to be removed in 4.0
+            if (isset($resolving[$lcKey])) {
                 throw new ParameterCircularReferenceException(array_keys($resolving));
             }
 
-            $resolved = $self->get($key);
+            $resolved = $this->get($key);
 
             if (!\is_string($resolved) && !is_numeric($resolved)) {
-                throw new RuntimeException(sprintf('A string value must be composed of strings and/or numbers, but found parameter "%s" of type %s inside string value "%s".', $key, \gettype($resolved), $value));
+                throw new RuntimeException(sprintf('A string value must be composed of strings and/or numbers, but found parameter "%s" of type "%s" inside string value "%s".', $key, \gettype($resolved), $value));
             }
 
             $resolved = (string) $resolved;
-            $resolving[$key] = true;
+            $resolving[$lcKey] = true;
 
-            return $self->isResolved() ? $resolved : $self->resolveString($resolved, $resolving);
+            return $this->isResolved() ? $resolved : $this->resolveString($resolved, $resolving);
         }, $value);
     }
 
@@ -241,7 +259,7 @@ class ParameterBag implements ParameterBagInterface
         }
 
         if (\is_array($value)) {
-            $result = array();
+            $result = [];
             foreach ($value as $k => $v) {
                 $result[$k] = $this->escapeValue($v);
             }
@@ -262,7 +280,7 @@ class ParameterBag implements ParameterBagInterface
         }
 
         if (\is_array($value)) {
-            $result = array();
+            $result = [];
             foreach ($value as $k => $v) {
                 $result[$k] = $this->unescapeValue($v);
             }
@@ -272,4 +290,18 @@ class ParameterBag implements ParameterBagInterface
 
         return $value;
     }
+
+    private function normalizeName($name)
+    {
+        if (isset($this->normalizedNames[$normalizedName = strtolower($name)])) {
+            $normalizedName = $this->normalizedNames[$normalizedName];
+            if ((string) $name !== $normalizedName) {
+                @trigger_error(sprintf('Parameter names will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since Symfony 3.4.', $name, $normalizedName), E_USER_DEPRECATED);
+            }
+        } else {
+            $normalizedName = $this->normalizedNames[$normalizedName] = (string) $name;
+        }
+
+        return $normalizedName;
+    }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/ParameterBag/ParameterBagInterface.php b/civicrm/vendor/symfony/dependency-injection/ParameterBag/ParameterBagInterface.php
index 3291b373deb9008c4da554857ffbd74a40972194..7386df06481a7c649f47e77d7581aeacd3f99469 100644
--- a/civicrm/vendor/symfony/dependency-injection/ParameterBag/ParameterBagInterface.php
+++ b/civicrm/vendor/symfony/dependency-injection/ParameterBag/ParameterBagInterface.php
@@ -55,6 +55,13 @@ interface ParameterBagInterface
      */
     public function get($name);
 
+    /**
+     * Removes a parameter.
+     *
+     * @param string $name The parameter name
+     */
+    public function remove($name);
+
     /**
      * Sets a service container parameter.
      *
diff --git a/civicrm/vendor/symfony/dependency-injection/README.md b/civicrm/vendor/symfony/dependency-injection/README.md
index 932647f94a903d62ac6297c5d9acc638f6c9261a..cb2d4a11c58869c32458d198a6c5dbe713b66814 100644
--- a/civicrm/vendor/symfony/dependency-injection/README.md
+++ b/civicrm/vendor/symfony/dependency-injection/README.md
@@ -7,7 +7,7 @@ way objects are constructed in your application.
 Resources
 ---------
 
-  * [Documentation](https://symfony.com/doc/current/components/dependency_injection/index.html)
+  * [Documentation](https://symfony.com/doc/current/components/dependency_injection.html)
   * [Contributing](https://symfony.com/doc/current/contributing/index.html)
   * [Report issues](https://github.com/symfony/symfony/issues) and
     [send Pull Requests](https://github.com/symfony/symfony/pulls)
diff --git a/civicrm/vendor/symfony/dependency-injection/Reference.php b/civicrm/vendor/symfony/dependency-injection/Reference.php
index 869dfae1bc029d41a0b8e0fc684c26c2faaab400..82906d2b7524c3b6654f5adf68ce9a58c685807d 100644
--- a/civicrm/vendor/symfony/dependency-injection/Reference.php
+++ b/civicrm/vendor/symfony/dependency-injection/Reference.php
@@ -20,22 +20,17 @@ class Reference
 {
     private $id;
     private $invalidBehavior;
-    private $strict;
 
     /**
-     * Note: The $strict parameter is deprecated since version 2.8 and will be removed in 3.0.
-     *
      * @param string $id              The service identifier
      * @param int    $invalidBehavior The behavior when the service does not exist
-     * @param bool   $strict          Sets how this reference is validated
      *
      * @see Container
      */
-    public function __construct($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $strict = true)
+    public function __construct($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE)
     {
-        $this->id = strtolower($id);
+        $this->id = (string) $id;
         $this->invalidBehavior = $invalidBehavior;
-        $this->strict = $strict;
     }
 
     /**
@@ -55,20 +50,4 @@ class Reference
     {
         return $this->invalidBehavior;
     }
-
-    /**
-     * Returns true when this Reference is strict.
-     *
-     * @return bool
-     *
-     * @deprecated since version 2.8, to be removed in 3.0.
-     */
-    public function isStrict($triggerDeprecationError = true)
-    {
-        if ($triggerDeprecationError) {
-            @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-        }
-
-        return $this->strict;
-    }
 }
diff --git a/civicrm/vendor/symfony/dependency-injection/Scope.php b/civicrm/vendor/symfony/dependency-injection/Scope.php
deleted file mode 100644
index b0b8ed6c2e5164d7973850dd3d9850373cdc21c4..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/dependency-injection/Scope.php
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\DependencyInjection;
-
-/**
- * Scope class.
- *
- * @author Johannes M. Schmitt <schmittjoh@gmail.com>
- *
- * @deprecated since version 2.8, to be removed in 3.0.
- */
-class Scope implements ScopeInterface
-{
-    private $name;
-    private $parentName;
-
-    public function __construct($name, $parentName = ContainerInterface::SCOPE_CONTAINER)
-    {
-        $this->name = $name;
-        $this->parentName = $parentName;
-    }
-
-    public function getName()
-    {
-        return $this->name;
-    }
-
-    public function getParentName()
-    {
-        return $this->parentName;
-    }
-}
diff --git a/civicrm/vendor/symfony/dependency-injection/ScopeInterface.php b/civicrm/vendor/symfony/dependency-injection/ScopeInterface.php
deleted file mode 100644
index 11b10973994adf337bcd1e9e67339d2b6f2971c9..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/dependency-injection/ScopeInterface.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\DependencyInjection;
-
-/**
- * Scope Interface.
- *
- * @author Johannes M. Schmitt <schmittjoh@gmail.com>
- *
- * @deprecated since version 2.8, to be removed in 3.0.
- */
-interface ScopeInterface
-{
-    public function getName();
-
-    public function getParentName();
-}
diff --git a/civicrm/vendor/symfony/dependency-injection/ServiceLocator.php b/civicrm/vendor/symfony/dependency-injection/ServiceLocator.php
new file mode 100644
index 0000000000000000000000000000000000000000..a4f5bf99453d7febd656d3669015e69e35ecac77
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/ServiceLocator.php
@@ -0,0 +1,146 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection;
+
+use Psr\Container\ContainerInterface as PsrContainerInterface;
+use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
+use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
+
+/**
+ * @author Robin Chalas <robin.chalas@gmail.com>
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class ServiceLocator implements PsrContainerInterface
+{
+    private $factories;
+    private $loading = [];
+    private $externalId;
+    private $container;
+
+    /**
+     * @param callable[] $factories
+     */
+    public function __construct(array $factories)
+    {
+        $this->factories = $factories;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function has($id)
+    {
+        return isset($this->factories[$id]);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function get($id)
+    {
+        if (!isset($this->factories[$id])) {
+            throw new ServiceNotFoundException($id, end($this->loading) ?: null, null, [], $this->createServiceNotFoundMessage($id));
+        }
+
+        if (isset($this->loading[$id])) {
+            $ids = array_values($this->loading);
+            $ids = \array_slice($this->loading, array_search($id, $ids));
+            $ids[] = $id;
+
+            throw new ServiceCircularReferenceException($id, $ids);
+        }
+
+        $this->loading[$id] = $id;
+        try {
+            return $this->factories[$id]();
+        } finally {
+            unset($this->loading[$id]);
+        }
+    }
+
+    public function __invoke($id)
+    {
+        return isset($this->factories[$id]) ? $this->get($id) : null;
+    }
+
+    /**
+     * @internal
+     */
+    public function withContext($externalId, Container $container)
+    {
+        $locator = clone $this;
+        $locator->externalId = $externalId;
+        $locator->container = $container;
+
+        return $locator;
+    }
+
+    private function createServiceNotFoundMessage($id)
+    {
+        if ($this->loading) {
+            return sprintf('The service "%s" has a dependency on a non-existent service "%s". This locator %s', end($this->loading), $id, $this->formatAlternatives());
+        }
+
+        $class = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS, 3);
+        $class = isset($class[2]['object']) ? \get_class($class[2]['object']) : null;
+        $externalId = $this->externalId ?: $class;
+
+        $msg = [];
+        $msg[] = sprintf('Service "%s" not found:', $id);
+
+        if (!$this->container) {
+            $class = null;
+        } elseif ($this->container->has($id) || isset($this->container->getRemovedIds()[$id])) {
+            $msg[] = 'even though it exists in the app\'s container,';
+        } else {
+            try {
+                $this->container->get($id);
+                $class = null;
+            } catch (ServiceNotFoundException $e) {
+                if ($e->getAlternatives()) {
+                    $msg[] = sprintf('did you mean %s? Anyway,', $this->formatAlternatives($e->getAlternatives(), 'or'));
+                } else {
+                    $class = null;
+                }
+            }
+        }
+        if ($externalId) {
+            $msg[] = sprintf('the container inside "%s" is a smaller service locator that %s', $externalId, $this->formatAlternatives());
+        } else {
+            $msg[] = sprintf('the current service locator %s', $this->formatAlternatives());
+        }
+
+        if (!$class) {
+            // no-op
+        } elseif (is_subclass_of($class, ServiceSubscriberInterface::class)) {
+            $msg[] = sprintf('Unless you need extra laziness, try using dependency injection instead. Otherwise, you need to declare it using "%s::getSubscribedServices()".', preg_replace('/([^\\\\]++\\\\)++/', '', $class));
+        } else {
+            $msg[] = 'Try using dependency injection instead.';
+        }
+
+        return implode(' ', $msg);
+    }
+
+    private function formatAlternatives(array $alternatives = null, $separator = 'and')
+    {
+        $format = '"%s"%s';
+        if (null === $alternatives) {
+            if (!$alternatives = array_keys($this->factories)) {
+                return 'is empty...';
+            }
+            $format = sprintf('only knows about the %s service%s.', $format, 1 < \count($alternatives) ? 's' : '');
+        }
+        $last = array_pop($alternatives);
+
+        return sprintf($format, $alternatives ? implode('", "', $alternatives) : $last, $alternatives ? sprintf(' %s "%s"', $separator, $last) : '');
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/ServiceSubscriberInterface.php b/civicrm/vendor/symfony/dependency-injection/ServiceSubscriberInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..10c238754421de2eedefbb1f2af90b09725ee86c
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/ServiceSubscriberInterface.php
@@ -0,0 +1,50 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection;
+
+/**
+ * A ServiceSubscriber exposes its dependencies via the static {@link getSubscribedServices} method.
+ *
+ * The getSubscribedServices method returns an array of service types required by such instances,
+ * optionally keyed by the service names used internally. Service types that start with an interrogation
+ * mark "?" are optional, while the other ones are mandatory service dependencies.
+ *
+ * The injected service locators SHOULD NOT allow access to any other services not specified by the method.
+ *
+ * It is expected that ServiceSubscriber instances consume PSR-11-based service locators internally.
+ * This interface does not dictate any injection method for these service locators, although constructor
+ * injection is recommended.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+interface ServiceSubscriberInterface
+{
+    /**
+     * Returns an array of service types required by such instances, optionally keyed by the service names used internally.
+     *
+     * For mandatory dependencies:
+     *
+     *  * ['logger' => 'Psr\Log\LoggerInterface'] means the objects use the "logger" name
+     *    internally to fetch a service which must implement Psr\Log\LoggerInterface.
+     *  * ['Psr\Log\LoggerInterface'] is a shortcut for
+     *  * ['Psr\Log\LoggerInterface' => 'Psr\Log\LoggerInterface']
+     *
+     * otherwise:
+     *
+     *  * ['logger' => '?Psr\Log\LoggerInterface'] denotes an optional dependency
+     *  * ['?Psr\Log\LoggerInterface'] is a shortcut for
+     *  * ['Psr\Log\LoggerInterface' => '?Psr\Log\LoggerInterface']
+     *
+     * @return array The required service types, optionally keyed by service names
+     */
+    public static function getSubscribedServices();
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/SimpleXMLElement.php b/civicrm/vendor/symfony/dependency-injection/SimpleXMLElement.php
deleted file mode 100644
index bb985ace4f48dba542b043563d8c2501b4eb3d6e..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/dependency-injection/SimpleXMLElement.php
+++ /dev/null
@@ -1,116 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\DependencyInjection;
-
-@trigger_error('The '.__NAMESPACE__.'\SimpleXMLElement class is deprecated since Symfony 2.5 and will be removed in 3.0.', E_USER_DEPRECATED);
-
-use Symfony\Component\Config\Util\XmlUtils;
-use Symfony\Component\ExpressionLanguage\Expression;
-
-/**
- * SimpleXMLElement class.
- *
- * @author Fabien Potencier <fabien@symfony.com>
- *
- * @deprecated since version 2.5, to be removed in 3.0.
- */
-class SimpleXMLElement extends \SimpleXMLElement
-{
-    /**
-     * Converts an attribute as a PHP type.
-     *
-     * @param string $name
-     *
-     * @return mixed
-     */
-    public function getAttributeAsPhp($name)
-    {
-        return self::phpize($this[$name]);
-    }
-
-    /**
-     * Returns arguments as valid PHP types.
-     *
-     * @param string $name
-     * @param bool   $lowercase
-     *
-     * @return mixed
-     */
-    public function getArgumentsAsPhp($name, $lowercase = true)
-    {
-        $arguments = array();
-        foreach ($this->$name as $arg) {
-            if (isset($arg['name'])) {
-                $arg['key'] = (string) $arg['name'];
-            }
-            $key = isset($arg['key']) ? (string) $arg['key'] : (!$arguments ? 0 : max(array_keys($arguments)) + 1);
-
-            // parameter keys are case insensitive
-            if ('parameter' == $name && $lowercase) {
-                $key = strtolower($key);
-            }
-
-            // this is used by DefinitionDecorator to overwrite a specific
-            // argument of the parent definition
-            if (isset($arg['index'])) {
-                $key = 'index_'.$arg['index'];
-            }
-
-            switch ($arg['type']) {
-                case 'service':
-                    $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
-                    if (isset($arg['on-invalid']) && 'ignore' == $arg['on-invalid']) {
-                        $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
-                    } elseif (isset($arg['on-invalid']) && 'null' == $arg['on-invalid']) {
-                        $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE;
-                    }
-
-                    if (isset($arg['strict'])) {
-                        $strict = self::phpize($arg['strict']);
-                    } else {
-                        $strict = true;
-                    }
-
-                    $arguments[$key] = new Reference((string) $arg['id'], $invalidBehavior, $strict);
-                    break;
-                case 'expression':
-                    $arguments[$key] = new Expression((string) $arg);
-                    break;
-                case 'collection':
-                    $arguments[$key] = $arg->getArgumentsAsPhp($name, false);
-                    break;
-                case 'string':
-                    $arguments[$key] = (string) $arg;
-                    break;
-                case 'constant':
-                    $arguments[$key] = \constant((string) $arg);
-                    break;
-                default:
-                    $arguments[$key] = self::phpize($arg);
-            }
-        }
-
-        return $arguments;
-    }
-
-    /**
-     * Converts an xml value to a PHP type.
-     *
-     * @param mixed $value
-     *
-     * @return mixed
-     */
-    public static function phpize($value)
-    {
-        return XmlUtils::phpize($value);
-    }
-}
diff --git a/civicrm/vendor/symfony/dependency-injection/TypedReference.php b/civicrm/vendor/symfony/dependency-injection/TypedReference.php
new file mode 100644
index 0000000000000000000000000000000000000000..aad78e806b6b61448c85b1036f3c3292df95ed9a
--- /dev/null
+++ b/civicrm/vendor/symfony/dependency-injection/TypedReference.php
@@ -0,0 +1,51 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection;
+
+/**
+ * Represents a PHP type-hinted service reference.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class TypedReference extends Reference
+{
+    private $type;
+    private $requiringClass;
+
+    /**
+     * @param string $id              The service identifier
+     * @param string $type            The PHP type of the identified service
+     * @param string $requiringClass  The class of the service that requires the referenced type
+     * @param int    $invalidBehavior The behavior when the service does not exist
+     */
+    public function __construct($id, $type, $requiringClass = '', $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE)
+    {
+        parent::__construct($id, $invalidBehavior);
+        $this->type = $type;
+        $this->requiringClass = $requiringClass;
+    }
+
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    public function getRequiringClass()
+    {
+        return $this->requiringClass;
+    }
+
+    public function canBeAutoregistered()
+    {
+        return $this->requiringClass && (false !== $i = strpos($this->type, '\\')) && 0 === strncasecmp($this->type, $this->requiringClass, 1 + $i);
+    }
+}
diff --git a/civicrm/vendor/symfony/dependency-injection/composer.json b/civicrm/vendor/symfony/dependency-injection/composer.json
index 2506690ca6997dd80e73ee9ecadbe4d1beb7f78e..6eaa3fd089cff319e2bd610f52b80edfdb30d3c2 100644
--- a/civicrm/vendor/symfony/dependency-injection/composer.json
+++ b/civicrm/vendor/symfony/dependency-injection/composer.json
@@ -16,22 +16,30 @@
         }
     ],
     "require": {
-        "php": ">=5.3.9"
+        "php": "^5.5.9|>=7.0.8",
+        "psr/container": "^1.0"
     },
     "require-dev": {
-        "symfony/yaml": "~2.3.42|~2.7.14|~2.8.7|~3.0.7",
-        "symfony/config": "~2.2|~3.0.0",
-        "symfony/expression-language": "~2.6|~3.0.0"
-    },
-    "conflict": {
-        "symfony/expression-language": "<2.6"
+        "symfony/yaml": "~3.4|~4.0",
+        "symfony/config": "~3.3|~4.0",
+        "symfony/expression-language": "~2.8|~3.0|~4.0"
     },
     "suggest": {
         "symfony/yaml": "",
         "symfony/config": "",
+        "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required",
         "symfony/expression-language": "For using expressions in service container configuration",
         "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them"
     },
+    "conflict": {
+        "symfony/config": "<3.3.7",
+        "symfony/finder": "<3.3",
+        "symfony/proxy-manager-bridge": "<3.4",
+        "symfony/yaml": "<3.4"
+    },
+    "provide": {
+        "psr/container-implementation": "1.0"
+    },
     "autoload": {
         "psr-4": { "Symfony\\Component\\DependencyInjection\\": "" },
         "exclude-from-classmap": [
@@ -41,7 +49,7 @@
     "minimum-stability": "dev",
     "extra": {
         "branch-alias": {
-            "dev-master": "2.8-dev"
+            "dev-master": "3.4-dev"
         }
     }
 }
diff --git a/civicrm/vendor/symfony/event-dispatcher/CHANGELOG.md b/civicrm/vendor/symfony/event-dispatcher/CHANGELOG.md
index bb42ee19c04ca2961e01b8fb314d2e6e9dcb92a7..c6aa5389ac724812183ba3defea3e9466c2fca78 100644
--- a/civicrm/vendor/symfony/event-dispatcher/CHANGELOG.md
+++ b/civicrm/vendor/symfony/event-dispatcher/CHANGELOG.md
@@ -1,6 +1,25 @@
 CHANGELOG
 =========
 
+3.4.0
+-----
+
+  * Implementing `TraceableEventDispatcherInterface` without the `reset()` method has been deprecated.
+
+3.3.0
+-----
+
+  * The ContainerAwareEventDispatcher class has been deprecated. Use EventDispatcher with closure factories instead.
+
+3.0.0
+-----
+
+  * The method `getListenerPriority($eventName, $listener)` has been added to the
+    `EventDispatcherInterface`.
+  * The methods `Event::setDispatcher()`, `Event::getDispatcher()`, `Event::setName()`
+    and `Event::getName()` have been removed.
+    The event dispatcher and the event name are passed to the listener call.
+
 2.5.0
 -----
 
diff --git a/civicrm/vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php b/civicrm/vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php
index 4dcede7e7381b5066ad7837618968f45f25a04e2..8f53a9d06a53cb07e29dbaf969c80dcd8779e065 100644
--- a/civicrm/vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php
+++ b/civicrm/vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php
@@ -11,6 +11,7 @@
 
 namespace Symfony\Component\EventDispatcher;
 
+use PHPUnit\Framework\MockObject\MockObject;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -20,6 +21,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  * @author Fabien Potencier <fabien@symfony.com>
  * @author Bernhard Schussek <bschussek@gmail.com>
  * @author Jordan Alliot <jordan.alliot@gmail.com>
+ *
+ * @deprecated since 3.3, to be removed in 4.0. Use EventDispatcher with closure factories instead.
  */
 class ContainerAwareEventDispatcher extends EventDispatcher
 {
@@ -28,16 +31,24 @@ class ContainerAwareEventDispatcher extends EventDispatcher
     /**
      * The service IDs of the event listeners and subscribers.
      */
-    private $listenerIds = array();
+    private $listenerIds = [];
 
     /**
      * The services registered as listeners.
      */
-    private $listeners = array();
+    private $listeners = [];
 
     public function __construct(ContainerInterface $container)
     {
         $this->container = $container;
+
+        $class = static::class;
+        if ($this instanceof \PHPUnit_Framework_MockObject_MockObject || $this instanceof MockObject || $this instanceof \Prophecy\Doubler\DoubleInterface) {
+            $class = get_parent_class($class);
+        }
+        if (__CLASS__ !== $class) {
+            @trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use EventDispatcher with closure factories instead.', __CLASS__), E_USER_DEPRECATED);
+        }
     }
 
     /**
@@ -54,11 +65,13 @@ class ContainerAwareEventDispatcher extends EventDispatcher
      */
     public function addListenerService($eventName, $callback, $priority = 0)
     {
+        @trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use EventDispatcher with closure factories instead.', __CLASS__), E_USER_DEPRECATED);
+
         if (!\is_array($callback) || 2 !== \count($callback)) {
-            throw new \InvalidArgumentException('Expected an array("service", "method") argument');
+            throw new \InvalidArgumentException('Expected an ["service", "method"] argument.');
         }
 
-        $this->listenerIds[$eventName][] = array($callback[0], $callback[1], $priority);
+        $this->listenerIds[$eventName][] = [$callback[0], $callback[1], $priority];
     }
 
     public function removeListener($eventName, $listener)
@@ -66,10 +79,9 @@ class ContainerAwareEventDispatcher extends EventDispatcher
         $this->lazyLoad($eventName);
 
         if (isset($this->listenerIds[$eventName])) {
-            foreach ($this->listenerIds[$eventName] as $i => $args) {
-                list($serviceId, $method) = $args;
+            foreach ($this->listenerIds[$eventName] as $i => list($serviceId, $method)) {
                 $key = $serviceId.'.'.$method;
-                if (isset($this->listeners[$eventName][$key]) && $listener === array($this->listeners[$eventName][$key], $method)) {
+                if (isset($this->listeners[$eventName][$key]) && $listener === [$this->listeners[$eventName][$key], $method]) {
                     unset($this->listeners[$eventName][$key]);
                     if (empty($this->listeners[$eventName])) {
                         unset($this->listeners[$eventName]);
@@ -135,14 +147,16 @@ class ContainerAwareEventDispatcher extends EventDispatcher
      */
     public function addSubscriberService($serviceId, $class)
     {
+        @trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use EventDispatcher with closure factories instead.', __CLASS__), E_USER_DEPRECATED);
+
         foreach ($class::getSubscribedEvents() as $eventName => $params) {
             if (\is_string($params)) {
-                $this->listenerIds[$eventName][] = array($serviceId, $params, 0);
+                $this->listenerIds[$eventName][] = [$serviceId, $params, 0];
             } elseif (\is_string($params[0])) {
-                $this->listenerIds[$eventName][] = array($serviceId, $params[0], isset($params[1]) ? $params[1] : 0);
+                $this->listenerIds[$eventName][] = [$serviceId, $params[0], isset($params[1]) ? $params[1] : 0];
             } else {
                 foreach ($params as $listener) {
-                    $this->listenerIds[$eventName][] = array($serviceId, $listener[0], isset($listener[1]) ? $listener[1] : 0);
+                    $this->listenerIds[$eventName][] = [$serviceId, $listener[0], isset($listener[1]) ? $listener[1] : 0];
                 }
             }
         }
@@ -150,6 +164,8 @@ class ContainerAwareEventDispatcher extends EventDispatcher
 
     public function getContainer()
     {
+        @trigger_error('The '.__METHOD__.'() method is deprecated since Symfony 3.3 as its class will be removed in 4.0. Inject the container or the services you need in your listeners/subscribers instead.', E_USER_DEPRECATED);
+
         return $this->container;
     }
 
@@ -164,16 +180,15 @@ class ContainerAwareEventDispatcher extends EventDispatcher
     protected function lazyLoad($eventName)
     {
         if (isset($this->listenerIds[$eventName])) {
-            foreach ($this->listenerIds[$eventName] as $args) {
-                list($serviceId, $method, $priority) = $args;
+            foreach ($this->listenerIds[$eventName] as list($serviceId, $method, $priority)) {
                 $listener = $this->container->get($serviceId);
 
                 $key = $serviceId.'.'.$method;
                 if (!isset($this->listeners[$eventName][$key])) {
-                    $this->addListener($eventName, array($listener, $method), $priority);
+                    $this->addListener($eventName, [$listener, $method], $priority);
                 } elseif ($this->listeners[$eventName][$key] !== $listener) {
-                    parent::removeListener($eventName, array($this->listeners[$eventName][$key], $method));
-                    $this->addListener($eventName, array($listener, $method), $priority);
+                    parent::removeListener($eventName, [$this->listeners[$eventName][$key], $method]);
+                    $this->addListener($eventName, [$listener, $method], $priority);
                 }
 
                 $this->listeners[$eventName][$key] = $listener;
diff --git a/civicrm/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php b/civicrm/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php
index 53d7c5d51a8f6d6127e5e06bed2ebf48bd0bcf42..017459723d92d0bd5c9f317f48aa9b4020460b72 100644
--- a/civicrm/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php
+++ b/civicrm/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php
@@ -29,7 +29,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
     protected $logger;
     protected $stopwatch;
 
-    private $called;
+    private $callStack;
     private $dispatcher;
     private $wrappedListeners;
 
@@ -38,8 +38,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
         $this->dispatcher = $dispatcher;
         $this->stopwatch = $stopwatch;
         $this->logger = $logger;
-        $this->called = array();
-        $this->wrappedListeners = array();
+        $this->wrappedListeners = [];
     }
 
     /**
@@ -97,8 +96,14 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
      */
     public function getListenerPriority($eventName, $listener)
     {
-        if (!method_exists($this->dispatcher, 'getListenerPriority')) {
-            return 0;
+        // we might have wrapped listeners for the event (if called while dispatching)
+        // in that case get the priority by wrapper
+        if (isset($this->wrappedListeners[$eventName])) {
+            foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) {
+                if ($wrappedListener->getWrappedListener() === $listener) {
+                    return $this->dispatcher->getListenerPriority($eventName, $wrappedListener);
+                }
+            }
         }
 
         return $this->dispatcher->getListenerPriority($eventName, $listener);
@@ -117,6 +122,10 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
      */
     public function dispatch($eventName, Event $event = null)
     {
+        if (null === $this->callStack) {
+            $this->callStack = new \SplObjectStorage();
+        }
+
         if (null === $event) {
             $event = new Event();
         }
@@ -126,19 +135,24 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
         }
 
         $this->preProcess($eventName);
-        $this->preDispatch($eventName, $event);
-
-        $e = $this->stopwatch->start($eventName, 'section');
-
-        $this->dispatcher->dispatch($eventName, $event);
-
-        if ($e->isStarted()) {
-            $e->stop();
+        try {
+            $this->preDispatch($eventName, $event);
+            try {
+                $e = $this->stopwatch->start($eventName, 'section');
+                try {
+                    $this->dispatcher->dispatch($eventName, $event);
+                } finally {
+                    if ($e->isStarted()) {
+                        $e->stop();
+                    }
+                }
+            } finally {
+                $this->postDispatch($eventName, $event);
+            }
+        } finally {
+            $this->postProcess($eventName);
         }
 
-        $this->postDispatch($eventName, $event);
-        $this->postProcess($eventName);
-
         return $event;
     }
 
@@ -147,12 +161,15 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
      */
     public function getCalledListeners()
     {
-        $called = array();
-        foreach ($this->called as $eventName => $listeners) {
-            foreach ($listeners as $listener) {
-                $info = $this->getListenerInfo($listener->getWrappedListener(), $eventName);
-                $called[$eventName.'.'.$info['pretty']] = $info;
-            }
+        if (null === $this->callStack) {
+            return [];
+        }
+
+        $called = [];
+        foreach ($this->callStack as $listener) {
+            list($eventName) = $this->callStack->getInfo();
+
+            $called[] = $listener->getInfo($eventName);
         }
 
         return $called;
@@ -167,39 +184,43 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
             $allListeners = $this->getListeners();
         } catch (\Exception $e) {
             if (null !== $this->logger) {
-                $this->logger->info('An exception was thrown while getting the uncalled listeners.', array('exception' => $e));
+                $this->logger->info('An exception was thrown while getting the uncalled listeners.', ['exception' => $e]);
             }
 
             // unable to retrieve the uncalled listeners
-            return array();
+            return [];
+        }
+
+        $calledListeners = [];
+
+        if (null !== $this->callStack) {
+            foreach ($this->callStack as $calledListener) {
+                $calledListeners[] = $calledListener->getWrappedListener();
+            }
         }
 
-        $notCalled = array();
+        $notCalled = [];
         foreach ($allListeners as $eventName => $listeners) {
             foreach ($listeners as $listener) {
-                $called = false;
-                if (isset($this->called[$eventName])) {
-                    foreach ($this->called[$eventName] as $l) {
-                        if ($l->getWrappedListener() === $listener) {
-                            $called = true;
-
-                            break;
-                        }
+                if (!\in_array($listener, $calledListeners, true)) {
+                    if (!$listener instanceof WrappedListener) {
+                        $listener = new WrappedListener($listener, null, $this->stopwatch, $this);
                     }
-                }
-
-                if (!$called) {
-                    $info = $this->getListenerInfo($listener, $eventName);
-                    $notCalled[$eventName.'.'.$info['pretty']] = $info;
+                    $notCalled[] = $listener->getInfo($eventName);
                 }
             }
         }
 
-        uasort($notCalled, array($this, 'sortListenersByPriority'));
+        uasort($notCalled, [$this, 'sortNotCalledListeners']);
 
         return $notCalled;
     }
 
+    public function reset()
+    {
+        $this->callStack = null;
+    }
+
     /**
      * Proxies all method calls to the original event dispatcher.
      *
@@ -210,7 +231,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
      */
     public function __call($method, $arguments)
     {
-        return \call_user_func_array(array($this->dispatcher, $method), $arguments);
+        return \call_user_func_array([$this->dispatcher, $method], $arguments);
     }
 
     /**
@@ -236,12 +257,12 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
     private function preProcess($eventName)
     {
         foreach ($this->dispatcher->getListeners($eventName) as $listener) {
-            $info = $this->getListenerInfo($listener, $eventName);
-            $name = isset($info['class']) ? $info['class'] : $info['type'];
-            $wrappedListener = new WrappedListener($listener, $name, $this->stopwatch, $this);
+            $priority = $this->getListenerPriority($eventName, $listener);
+            $wrappedListener = new WrappedListener($listener instanceof WrappedListener ? $listener->getWrappedListener() : $listener, null, $this->stopwatch, $this);
             $this->wrappedListeners[$eventName][] = $wrappedListener;
             $this->dispatcher->removeListener($eventName, $listener);
-            $this->dispatcher->addListener($eventName, $wrappedListener, $info['priority']);
+            $this->dispatcher->addListener($eventName, $wrappedListener, $priority);
+            $this->callStack->attach($wrappedListener, [$eventName]);
         }
     }
 
@@ -258,26 +279,25 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
             $this->dispatcher->removeListener($eventName, $listener);
             $this->dispatcher->addListener($eventName, $listener->getWrappedListener(), $priority);
 
-            $info = $this->getListenerInfo($listener->getWrappedListener(), $eventName);
+            if (null !== $this->logger) {
+                $context = ['event' => $eventName, 'listener' => $listener->getPretty()];
+            }
+
             if ($listener->wasCalled()) {
                 if (null !== $this->logger) {
-                    $this->logger->debug(sprintf('Notified event "%s" to listener "%s".', $eventName, $info['pretty']));
-                }
-
-                if (!isset($this->called[$eventName])) {
-                    $this->called[$eventName] = new \SplObjectStorage();
+                    $this->logger->debug('Notified event "{event}" to listener "{listener}".', $context);
                 }
-
-                $this->called[$eventName]->attach($listener);
+            } else {
+                $this->callStack->detach($listener);
             }
 
             if (null !== $this->logger && $skipped) {
-                $this->logger->debug(sprintf('Listener "%s" was not called for event "%s".', $info['pretty'], $eventName));
+                $this->logger->debug('Listener "{listener}" was not called for event "{event}".', $context);
             }
 
             if ($listener->stoppedPropagation()) {
                 if (null !== $this->logger) {
-                    $this->logger->debug(sprintf('Listener "%s" stopped propagation of the event "%s".', $info['pretty'], $eventName));
+                    $this->logger->debug('Listener "{listener}" stopped propagation of the event "{event}".', $context);
                 }
 
                 $skipped = true;
@@ -285,75 +305,12 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
         }
     }
 
-    /**
-     * Returns information about the listener.
-     *
-     * @param object $listener  The listener
-     * @param string $eventName The event name
-     *
-     * @return array Information about the listener
-     */
-    private function getListenerInfo($listener, $eventName)
+    private function sortNotCalledListeners(array $a, array $b)
     {
-        $info = array(
-            'event' => $eventName,
-            'priority' => $this->getListenerPriority($eventName, $listener),
-        );
-
-        // unwrap for correct listener info
-        if ($listener instanceof WrappedListener) {
-            $listener = $listener->getWrappedListener();
+        if (0 !== $cmp = strcmp($a['event'], $b['event'])) {
+            return $cmp;
         }
 
-        if ($listener instanceof \Closure) {
-            $info += array(
-                'type' => 'Closure',
-                'pretty' => 'closure',
-            );
-        } elseif (\is_string($listener)) {
-            try {
-                $r = new \ReflectionFunction($listener);
-                $file = $r->getFileName();
-                $line = $r->getStartLine();
-            } catch (\ReflectionException $e) {
-                $file = null;
-                $line = null;
-            }
-            $info += array(
-                'type' => 'Function',
-                'function' => $listener,
-                'file' => $file,
-                'line' => $line,
-                'pretty' => $listener,
-            );
-        } elseif (\is_array($listener) || (\is_object($listener) && \is_callable($listener))) {
-            if (!\is_array($listener)) {
-                $listener = array($listener, '__invoke');
-            }
-            $class = \is_object($listener[0]) ? \get_class($listener[0]) : $listener[0];
-            try {
-                $r = new \ReflectionMethod($class, $listener[1]);
-                $file = $r->getFileName();
-                $line = $r->getStartLine();
-            } catch (\ReflectionException $e) {
-                $file = null;
-                $line = null;
-            }
-            $info += array(
-                'type' => 'Method',
-                'class' => $class,
-                'method' => $listener[1],
-                'file' => $file,
-                'line' => $line,
-                'pretty' => $class.'::'.$listener[1],
-            );
-        }
-
-        return $info;
-    }
-
-    private function sortListenersByPriority($a, $b)
-    {
         if (\is_int($a['priority']) && !\is_int($b['priority'])) {
             return 1;
         }
diff --git a/civicrm/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php b/civicrm/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php
index 5483e815068c4388fa90ce6b6dd14f547ca52d4c..f0212753be5918bce203b5cc1ab4b159e7869d41 100644
--- a/civicrm/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php
+++ b/civicrm/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php
@@ -15,6 +15,8 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
 /**
  * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @method reset() Resets the trace.
  */
 interface TraceableEventDispatcherInterface extends EventDispatcherInterface
 {
diff --git a/civicrm/vendor/symfony/event-dispatcher/Debug/WrappedListener.php b/civicrm/vendor/symfony/event-dispatcher/Debug/WrappedListener.php
index 1552af02d8ed608dbff2c7af8255aa0f307ceb70..de2b850953c3d2cd12d8fa9d489a163bd630a9fe 100644
--- a/civicrm/vendor/symfony/event-dispatcher/Debug/WrappedListener.php
+++ b/civicrm/vendor/symfony/event-dispatcher/Debug/WrappedListener.php
@@ -14,6 +14,7 @@ namespace Symfony\Component\EventDispatcher\Debug;
 use Symfony\Component\EventDispatcher\Event;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 use Symfony\Component\Stopwatch\Stopwatch;
+use Symfony\Component\VarDumper\Caster\ClassStub;
 
 /**
  * @author Fabien Potencier <fabien@symfony.com>
@@ -26,15 +27,46 @@ class WrappedListener
     private $stoppedPropagation;
     private $stopwatch;
     private $dispatcher;
+    private $pretty;
+    private $stub;
+    private $priority;
+    private static $hasClassStub;
 
     public function __construct($listener, $name, Stopwatch $stopwatch, EventDispatcherInterface $dispatcher = null)
     {
         $this->listener = $listener;
-        $this->name = $name;
         $this->stopwatch = $stopwatch;
         $this->dispatcher = $dispatcher;
         $this->called = false;
         $this->stoppedPropagation = false;
+
+        if (\is_array($listener)) {
+            $this->name = \is_object($listener[0]) ? \get_class($listener[0]) : $listener[0];
+            $this->pretty = $this->name.'::'.$listener[1];
+        } elseif ($listener instanceof \Closure) {
+            $r = new \ReflectionFunction($listener);
+            if (false !== strpos($r->name, '{closure}')) {
+                $this->pretty = $this->name = 'closure';
+            } elseif ($class = $r->getClosureScopeClass()) {
+                $this->name = $class->name;
+                $this->pretty = $this->name.'::'.$r->name;
+            } else {
+                $this->pretty = $this->name = $r->name;
+            }
+        } elseif (\is_string($listener)) {
+            $this->pretty = $this->name = $listener;
+        } else {
+            $this->name = \get_class($listener);
+            $this->pretty = $this->name.'::__invoke';
+        }
+
+        if (null !== $name) {
+            $this->name = $name;
+        }
+
+        if (null === self::$hasClassStub) {
+            self::$hasClassStub = class_exists(ClassStub::class);
+        }
     }
 
     public function getWrappedListener()
@@ -52,13 +84,35 @@ class WrappedListener
         return $this->stoppedPropagation;
     }
 
+    public function getPretty()
+    {
+        return $this->pretty;
+    }
+
+    public function getInfo($eventName)
+    {
+        if (null === $this->stub) {
+            $this->stub = self::$hasClassStub ? new ClassStub($this->pretty.'()', $this->listener) : $this->pretty.'()';
+        }
+
+        return [
+            'event' => $eventName,
+            'priority' => null !== $this->priority ? $this->priority : (null !== $this->dispatcher ? $this->dispatcher->getListenerPriority($eventName, $this->listener) : null),
+            'pretty' => $this->pretty,
+            'stub' => $this->stub,
+        ];
+    }
+
     public function __invoke(Event $event, $eventName, EventDispatcherInterface $dispatcher)
     {
+        $dispatcher = $this->dispatcher ?: $dispatcher;
+
         $this->called = true;
+        $this->priority = $dispatcher->getListenerPriority($eventName, $this->listener);
 
         $e = $this->stopwatch->start($this->name, 'event_listener');
 
-        \call_user_func($this->listener, $event, $eventName, $this->dispatcher ?: $dispatcher);
+        \call_user_func($this->listener, $event, $eventName, $dispatcher);
 
         if ($e->isStarted()) {
             $e->stop();
diff --git a/civicrm/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php b/civicrm/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php
index 5a94ae82b3bd4342a5394b56cce608e1bb87fb65..2951c1ee45aa85c4c08b8cb89ef5955969364a29 100644
--- a/civicrm/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php
+++ b/civicrm/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php
@@ -11,8 +11,13 @@
 
 namespace Symfony\Component\EventDispatcher\DependencyInjection;
 
+use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
  * Compiler pass to register tagged services for an event dispatcher.
@@ -23,6 +28,9 @@ class RegisterListenersPass implements CompilerPassInterface
     protected $listenerTag;
     protected $subscriberTag;
 
+    private $hotPathEvents = [];
+    private $hotPathTagName;
+
     /**
      * @param string $dispatcherService Service name of the event dispatcher in processed container
      * @param string $listenerTag       Tag name used for listener
@@ -35,6 +43,14 @@ class RegisterListenersPass implements CompilerPassInterface
         $this->subscriberTag = $subscriberTag;
     }
 
+    public function setHotPathEvents(array $hotPathEvents, $tagName = 'container.hot_path')
+    {
+        $this->hotPathEvents = array_flip($hotPathEvents);
+        $this->hotPathTagName = $tagName;
+
+        return $this;
+    }
+
     public function process(ContainerBuilder $container)
     {
         if (!$container->hasDefinition($this->dispatcherService) && !$container->hasAlias($this->dispatcherService)) {
@@ -43,58 +59,79 @@ class RegisterListenersPass implements CompilerPassInterface
 
         $definition = $container->findDefinition($this->dispatcherService);
 
-        foreach ($container->findTaggedServiceIds($this->listenerTag) as $id => $events) {
-            $def = $container->getDefinition($id);
-            if (!$def->isPublic()) {
-                throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event listeners are lazy-loaded.', $id));
-            }
-
-            if ($def->isAbstract()) {
-                throw new \InvalidArgumentException(sprintf('The service "%s" must not be abstract as event listeners are lazy-loaded.', $id));
-            }
-
+        foreach ($container->findTaggedServiceIds($this->listenerTag, true) as $id => $events) {
             foreach ($events as $event) {
                 $priority = isset($event['priority']) ? $event['priority'] : 0;
 
                 if (!isset($event['event'])) {
-                    throw new \InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "%s" tags.', $id, $this->listenerTag));
+                    throw new InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "%s" tags.', $id, $this->listenerTag));
                 }
 
                 if (!isset($event['method'])) {
-                    $event['method'] = 'on'.preg_replace_callback(array(
+                    $event['method'] = 'on'.preg_replace_callback([
                         '/(?<=\b)[a-z]/i',
                         '/[^a-z0-9]/i',
-                    ), function ($matches) { return strtoupper($matches[0]); }, $event['event']);
+                    ], function ($matches) { return strtoupper($matches[0]); }, $event['event']);
                     $event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']);
                 }
 
-                $definition->addMethodCall('addListenerService', array($event['event'], array($id, $event['method']), $priority));
+                $definition->addMethodCall('addListener', [$event['event'], [new ServiceClosureArgument(new Reference($id)), $event['method']], $priority]);
+
+                if (isset($this->hotPathEvents[$event['event']])) {
+                    $container->getDefinition($id)->addTag($this->hotPathTagName);
+                }
             }
         }
 
-        foreach ($container->findTaggedServiceIds($this->subscriberTag) as $id => $attributes) {
+        $extractingDispatcher = new ExtractingEventDispatcher();
+
+        foreach ($container->findTaggedServiceIds($this->subscriberTag, true) as $id => $attributes) {
             $def = $container->getDefinition($id);
-            if (!$def->isPublic()) {
-                throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event subscribers are lazy-loaded.', $id));
-            }
 
-            if ($def->isAbstract()) {
-                throw new \InvalidArgumentException(sprintf('The service "%s" must not be abstract as event subscribers are lazy-loaded.', $id));
+            // We must assume that the class value has been correctly filled, even if the service is created by a factory
+            $class = $def->getClass();
+
+            if (!$r = $container->getReflectionClass($class)) {
+                throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
+            }
+            if (!$r->isSubclassOf(EventSubscriberInterface::class)) {
+                throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, EventSubscriberInterface::class));
             }
+            $class = $r->name;
 
-            // We must assume that the class value has been correctly filled, even if the service is created by a factory
-            $class = $container->getParameterBag()->resolveValue($def->getClass());
-            $interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface';
+            ExtractingEventDispatcher::$subscriber = $class;
+            $extractingDispatcher->addSubscriber($extractingDispatcher);
+            foreach ($extractingDispatcher->listeners as $args) {
+                $args[1] = [new ServiceClosureArgument(new Reference($id)), $args[1]];
+                $definition->addMethodCall('addListener', $args);
 
-            if (!is_subclass_of($class, $interface)) {
-                if (!class_exists($class, false)) {
-                    throw new \InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
+                if (isset($this->hotPathEvents[$args[0]])) {
+                    $container->getDefinition($id)->addTag($this->hotPathTagName);
                 }
-
-                throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface));
             }
-
-            $definition->addMethodCall('addSubscriberService', array($id, $class));
+            $extractingDispatcher->listeners = [];
         }
     }
 }
+
+/**
+ * @internal
+ */
+class ExtractingEventDispatcher extends EventDispatcher implements EventSubscriberInterface
+{
+    public $listeners = [];
+
+    public static $subscriber;
+
+    public function addListener($eventName, $listener, $priority = 0)
+    {
+        $this->listeners[] = [$eventName, $listener[1], $priority];
+    }
+
+    public static function getSubscribedEvents()
+    {
+        $callback = [self::$subscriber, 'getSubscribedEvents'];
+
+        return $callback();
+    }
+}
diff --git a/civicrm/vendor/symfony/event-dispatcher/Event.php b/civicrm/vendor/symfony/event-dispatcher/Event.php
index 320919ae2faae6022f2a67be1351808332312840..9c56b2f55b8a70c8986de919fd3a8311f9f05dd7 100644
--- a/civicrm/vendor/symfony/event-dispatcher/Event.php
+++ b/civicrm/vendor/symfony/event-dispatcher/Event.php
@@ -32,16 +32,6 @@ class Event
      */
     private $propagationStopped = false;
 
-    /**
-     * @var EventDispatcherInterface Dispatcher that dispatched this event
-     */
-    private $dispatcher;
-
-    /**
-     * @var string This event's name
-     */
-    private $name;
-
     /**
      * Returns whether further event listeners should be triggered.
      *
@@ -65,56 +55,4 @@ class Event
     {
         $this->propagationStopped = true;
     }
-
-    /**
-     * Stores the EventDispatcher that dispatches this Event.
-     *
-     * @param EventDispatcherInterface $dispatcher
-     *
-     * @deprecated since version 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call.
-     */
-    public function setDispatcher(EventDispatcherInterface $dispatcher)
-    {
-        $this->dispatcher = $dispatcher;
-    }
-
-    /**
-     * Returns the EventDispatcher that dispatches this Event.
-     *
-     * @return EventDispatcherInterface
-     *
-     * @deprecated since version 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call.
-     */
-    public function getDispatcher()
-    {
-        @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.4 and will be removed in 3.0. The event dispatcher instance can be received in the listener call instead.', E_USER_DEPRECATED);
-
-        return $this->dispatcher;
-    }
-
-    /**
-     * Gets the event's name.
-     *
-     * @return string
-     *
-     * @deprecated since version 2.4, to be removed in 3.0. The event name is passed to the listener call.
-     */
-    public function getName()
-    {
-        @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.4 and will be removed in 3.0. The event name can be received in the listener call instead.', E_USER_DEPRECATED);
-
-        return $this->name;
-    }
-
-    /**
-     * Sets the event's name property.
-     *
-     * @param string $name The event name
-     *
-     * @deprecated since version 2.4, to be removed in 3.0. The event name is passed to the listener call.
-     */
-    public function setName($name)
-    {
-        $this->name = $name;
-    }
 }
diff --git a/civicrm/vendor/symfony/event-dispatcher/EventDispatcher.php b/civicrm/vendor/symfony/event-dispatcher/EventDispatcher.php
index b41b98e554e0d9de70cc54cc6df10942f06c20ad..207790f06b0f9742c553fdd4caf11fd40b5a98db 100644
--- a/civicrm/vendor/symfony/event-dispatcher/EventDispatcher.php
+++ b/civicrm/vendor/symfony/event-dispatcher/EventDispatcher.php
@@ -24,11 +24,12 @@ namespace Symfony\Component\EventDispatcher;
  * @author Fabien Potencier <fabien@symfony.com>
  * @author Jordi Boggiano <j.boggiano@seld.be>
  * @author Jordan Alliot <jordan.alliot@gmail.com>
+ * @author Nicolas Grekas <p@tchwork.com>
  */
 class EventDispatcher implements EventDispatcherInterface
 {
-    private $listeners = array();
-    private $sorted = array();
+    private $listeners = [];
+    private $sorted = [];
 
     /**
      * {@inheritdoc}
@@ -39,9 +40,6 @@ class EventDispatcher implements EventDispatcherInterface
             $event = new Event();
         }
 
-        $event->setDispatcher($this);
-        $event->setName($eventName);
-
         if ($listeners = $this->getListeners($eventName)) {
             $this->doDispatch($listeners, $eventName, $event);
         }
@@ -55,8 +53,8 @@ class EventDispatcher implements EventDispatcherInterface
     public function getListeners($eventName = null)
     {
         if (null !== $eventName) {
-            if (!isset($this->listeners[$eventName])) {
-                return array();
+            if (empty($this->listeners[$eventName])) {
+                return [];
             }
 
             if (!isset($this->sorted[$eventName])) {
@@ -76,26 +74,31 @@ class EventDispatcher implements EventDispatcherInterface
     }
 
     /**
-     * Gets the listener priority for a specific event.
-     *
-     * Returns null if the event or the listener does not exist.
-     *
-     * @param string   $eventName The name of the event
-     * @param callable $listener  The listener
-     *
-     * @return int|null The event listener priority
+     * {@inheritdoc}
      */
     public function getListenerPriority($eventName, $listener)
     {
-        if (!isset($this->listeners[$eventName])) {
-            return;
+        if (empty($this->listeners[$eventName])) {
+            return null;
+        }
+
+        if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) {
+            $listener[0] = $listener[0]();
         }
 
         foreach ($this->listeners[$eventName] as $priority => $listeners) {
-            if (false !== \in_array($listener, $listeners, true)) {
-                return $priority;
+            foreach ($listeners as $k => $v) {
+                if ($v !== $listener && \is_array($v) && isset($v[0]) && $v[0] instanceof \Closure) {
+                    $v[0] = $v[0]();
+                    $this->listeners[$eventName][$priority][$k] = $v;
+                }
+                if ($v === $listener) {
+                    return $priority;
+                }
             }
         }
+
+        return null;
     }
 
     /**
@@ -103,7 +106,17 @@ class EventDispatcher implements EventDispatcherInterface
      */
     public function hasListeners($eventName = null)
     {
-        return (bool) $this->getListeners($eventName);
+        if (null !== $eventName) {
+            return !empty($this->listeners[$eventName]);
+        }
+
+        foreach ($this->listeners as $eventListeners) {
+            if ($eventListeners) {
+                return true;
+            }
+        }
+
+        return false;
     }
 
     /**
@@ -120,13 +133,30 @@ class EventDispatcher implements EventDispatcherInterface
      */
     public function removeListener($eventName, $listener)
     {
-        if (!isset($this->listeners[$eventName])) {
+        if (empty($this->listeners[$eventName])) {
             return;
         }
 
+        if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) {
+            $listener[0] = $listener[0]();
+        }
+
         foreach ($this->listeners[$eventName] as $priority => $listeners) {
-            if (false !== ($key = array_search($listener, $listeners, true))) {
-                unset($this->listeners[$eventName][$priority][$key], $this->sorted[$eventName]);
+            foreach ($listeners as $k => $v) {
+                if ($v !== $listener && \is_array($v) && isset($v[0]) && $v[0] instanceof \Closure) {
+                    $v[0] = $v[0]();
+                }
+                if ($v === $listener) {
+                    unset($listeners[$k], $this->sorted[$eventName]);
+                } else {
+                    $listeners[$k] = $v;
+                }
+            }
+
+            if ($listeners) {
+                $this->listeners[$eventName][$priority] = $listeners;
+            } else {
+                unset($this->listeners[$eventName][$priority]);
             }
         }
     }
@@ -138,12 +168,12 @@ class EventDispatcher implements EventDispatcherInterface
     {
         foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
             if (\is_string($params)) {
-                $this->addListener($eventName, array($subscriber, $params));
+                $this->addListener($eventName, [$subscriber, $params]);
             } elseif (\is_string($params[0])) {
-                $this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0);
+                $this->addListener($eventName, [$subscriber, $params[0]], isset($params[1]) ? $params[1] : 0);
             } else {
                 foreach ($params as $listener) {
-                    $this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0);
+                    $this->addListener($eventName, [$subscriber, $listener[0]], isset($listener[1]) ? $listener[1] : 0);
                 }
             }
         }
@@ -157,10 +187,10 @@ class EventDispatcher implements EventDispatcherInterface
         foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
             if (\is_array($params) && \is_array($params[0])) {
                 foreach ($params as $listener) {
-                    $this->removeListener($eventName, array($subscriber, $listener[0]));
+                    $this->removeListener($eventName, [$subscriber, $listener[0]]);
                 }
             } else {
-                $this->removeListener($eventName, array($subscriber, \is_string($params) ? $params : $params[0]));
+                $this->removeListener($eventName, [$subscriber, \is_string($params) ? $params : $params[0]]);
             }
         }
     }
@@ -193,6 +223,16 @@ class EventDispatcher implements EventDispatcherInterface
     private function sortListeners($eventName)
     {
         krsort($this->listeners[$eventName]);
-        $this->sorted[$eventName] = \call_user_func_array('array_merge', $this->listeners[$eventName]);
+        $this->sorted[$eventName] = [];
+
+        foreach ($this->listeners[$eventName] as $priority => $listeners) {
+            foreach ($listeners as $k => $listener) {
+                if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) {
+                    $listener[0] = $listener[0]();
+                    $this->listeners[$eventName][$priority][$k] = $listener;
+                }
+                $this->sorted[$eventName][] = $listener;
+            }
+        }
     }
 }
diff --git a/civicrm/vendor/symfony/event-dispatcher/EventDispatcherInterface.php b/civicrm/vendor/symfony/event-dispatcher/EventDispatcherInterface.php
index 60160a90e00ae8a1c02545f0d0209e0711cdfd36..bde753a12fca0583ff7fdb71f3905bb8c20a1bfd 100644
--- a/civicrm/vendor/symfony/event-dispatcher/EventDispatcherInterface.php
+++ b/civicrm/vendor/symfony/event-dispatcher/EventDispatcherInterface.php
@@ -23,11 +23,11 @@ interface EventDispatcherInterface
     /**
      * Dispatches an event to all registered listeners.
      *
-     * @param string $eventName The name of the event to dispatch. The name of
-     *                          the event is the name of the method that is
-     *                          invoked on listeners.
-     * @param Event  $event     The event to pass to the event handlers/listeners
-     *                          If not supplied, an empty Event instance is created
+     * @param string     $eventName The name of the event to dispatch. The name of
+     *                              the event is the name of the method that is
+     *                              invoked on listeners.
+     * @param Event|null $event     The event to pass to the event handlers/listeners
+     *                              If not supplied, an empty Event instance is created
      *
      * @return Event
      */
@@ -46,7 +46,7 @@ interface EventDispatcherInterface
     /**
      * Adds an event subscriber.
      *
-     * The subscriber is asked for all the events he is
+     * The subscriber is asked for all the events it is
      * interested in and added as a listener for these events.
      */
     public function addSubscriber(EventSubscriberInterface $subscriber);
@@ -64,16 +64,28 @@ interface EventDispatcherInterface
     /**
      * Gets the listeners of a specific event or all listeners sorted by descending priority.
      *
-     * @param string $eventName The name of the event
+     * @param string|null $eventName The name of the event
      *
      * @return array The event listeners for the specified event, or all event listeners by event name
      */
     public function getListeners($eventName = null);
 
+    /**
+     * Gets the listener priority for a specific event.
+     *
+     * Returns null if the event or the listener does not exist.
+     *
+     * @param string   $eventName The name of the event
+     * @param callable $listener  The listener
+     *
+     * @return int|null The event listener priority
+     */
+    public function getListenerPriority($eventName, $listener);
+
     /**
      * Checks whether an event has any registered listeners.
      *
-     * @param string $eventName The name of the event
+     * @param string|null $eventName The name of the event
      *
      * @return bool true if the specified event has any listeners, false otherwise
      */
diff --git a/civicrm/vendor/symfony/event-dispatcher/EventSubscriberInterface.php b/civicrm/vendor/symfony/event-dispatcher/EventSubscriberInterface.php
index 8af778919bab71042d545f5f225fde02fa81845d..824f21599c2560611fb1ff88398d475c908e9be8 100644
--- a/civicrm/vendor/symfony/event-dispatcher/EventSubscriberInterface.php
+++ b/civicrm/vendor/symfony/event-dispatcher/EventSubscriberInterface.php
@@ -12,7 +12,7 @@
 namespace Symfony\Component\EventDispatcher;
 
 /**
- * An EventSubscriber knows himself what events he is interested in.
+ * An EventSubscriber knows itself what events it is interested in.
  * If an EventSubscriber is added to an EventDispatcherInterface, the manager invokes
  * {@link getSubscribedEvents} and registers the subscriber as a listener for all
  * returned events.
@@ -36,9 +36,9 @@ interface EventSubscriberInterface
      *
      * For instance:
      *
-     *  * array('eventName' => 'methodName')
-     *  * array('eventName' => array('methodName', $priority))
-     *  * array('eventName' => array(array('methodName1', $priority), array('methodName2')))
+     *  * ['eventName' => 'methodName']
+     *  * ['eventName' => ['methodName', $priority]]
+     *  * ['eventName' => [['methodName1', $priority], ['methodName2']]]
      *
      * @return array The event names to listen to
      */
diff --git a/civicrm/vendor/symfony/event-dispatcher/GenericEvent.php b/civicrm/vendor/symfony/event-dispatcher/GenericEvent.php
index f0be7e18ff3c343b552cae3a70268275acd54955..f005e3a3db0762e55c1274b59b56d3cb0035e8c1 100644
--- a/civicrm/vendor/symfony/event-dispatcher/GenericEvent.php
+++ b/civicrm/vendor/symfony/event-dispatcher/GenericEvent.php
@@ -29,7 +29,7 @@ class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
      * @param mixed $subject   The subject of the event, usually an object or a callable
      * @param array $arguments Arguments to store in the event
      */
-    public function __construct($subject = null, array $arguments = array())
+    public function __construct($subject = null, array $arguments = [])
     {
         $this->subject = $subject;
         $this->arguments = $arguments;
@@ -95,7 +95,7 @@ class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
      *
      * @return $this
      */
-    public function setArguments(array $args = array())
+    public function setArguments(array $args = [])
     {
         $this->arguments = $args;
 
@@ -111,7 +111,7 @@ class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
      */
     public function hasArgument($key)
     {
-        return array_key_exists($key, $this->arguments);
+        return \array_key_exists($key, $this->arguments);
     }
 
     /**
diff --git a/civicrm/vendor/symfony/event-dispatcher/LICENSE b/civicrm/vendor/symfony/event-dispatcher/LICENSE
index 21d7fb9e2f29b50caca3a76f0647e94e2cc8ddc1..9e936ec0448b8549e5edf08e5ac5f01491a8bfc8 100644
--- a/civicrm/vendor/symfony/event-dispatcher/LICENSE
+++ b/civicrm/vendor/symfony/event-dispatcher/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2018 Fabien Potencier
+Copyright (c) 2004-2020 Fabien Potencier
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/civicrm/vendor/symfony/event-dispatcher/README.md b/civicrm/vendor/symfony/event-dispatcher/README.md
index 185c3fecf8fee623ac3b03005fd32ec302147bd4..e0d38eed017f8f46eac131d33e05ac2bdac4929b 100644
--- a/civicrm/vendor/symfony/event-dispatcher/README.md
+++ b/civicrm/vendor/symfony/event-dispatcher/README.md
@@ -8,7 +8,7 @@ them.
 Resources
 ---------
 
-  * [Documentation](https://symfony.com/doc/current/components/event_dispatcher/index.html)
+  * [Documentation](https://symfony.com/doc/current/components/event_dispatcher.html)
   * [Contributing](https://symfony.com/doc/current/contributing/index.html)
   * [Report issues](https://github.com/symfony/symfony/issues) and
     [send Pull Requests](https://github.com/symfony/symfony/pulls)
diff --git a/civicrm/vendor/symfony/event-dispatcher/composer.json b/civicrm/vendor/symfony/event-dispatcher/composer.json
index 14fc24ba22a0a5b30e647d9caebd2ec5466ee672..75b881b917cd9a7d6b3831c1c5d0cb0ae7d32a77 100644
--- a/civicrm/vendor/symfony/event-dispatcher/composer.json
+++ b/civicrm/vendor/symfony/event-dispatcher/composer.json
@@ -16,15 +16,18 @@
         }
     ],
     "require": {
-        "php": ">=5.3.9"
+        "php": "^5.5.9|>=7.0.8"
     },
     "require-dev": {
-        "symfony/dependency-injection": "~2.6|~3.0.0",
-        "symfony/expression-language": "~2.6|~3.0.0",
-        "symfony/config": "^2.0.5|~3.0.0",
-        "symfony/stopwatch": "~2.3|~3.0.0",
+        "symfony/dependency-injection": "~3.3|~4.0",
+        "symfony/expression-language": "~2.8|~3.0|~4.0",
+        "symfony/config": "~2.8|~3.0|~4.0",
+        "symfony/stopwatch": "~2.8|~3.0|~4.0",
         "psr/log": "~1.0"
     },
+    "conflict": {
+        "symfony/dependency-injection": "<3.3"
+    },
     "suggest": {
         "symfony/dependency-injection": "",
         "symfony/http-kernel": ""
@@ -38,7 +41,7 @@
     "minimum-stability": "dev",
     "extra": {
         "branch-alias": {
-            "dev-master": "2.8-dev"
+            "dev-master": "3.4-dev"
         }
     }
 }
diff --git a/civicrm/vendor/symfony/filesystem/CHANGELOG.md b/civicrm/vendor/symfony/filesystem/CHANGELOG.md
index aee6e804b0d0279d67f374affcf1d1852cafebdf..d01f5f45e1a7dbd03323e3ef5565cf049f0cbe1d 100644
--- a/civicrm/vendor/symfony/filesystem/CHANGELOG.md
+++ b/civicrm/vendor/symfony/filesystem/CHANGELOG.md
@@ -1,6 +1,26 @@
 CHANGELOG
 =========
 
+3.4.0
+-----
+
+ * support for passing relative paths to `Filesystem::makePathRelative()` is deprecated and will be removed in 4.0
+
+3.3.0
+-----
+
+ * added `appendToFile()` to append contents to existing files
+
+3.2.0
+-----
+
+ * added `readlink()` as a platform independent method to read links
+
+3.0.0
+-----
+
+ * removed `$mode` argument from `Filesystem::dumpFile()`
+
 2.8.0
 -----
 
diff --git a/civicrm/vendor/symfony/filesystem/Exception/IOExceptionInterface.php b/civicrm/vendor/symfony/filesystem/Exception/IOExceptionInterface.php
index c11965a4247ee4811f7ee38b78bc792f76acc08b..f9d4644a8727946c24276c9fa648d2f372bb48e9 100644
--- a/civicrm/vendor/symfony/filesystem/Exception/IOExceptionInterface.php
+++ b/civicrm/vendor/symfony/filesystem/Exception/IOExceptionInterface.php
@@ -21,7 +21,7 @@ interface IOExceptionInterface extends ExceptionInterface
     /**
      * Returns the associated path for the exception.
      *
-     * @return string The path
+     * @return string|null The path
      */
     public function getPath();
 }
diff --git a/civicrm/vendor/symfony/filesystem/Filesystem.php b/civicrm/vendor/symfony/filesystem/Filesystem.php
index df316205619fc69e0715f6b158b729c0aba8c2a9..a8701533cbd3866bda7f8eca15406cfafddf5f0e 100644
--- a/civicrm/vendor/symfony/filesystem/Filesystem.php
+++ b/civicrm/vendor/symfony/filesystem/Filesystem.php
@@ -52,13 +52,13 @@ class Filesystem
         }
 
         if ($doCopy) {
-            // https://bugs.php.net/bug.php?id=64634
+            // https://bugs.php.net/64634
             if (false === $source = @fopen($originFile, 'r')) {
                 throw new IOException(sprintf('Failed to copy "%s" to "%s" because source file could not be opened for reading.', $originFile, $targetFile), 0, null, $originFile);
             }
 
             // Stream context created to allow files overwrite when using FTP stream wrapper - disabled by default
-            if (false === $target = @fopen($targetFile, 'w', null, stream_context_create(array('ftp' => array('overwrite' => true))))) {
+            if (false === $target = @fopen($targetFile, 'w', null, stream_context_create(['ftp' => ['overwrite' => true]]))) {
                 throw new IOException(sprintf('Failed to copy "%s" to "%s" because target file could not be opened for writing.', $originFile, $targetFile), 0, null, $originFile);
             }
 
@@ -92,7 +92,7 @@ class Filesystem
      */
     public function mkdir($dirs, $mode = 0777)
     {
-        foreach ($this->toIterator($dirs) as $dir) {
+        foreach ($this->toIterable($dirs) as $dir) {
             if (is_dir($dir)) {
                 continue;
             }
@@ -101,9 +101,9 @@ class Filesystem
                 if (!is_dir($dir)) {
                     // The directory was not created by a concurrent process. Let's throw an exception with a developer friendly error message if we have one
                     if (self::$lastError) {
-                        throw new IOException(sprintf('Failed to create "%s": %s.', $dir, self::$lastError), 0, null, $dir);
+                        throw new IOException(sprintf('Failed to create "%s": '.self::$lastError, $dir), 0, null, $dir);
                     }
-                    throw new IOException(sprintf('Failed to create "%s"', $dir), 0, null, $dir);
+                    throw new IOException(sprintf('Failed to create "%s".', $dir), 0, null, $dir);
                 }
             }
         }
@@ -120,7 +120,7 @@ class Filesystem
     {
         $maxPathLength = PHP_MAXPATHLEN - 2;
 
-        foreach ($this->toIterator($files) as $file) {
+        foreach ($this->toIterable($files) as $file) {
             if (\strlen($file) > $maxPathLength) {
                 throw new IOException(sprintf('Could not check if file exist because path length exceeds %d characters.', $maxPathLength), 0, null, $file);
             }
@@ -137,14 +137,14 @@ class Filesystem
      * Sets access and modification time of file.
      *
      * @param string|iterable $files A filename, an array of files, or a \Traversable instance to create
-     * @param int             $time  The touch time as a Unix timestamp
-     * @param int             $atime The access time as a Unix timestamp
+     * @param int|null        $time  The touch time as a Unix timestamp, if not supplied the current system time is used
+     * @param int|null        $atime The access time as a Unix timestamp, if not supplied the current system time is used
      *
      * @throws IOException When touch fails
      */
     public function touch($files, $time = null, $atime = null)
     {
-        foreach ($this->toIterator($files) as $file) {
+        foreach ($this->toIterable($files) as $file) {
             $touch = $time ? @touch($file, $time, $atime) : @touch($file);
             if (true !== $touch) {
                 throw new IOException(sprintf('Failed to touch "%s".', $file), 0, null, $file);
@@ -164,23 +164,23 @@ class Filesystem
         if ($files instanceof \Traversable) {
             $files = iterator_to_array($files, false);
         } elseif (!\is_array($files)) {
-            $files = array($files);
+            $files = [$files];
         }
         $files = array_reverse($files);
         foreach ($files as $file) {
             if (is_link($file)) {
                 // See https://bugs.php.net/52176
                 if (!(self::box('unlink', $file) || '\\' !== \DIRECTORY_SEPARATOR || self::box('rmdir', $file)) && file_exists($file)) {
-                    throw new IOException(sprintf('Failed to remove symlink "%s": %s.', $file, self::$lastError));
+                    throw new IOException(sprintf('Failed to remove symlink "%s": '.self::$lastError, $file));
                 }
             } elseif (is_dir($file)) {
                 $this->remove(new \FilesystemIterator($file, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS));
 
                 if (!self::box('rmdir', $file) && file_exists($file)) {
-                    throw new IOException(sprintf('Failed to remove directory "%s": %s.', $file, self::$lastError));
+                    throw new IOException(sprintf('Failed to remove directory "%s": '.self::$lastError, $file));
                 }
             } elseif (!self::box('unlink', $file) && file_exists($file)) {
-                throw new IOException(sprintf('Failed to remove file "%s": %s.', $file, self::$lastError));
+                throw new IOException(sprintf('Failed to remove file "%s": '.self::$lastError, $file));
             }
         }
     }
@@ -193,11 +193,11 @@ class Filesystem
      * @param int             $umask     The mode mask (octal)
      * @param bool            $recursive Whether change the mod recursively or not
      *
-     * @throws IOException When the change fail
+     * @throws IOException When the change fails
      */
     public function chmod($files, $mode, $umask = 0000, $recursive = false)
     {
-        foreach ($this->toIterator($files) as $file) {
+        foreach ($this->toIterable($files) as $file) {
             if (true !== @chmod($file, $mode & ~$umask)) {
                 throw new IOException(sprintf('Failed to chmod file "%s".', $file), 0, null, $file);
             }
@@ -211,14 +211,14 @@ class Filesystem
      * Change the owner of an array of files or directories.
      *
      * @param string|iterable $files     A filename, an array of files, or a \Traversable instance to change owner
-     * @param string          $user      The new owner user name
+     * @param string|int      $user      A user name or number
      * @param bool            $recursive Whether change the owner recursively or not
      *
-     * @throws IOException When the change fail
+     * @throws IOException When the change fails
      */
     public function chown($files, $user, $recursive = false)
     {
-        foreach ($this->toIterator($files) as $file) {
+        foreach ($this->toIterable($files) as $file) {
             if ($recursive && is_dir($file) && !is_link($file)) {
                 $this->chown(new \FilesystemIterator($file), $user, true);
             }
@@ -238,14 +238,14 @@ class Filesystem
      * Change the group of an array of files or directories.
      *
      * @param string|iterable $files     A filename, an array of files, or a \Traversable instance to change group
-     * @param string          $group     The group name
+     * @param string|int      $group     A group name or number
      * @param bool            $recursive Whether change the group recursively or not
      *
-     * @throws IOException When the change fail
+     * @throws IOException When the change fails
      */
     public function chgrp($files, $group, $recursive = false)
     {
-        foreach ($this->toIterator($files) as $file) {
+        foreach ($this->toIterable($files) as $file) {
             if ($recursive && is_dir($file) && !is_link($file)) {
                 $this->chgrp(new \FilesystemIterator($file), $group, true);
             }
@@ -280,8 +280,8 @@ class Filesystem
 
         if (true !== @rename($origin, $target)) {
             if (is_dir($origin)) {
-                // See https://bugs.php.net/bug.php?id=54097 & http://php.net/manual/en/function.rename.php#113943
-                $this->mirror($origin, $target, null, array('override' => $overwrite, 'delete' => $overwrite));
+                // See https://bugs.php.net/54097 & https://php.net/rename#113943
+                $this->mirror($origin, $target, null, ['override' => $overwrite, 'delete' => $overwrite]);
                 $this->remove($origin);
 
                 return;
@@ -342,13 +342,97 @@ class Filesystem
         }
 
         if (!self::box('symlink', $originDir, $targetDir)) {
-            if (null !== self::$lastError) {
-                if ('\\' === \DIRECTORY_SEPARATOR && false !== strpos(self::$lastError, 'error code(1314)')) {
-                    throw new IOException('Unable to create symlink due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?', 0, null, $targetDir);
+            $this->linkException($originDir, $targetDir, 'symbolic');
+        }
+    }
+
+    /**
+     * Creates a hard link, or several hard links to a file.
+     *
+     * @param string          $originFile  The original file
+     * @param string|string[] $targetFiles The target file(s)
+     *
+     * @throws FileNotFoundException When original file is missing or not a file
+     * @throws IOException           When link fails, including if link already exists
+     */
+    public function hardlink($originFile, $targetFiles)
+    {
+        if (!$this->exists($originFile)) {
+            throw new FileNotFoundException(null, 0, null, $originFile);
+        }
+
+        if (!is_file($originFile)) {
+            throw new FileNotFoundException(sprintf('Origin file "%s" is not a file.', $originFile));
+        }
+
+        foreach ($this->toIterable($targetFiles) as $targetFile) {
+            if (is_file($targetFile)) {
+                if (fileinode($originFile) === fileinode($targetFile)) {
+                    continue;
                 }
+                $this->remove($targetFile);
+            }
+
+            if (!self::box('link', $originFile, $targetFile)) {
+                $this->linkException($originFile, $targetFile, 'hard');
+            }
+        }
+    }
+
+    /**
+     * @param string $origin
+     * @param string $target
+     * @param string $linkType Name of the link type, typically 'symbolic' or 'hard'
+     */
+    private function linkException($origin, $target, $linkType)
+    {
+        if (self::$lastError) {
+            if ('\\' === \DIRECTORY_SEPARATOR && false !== strpos(self::$lastError, 'error code(1314)')) {
+                throw new IOException(sprintf('Unable to create "%s" link due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?', $linkType), 0, null, $target);
+            }
+        }
+        throw new IOException(sprintf('Failed to create "%s" link from "%s" to "%s".', $linkType, $origin, $target), 0, null, $target);
+    }
+
+    /**
+     * Resolves links in paths.
+     *
+     * With $canonicalize = false (default)
+     *      - if $path does not exist or is not a link, returns null
+     *      - if $path is a link, returns the next direct target of the link without considering the existence of the target
+     *
+     * With $canonicalize = true
+     *      - if $path does not exist, returns null
+     *      - if $path exists, returns its absolute fully resolved final version
+     *
+     * @param string $path         A filesystem path
+     * @param bool   $canonicalize Whether or not to return a canonicalized path
+     *
+     * @return string|null
+     */
+    public function readlink($path, $canonicalize = false)
+    {
+        if (!$canonicalize && !is_link($path)) {
+            return null;
+        }
+
+        if ($canonicalize) {
+            if (!$this->exists($path)) {
+                return null;
             }
-            throw new IOException(sprintf('Failed to create symbolic link from "%s" to "%s".', $originDir, $targetDir), 0, null, $targetDir);
+
+            if ('\\' === \DIRECTORY_SEPARATOR) {
+                $path = readlink($path);
+            }
+
+            return realpath($path);
         }
+
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            return realpath($path);
+        }
+
+        return readlink($path);
     }
 
     /**
@@ -361,6 +445,10 @@ class Filesystem
      */
     public function makePathRelative($endPath, $startPath)
     {
+        if (!$this->isAbsolutePath($endPath) || !$this->isAbsolutePath($startPath)) {
+            @trigger_error(sprintf('Support for passing relative paths to %s() is deprecated since Symfony 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED);
+        }
+
         // Normalize separators on Windows
         if ('\\' === \DIRECTORY_SEPARATOR) {
             $endPath = str_replace('\\', '/', $endPath);
@@ -383,7 +471,7 @@ class Filesystem
         $endPathArr = explode('/', trim($endPath, '/'));
 
         $normalizePathArray = function ($pathSegments, $absolute) {
-            $result = array();
+            $result = [];
 
             foreach ($pathSegments as $segment) {
                 if ('..' === $segment && ($absolute || \count($result))) {
@@ -431,18 +519,18 @@ class Filesystem
      *  - existing files in the target directory will be overwritten, except if they are newer (see the `override` option)
      *  - files in the target directory that do not exist in the source directory will not be deleted (see the `delete` option)
      *
-     * @param string       $originDir The origin directory
-     * @param string       $targetDir The target directory
-     * @param \Traversable $iterator  Iterator that filters which files and directories to copy
-     * @param array        $options   An array of boolean options
-     *                                Valid options are:
-     *                                - $options['override'] If true, target files newer than origin files are overwritten (see copy(), defaults to false)
-     *                                - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink(), defaults to false)
-     *                                - $options['delete'] Whether to delete files that are not in the source directory (defaults to false)
+     * @param string            $originDir The origin directory
+     * @param string            $targetDir The target directory
+     * @param \Traversable|null $iterator  Iterator that filters which files and directories to copy, if null a recursive iterator is created
+     * @param array             $options   An array of boolean options
+     *                                     Valid options are:
+     *                                     - $options['override'] If true, target files newer than origin files are overwritten (see copy(), defaults to false)
+     *                                     - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink(), defaults to false)
+     *                                     - $options['delete'] Whether to delete files that are not in the source directory (defaults to false)
      *
      * @throws IOException When file type is unknown
      */
-    public function mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array())
+    public function mirror($originDir, $targetDir, \Traversable $iterator = null, $options = [])
     {
         $targetDir = rtrim($targetDir, '/\\');
         $originDir = rtrim($originDir, '/\\');
@@ -514,7 +602,7 @@ class Filesystem
     {
         return strspn($file, '/\\', 0, 1)
             || (\strlen($file) > 3 && ctype_alpha($file[0])
-                && ':' === substr($file, 1, 1)
+                && ':' === $file[1]
                 && strspn($file, '/\\', 2, 1)
             )
             || null !== parse_url($file, PHP_URL_SCHEME)
@@ -576,14 +664,12 @@ class Filesystem
     /**
      * Atomically dumps content into a file.
      *
-     * @param string   $filename The file to be written to
-     * @param string   $content  The data to write into the file
-     * @param int|null $mode     The file mode (octal). If null, file permissions are not modified
-     *                           Deprecated since version 2.3.12, to be removed in 3.0.
+     * @param string $filename The file to be written to
+     * @param string $content  The data to write into the file
      *
      * @throws IOException if the file cannot be written to
      */
-    public function dumpFile($filename, $content, $mode = 0666)
+    public function dumpFile($filename, $content)
     {
         $dir = \dirname($filename);
 
@@ -595,41 +681,56 @@ class Filesystem
             throw new IOException(sprintf('Unable to write to the "%s" directory.', $dir), 0, null, $dir);
         }
 
+        // Will create a temp file with 0600 access rights
+        // when the filesystem supports chmod.
         $tmpFile = $this->tempnam($dir, basename($filename));
 
         if (false === @file_put_contents($tmpFile, $content)) {
             throw new IOException(sprintf('Failed to write file "%s".', $filename), 0, null, $filename);
         }
 
-        if (null !== $mode) {
-            if (\func_num_args() > 2) {
-                @trigger_error('Support for modifying file permissions is deprecated since Symfony 2.3.12 and will be removed in 3.0.', E_USER_DEPRECATED);
-            }
-
-            $this->chmod($tmpFile, $mode);
-        } elseif (file_exists($filename)) {
-            @chmod($tmpFile, fileperms($filename));
-        }
+        @chmod($tmpFile, file_exists($filename) ? fileperms($filename) : 0666 & ~umask());
 
         $this->rename($tmpFile, $filename, true);
     }
 
     /**
-     * @param mixed $files
+     * Appends content to an existing file.
      *
-     * @return \Traversable
+     * @param string $filename The file to which to append content
+     * @param string $content  The content to append
+     *
+     * @throws IOException If the file is not writable
      */
-    private function toIterator($files)
+    public function appendToFile($filename, $content)
     {
-        if (!$files instanceof \Traversable) {
-            $files = new \ArrayObject(\is_array($files) ? $files : array($files));
+        $dir = \dirname($filename);
+
+        if (!is_dir($dir)) {
+            $this->mkdir($dir);
+        }
+
+        if (!is_writable($dir)) {
+            throw new IOException(sprintf('Unable to write to the "%s" directory.', $dir), 0, null, $dir);
         }
 
-        return $files;
+        if (false === @file_put_contents($filename, $content, FILE_APPEND)) {
+            throw new IOException(sprintf('Failed to write file "%s".', $filename), 0, null, $filename);
+        }
     }
 
     /**
-     * Gets a 2-tuple of scheme (may be null) and hierarchical part of a filename (e.g. file:///tmp -> array(file, tmp)).
+     * @param mixed $files
+     *
+     * @return array|\Traversable
+     */
+    private function toIterable($files)
+    {
+        return \is_array($files) || $files instanceof \Traversable ? $files : [$files];
+    }
+
+    /**
+     * Gets a 2-tuple of scheme (may be null) and hierarchical part of a filename (e.g. file:///tmp -> [file, tmp]).
      *
      * @param string $filename The filename to be parsed
      *
@@ -639,22 +740,27 @@ class Filesystem
     {
         $components = explode('://', $filename, 2);
 
-        return 2 === \count($components) ? array($components[0], $components[1]) : array(null, $components[0]);
+        return 2 === \count($components) ? [$components[0], $components[1]] : [null, $components[0]];
     }
 
+    /**
+     * @param callable $func
+     *
+     * @return mixed
+     */
     private static function box($func)
     {
         self::$lastError = null;
-        \set_error_handler(__CLASS__.'::handleError');
+        set_error_handler(__CLASS__.'::handleError');
         try {
             $result = \call_user_func_array($func, \array_slice(\func_get_args(), 1));
-            \restore_error_handler();
+            restore_error_handler();
 
             return $result;
         } catch (\Throwable $e) {
         } catch (\Exception $e) {
         }
-        \restore_error_handler();
+        restore_error_handler();
 
         throw $e;
     }
diff --git a/civicrm/vendor/symfony/filesystem/LICENSE b/civicrm/vendor/symfony/filesystem/LICENSE
index 21d7fb9e2f29b50caca3a76f0647e94e2cc8ddc1..9e936ec0448b8549e5edf08e5ac5f01491a8bfc8 100644
--- a/civicrm/vendor/symfony/filesystem/LICENSE
+++ b/civicrm/vendor/symfony/filesystem/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2018 Fabien Potencier
+Copyright (c) 2004-2020 Fabien Potencier
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/civicrm/vendor/symfony/filesystem/LockHandler.php b/civicrm/vendor/symfony/filesystem/LockHandler.php
index 6d85c51b8cb4ce4dbd31c738c941a9048820a06d..8e0eb741213b2e1c03bd62fbf86ea1450e0ad692 100644
--- a/civicrm/vendor/symfony/filesystem/LockHandler.php
+++ b/civicrm/vendor/symfony/filesystem/LockHandler.php
@@ -12,6 +12,10 @@
 namespace Symfony\Component\Filesystem;
 
 use Symfony\Component\Filesystem\Exception\IOException;
+use Symfony\Component\Lock\Store\FlockStore;
+use Symfony\Component\Lock\Store\SemaphoreStore;
+
+@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use %s or %s instead.', LockHandler::class, SemaphoreStore::class, FlockStore::class), E_USER_DEPRECATED);
 
 /**
  * LockHandler class provides a simple abstraction to lock anything by means of
@@ -25,6 +29,8 @@ use Symfony\Component\Filesystem\Exception\IOException;
  * @author Grégoire Pineau <lyrixx@lyrixx.info>
  * @author Romain Neutron <imprec@gmail.com>
  * @author Nicolas Grekas <p@tchwork.com>
+ *
+ * @deprecated since version 3.4, to be removed in 4.0. Use Symfony\Component\Lock\Store\SemaphoreStore or Symfony\Component\Lock\Store\FlockStore instead.
  */
 class LockHandler
 {
diff --git a/civicrm/vendor/symfony/filesystem/README.md b/civicrm/vendor/symfony/filesystem/README.md
index 877ab3543f32d0f60ee2508e799d21b0c4538b24..cb03d43c15dd25fabc83feb7b129b7037c6c6c8b 100644
--- a/civicrm/vendor/symfony/filesystem/README.md
+++ b/civicrm/vendor/symfony/filesystem/README.md
@@ -6,7 +6,7 @@ The Filesystem component provides basic utilities for the filesystem.
 Resources
 ---------
 
-  * [Documentation](https://symfony.com/doc/current/components/filesystem/index.html)
+  * [Documentation](https://symfony.com/doc/current/components/filesystem.html)
   * [Contributing](https://symfony.com/doc/current/contributing/index.html)
   * [Report issues](https://github.com/symfony/symfony/issues) and
     [send Pull Requests](https://github.com/symfony/symfony/pulls)
diff --git a/civicrm/vendor/symfony/filesystem/composer.json b/civicrm/vendor/symfony/filesystem/composer.json
index 3a2db520b6687346a692906d3df3075e3b3cd3c3..0fc8043cc092a08a3c876504766ba3615e594f8e 100644
--- a/civicrm/vendor/symfony/filesystem/composer.json
+++ b/civicrm/vendor/symfony/filesystem/composer.json
@@ -16,7 +16,7 @@
         }
     ],
     "require": {
-        "php": ">=5.3.9",
+        "php": "^5.5.9|>=7.0.8",
         "symfony/polyfill-ctype": "~1.8"
     },
     "autoload": {
@@ -28,7 +28,7 @@
     "minimum-stability": "dev",
     "extra": {
         "branch-alias": {
-            "dev-master": "2.8-dev"
+            "dev-master": "3.4-dev"
         }
     }
 }
diff --git a/civicrm/vendor/symfony/finder/Adapter/AbstractAdapter.php b/civicrm/vendor/symfony/finder/Adapter/AbstractAdapter.php
deleted file mode 100644
index 1a1647c31734b21d71d6cf4ac79fee008d6ef449..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/finder/Adapter/AbstractAdapter.php
+++ /dev/null
@@ -1,240 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Finder\Adapter;
-
-@trigger_error('The '.__NAMESPACE__.'\AbstractAdapter class is deprecated since Symfony 2.8 and will be removed in 3.0. Use directly the Finder class instead.', E_USER_DEPRECATED);
-
-/**
- * Interface for finder engine implementations.
- *
- * @author Jean-François Simon <contact@jfsimon.fr>
- *
- * @deprecated since 2.8, to be removed in 3.0. Use Finder instead.
- */
-abstract class AbstractAdapter implements AdapterInterface
-{
-    protected $followLinks = false;
-    protected $mode = 0;
-    protected $minDepth = 0;
-    protected $maxDepth = PHP_INT_MAX;
-    protected $exclude = array();
-    protected $names = array();
-    protected $notNames = array();
-    protected $contains = array();
-    protected $notContains = array();
-    protected $sizes = array();
-    protected $dates = array();
-    protected $filters = array();
-    protected $sort = false;
-    protected $paths = array();
-    protected $notPaths = array();
-    protected $ignoreUnreadableDirs = false;
-
-    private static $areSupported = array();
-
-    /**
-     * {@inheritdoc}
-     */
-    public function isSupported()
-    {
-        $name = $this->getName();
-
-        if (!array_key_exists($name, self::$areSupported)) {
-            self::$areSupported[$name] = $this->canBeUsed();
-        }
-
-        return self::$areSupported[$name];
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setFollowLinks($followLinks)
-    {
-        $this->followLinks = $followLinks;
-
-        return $this;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setMode($mode)
-    {
-        $this->mode = $mode;
-
-        return $this;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setDepths(array $depths)
-    {
-        $this->minDepth = 0;
-        $this->maxDepth = PHP_INT_MAX;
-
-        foreach ($depths as $comparator) {
-            switch ($comparator->getOperator()) {
-                case '>':
-                    $this->minDepth = $comparator->getTarget() + 1;
-                    break;
-                case '>=':
-                    $this->minDepth = $comparator->getTarget();
-                    break;
-                case '<':
-                    $this->maxDepth = $comparator->getTarget() - 1;
-                    break;
-                case '<=':
-                    $this->maxDepth = $comparator->getTarget();
-                    break;
-                default:
-                    $this->minDepth = $this->maxDepth = $comparator->getTarget();
-            }
-        }
-
-        return $this;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setExclude(array $exclude)
-    {
-        $this->exclude = $exclude;
-
-        return $this;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setNames(array $names)
-    {
-        $this->names = $names;
-
-        return $this;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setNotNames(array $notNames)
-    {
-        $this->notNames = $notNames;
-
-        return $this;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setContains(array $contains)
-    {
-        $this->contains = $contains;
-
-        return $this;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setNotContains(array $notContains)
-    {
-        $this->notContains = $notContains;
-
-        return $this;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setSizes(array $sizes)
-    {
-        $this->sizes = $sizes;
-
-        return $this;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setDates(array $dates)
-    {
-        $this->dates = $dates;
-
-        return $this;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setFilters(array $filters)
-    {
-        $this->filters = $filters;
-
-        return $this;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setSort($sort)
-    {
-        $this->sort = $sort;
-
-        return $this;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setPath(array $paths)
-    {
-        $this->paths = $paths;
-
-        return $this;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setNotPath(array $notPaths)
-    {
-        $this->notPaths = $notPaths;
-
-        return $this;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function ignoreUnreadableDirs($ignore = true)
-    {
-        $this->ignoreUnreadableDirs = (bool) $ignore;
-
-        return $this;
-    }
-
-    /**
-     * Returns whether the adapter is supported in the current environment.
-     *
-     * This method should be implemented in all adapters. Do not implement
-     * isSupported in the adapters as the generic implementation provides a cache
-     * layer.
-     *
-     * @see isSupported()
-     *
-     * @return bool Whether the adapter is supported
-     */
-    abstract protected function canBeUsed();
-}
diff --git a/civicrm/vendor/symfony/finder/Adapter/AbstractFindAdapter.php b/civicrm/vendor/symfony/finder/Adapter/AbstractFindAdapter.php
deleted file mode 100644
index ef458695b551c3937bcb7840b0f3f5b1db648e55..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/finder/Adapter/AbstractFindAdapter.php
+++ /dev/null
@@ -1,325 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Finder\Adapter;
-
-@trigger_error('The '.__NAMESPACE__.'\AbstractFindAdapter class is deprecated since Symfony 2.8 and will be removed in 3.0. Use directly the Finder class instead.', E_USER_DEPRECATED);
-
-use Symfony\Component\Finder\Comparator\DateComparator;
-use Symfony\Component\Finder\Comparator\NumberComparator;
-use Symfony\Component\Finder\Exception\AccessDeniedException;
-use Symfony\Component\Finder\Expression\Expression;
-use Symfony\Component\Finder\Iterator;
-use Symfony\Component\Finder\Shell\Command;
-use Symfony\Component\Finder\Shell\Shell;
-
-/**
- * Shell engine implementation using GNU find command.
- *
- * @author Jean-François Simon <contact@jfsimon.fr>
- *
- * @deprecated since 2.8, to be removed in 3.0. Use Finder instead.
- */
-abstract class AbstractFindAdapter extends AbstractAdapter
-{
-    protected $shell;
-
-    public function __construct()
-    {
-        $this->shell = new Shell();
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function searchInDirectory($dir)
-    {
-        // having "/../" in path make find fail
-        $dir = realpath($dir);
-
-        // searching directories containing or not containing strings leads to no result
-        if (Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES === $this->mode && ($this->contains || $this->notContains)) {
-            return new Iterator\FilePathsIterator(array(), $dir);
-        }
-
-        $command = Command::create();
-        $find = $this->buildFindCommand($command, $dir);
-
-        if ($this->followLinks) {
-            $find->add('-follow');
-        }
-
-        $find->add('-mindepth')->add($this->minDepth + 1);
-
-        if (PHP_INT_MAX !== $this->maxDepth) {
-            $find->add('-maxdepth')->add($this->maxDepth + 1);
-        }
-
-        if (Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES === $this->mode) {
-            $find->add('-type d');
-        } elseif (Iterator\FileTypeFilterIterator::ONLY_FILES === $this->mode) {
-            $find->add('-type f');
-        }
-
-        $this->buildNamesFiltering($find, $this->names);
-        $this->buildNamesFiltering($find, $this->notNames, true);
-        $this->buildPathsFiltering($find, $dir, $this->paths);
-        $this->buildPathsFiltering($find, $dir, $this->notPaths, true);
-        $this->buildSizesFiltering($find, $this->sizes);
-        $this->buildDatesFiltering($find, $this->dates);
-
-        $useGrep = $this->shell->testCommand('grep') && $this->shell->testCommand('xargs');
-        $useSort = \is_int($this->sort) && $this->shell->testCommand('sort') && $this->shell->testCommand('cut');
-
-        if ($useGrep && ($this->contains || $this->notContains)) {
-            $grep = $command->ins('grep');
-            $this->buildContentFiltering($grep, $this->contains);
-            $this->buildContentFiltering($grep, $this->notContains, true);
-        }
-
-        if ($useSort) {
-            $this->buildSorting($command, $this->sort);
-        }
-
-        $command->setErrorHandler(
-            $this->ignoreUnreadableDirs
-                // If directory is unreadable and finder is set to ignore it, `stderr` is ignored.
-                ? function ($stderr) { }
-                : function ($stderr) { throw new AccessDeniedException($stderr); }
-        );
-
-        $paths = $this->shell->testCommand('uniq') ? $command->add('| uniq')->execute() : array_unique($command->execute());
-        $iterator = new Iterator\FilePathsIterator($paths, $dir);
-
-        if ($this->exclude) {
-            $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude);
-        }
-
-        if (!$useGrep && ($this->contains || $this->notContains)) {
-            $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains);
-        }
-
-        if ($this->filters) {
-            $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
-        }
-
-        if (!$useSort && $this->sort) {
-            $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort);
-            $iterator = $iteratorAggregate->getIterator();
-        }
-
-        return $iterator;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function canBeUsed()
-    {
-        return $this->shell->testCommand('find');
-    }
-
-    /**
-     * @param Command $command
-     * @param string  $dir
-     *
-     * @return Command
-     */
-    protected function buildFindCommand(Command $command, $dir)
-    {
-        return $command
-            ->ins('find')
-            ->add('find ')
-            ->arg($dir)
-            ->add('-noleaf'); // the -noleaf option is required for filesystems that don't follow the '.' and '..' conventions
-    }
-
-    /**
-     * @param Command  $command
-     * @param string[] $names
-     * @param bool     $not
-     */
-    private function buildNamesFiltering(Command $command, array $names, $not = false)
-    {
-        if (0 === \count($names)) {
-            return;
-        }
-
-        $command->add($not ? '-not' : null)->cmd('(');
-
-        foreach ($names as $i => $name) {
-            $expr = Expression::create($name);
-
-            // Find does not support expandable globs ("*.{a,b}" syntax).
-            if ($expr->isGlob() && $expr->getGlob()->isExpandable()) {
-                $expr = Expression::create($expr->getGlob()->toRegex(false));
-            }
-
-            // Fixes 'not search' and 'full path matching' regex problems.
-            // - Jokers '.' are replaced by [^/].
-            // - We add '[^/]*' before and after regex (if no ^|$ flags are present).
-            if ($expr->isRegex()) {
-                $regex = $expr->getRegex();
-                $regex->prepend($regex->hasStartFlag() ? '/' : '/[^/]*')
-                    ->setStartFlag(false)
-                    ->setStartJoker(true)
-                    ->replaceJokers('[^/]');
-                if (!$regex->hasEndFlag() || $regex->hasEndJoker()) {
-                    $regex->setEndJoker(false)->append('[^/]*');
-                }
-            }
-
-            $command
-                ->add($i > 0 ? '-or' : null)
-                ->add($expr->isRegex()
-                    ? ($expr->isCaseSensitive() ? '-regex' : '-iregex')
-                    : ($expr->isCaseSensitive() ? '-name' : '-iname')
-                )
-                ->arg($expr->renderPattern());
-        }
-
-        $command->cmd(')');
-    }
-
-    /**
-     * @param Command  $command
-     * @param string   $dir
-     * @param string[] $paths
-     * @param bool     $not
-     */
-    private function buildPathsFiltering(Command $command, $dir, array $paths, $not = false)
-    {
-        if (0 === \count($paths)) {
-            return;
-        }
-
-        $command->add($not ? '-not' : null)->cmd('(');
-
-        foreach ($paths as $i => $path) {
-            $expr = Expression::create($path);
-
-            // Find does not support expandable globs ("*.{a,b}" syntax).
-            if ($expr->isGlob() && $expr->getGlob()->isExpandable()) {
-                $expr = Expression::create($expr->getGlob()->toRegex(false));
-            }
-
-            // Fixes 'not search' regex problems.
-            if ($expr->isRegex()) {
-                $regex = $expr->getRegex();
-                $regex->prepend($regex->hasStartFlag() ? preg_quote($dir).\DIRECTORY_SEPARATOR : '.*')->setEndJoker(!$regex->hasEndFlag());
-            } else {
-                $expr->prepend('*')->append('*');
-            }
-
-            $command
-                ->add($i > 0 ? '-or' : null)
-                ->add($expr->isRegex()
-                    ? ($expr->isCaseSensitive() ? '-regex' : '-iregex')
-                    : ($expr->isCaseSensitive() ? '-path' : '-ipath')
-                )
-                ->arg($expr->renderPattern());
-        }
-
-        $command->cmd(')');
-    }
-
-    /**
-     * @param Command            $command
-     * @param NumberComparator[] $sizes
-     */
-    private function buildSizesFiltering(Command $command, array $sizes)
-    {
-        foreach ($sizes as $i => $size) {
-            $command->add($i > 0 ? '-and' : null);
-
-            switch ($size->getOperator()) {
-                case '<=':
-                    $command->add('-size -'.($size->getTarget() + 1).'c');
-                    break;
-                case '>=':
-                    $command->add('-size +'.($size->getTarget() - 1).'c');
-                    break;
-                case '>':
-                    $command->add('-size +'.$size->getTarget().'c');
-                    break;
-                case '!=':
-                    $command->add('-size -'.$size->getTarget().'c');
-                    $command->add('-size +'.$size->getTarget().'c');
-                    break;
-                case '<':
-                default:
-                    $command->add('-size -'.$size->getTarget().'c');
-            }
-        }
-    }
-
-    /**
-     * @param Command          $command
-     * @param DateComparator[] $dates
-     */
-    private function buildDatesFiltering(Command $command, array $dates)
-    {
-        foreach ($dates as $i => $date) {
-            $command->add($i > 0 ? '-and' : null);
-
-            $mins = (int) round((time() - $date->getTarget()) / 60);
-
-            if (0 > $mins) {
-                // mtime is in the future
-                $command->add(' -mmin -0');
-                // we will have no result so we don't need to continue
-                return;
-            }
-
-            switch ($date->getOperator()) {
-                case '<=':
-                    $command->add('-mmin +'.($mins - 1));
-                    break;
-                case '>=':
-                    $command->add('-mmin -'.($mins + 1));
-                    break;
-                case '>':
-                    $command->add('-mmin -'.$mins);
-                    break;
-                case '!=':
-                    $command->add('-mmin +'.$mins.' -or -mmin -'.$mins);
-                    break;
-                case '<':
-                default:
-                    $command->add('-mmin +'.$mins);
-            }
-        }
-    }
-
-    /**
-     * @param Command $command
-     * @param string  $sort
-     *
-     * @throws \InvalidArgumentException
-     */
-    private function buildSorting(Command $command, $sort)
-    {
-        $this->buildFormatSorting($command, $sort);
-    }
-
-    /**
-     * @param Command $command
-     * @param string  $sort
-     */
-    abstract protected function buildFormatSorting(Command $command, $sort);
-
-    /**
-     * @param Command $command
-     * @param array   $contains
-     * @param bool    $not
-     */
-    abstract protected function buildContentFiltering(Command $command, array $contains, $not = false);
-}
diff --git a/civicrm/vendor/symfony/finder/Adapter/AdapterInterface.php b/civicrm/vendor/symfony/finder/Adapter/AdapterInterface.php
deleted file mode 100644
index 8d8fb0749a6b4e753214507df079dbead6671209..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/finder/Adapter/AdapterInterface.php
+++ /dev/null
@@ -1,124 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Finder\Adapter;
-
-/**
- * @author Jean-François Simon <contact@jfsimon.fr>
- *
- * @deprecated since 2.8, to be removed in 3.0.
- */
-interface AdapterInterface
-{
-    /**
-     * @param bool $followLinks
-     *
-     * @return $this
-     */
-    public function setFollowLinks($followLinks);
-
-    /**
-     * @param int $mode
-     *
-     * @return $this
-     */
-    public function setMode($mode);
-
-    /**
-     * @return $this
-     */
-    public function setExclude(array $exclude);
-
-    /**
-     * @return $this
-     */
-    public function setDepths(array $depths);
-
-    /**
-     * @return $this
-     */
-    public function setNames(array $names);
-
-    /**
-     * @return $this
-     */
-    public function setNotNames(array $notNames);
-
-    /**
-     * @return $this
-     */
-    public function setContains(array $contains);
-
-    /**
-     * @return $this
-     */
-    public function setNotContains(array $notContains);
-
-    /**
-     * @return $this
-     */
-    public function setSizes(array $sizes);
-
-    /**
-     * @return $this
-     */
-    public function setDates(array $dates);
-
-    /**
-     * @return $this
-     */
-    public function setFilters(array $filters);
-
-    /**
-     * @param \Closure|int $sort
-     *
-     * @return $this
-     */
-    public function setSort($sort);
-
-    /**
-     * @return $this
-     */
-    public function setPath(array $paths);
-
-    /**
-     * @return $this
-     */
-    public function setNotPath(array $notPaths);
-
-    /**
-     * @param bool $ignore
-     *
-     * @return $this
-     */
-    public function ignoreUnreadableDirs($ignore = true);
-
-    /**
-     * @param string $dir
-     *
-     * @return \Iterator Result iterator
-     */
-    public function searchInDirectory($dir);
-
-    /**
-     * Tests adapter support for current platform.
-     *
-     * @return bool
-     */
-    public function isSupported();
-
-    /**
-     * Returns adapter name.
-     *
-     * @return string
-     */
-    public function getName();
-}
diff --git a/civicrm/vendor/symfony/finder/Adapter/BsdFindAdapter.php b/civicrm/vendor/symfony/finder/Adapter/BsdFindAdapter.php
deleted file mode 100644
index f6e4303d0085c096a3ce97a2a25d105c78ccddaa..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/finder/Adapter/BsdFindAdapter.php
+++ /dev/null
@@ -1,107 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Finder\Adapter;
-
-@trigger_error('The '.__NAMESPACE__.'\BsdFindAdapter class is deprecated since Symfony 2.8 and will be removed in 3.0. Use directly the Finder class instead.', E_USER_DEPRECATED);
-
-use Symfony\Component\Finder\Expression\Expression;
-use Symfony\Component\Finder\Iterator\SortableIterator;
-use Symfony\Component\Finder\Shell\Command;
-use Symfony\Component\Finder\Shell\Shell;
-
-/**
- * Shell engine implementation using BSD find command.
- *
- * @author Jean-François Simon <contact@jfsimon.fr>
- *
- * @deprecated since 2.8, to be removed in 3.0. Use Finder instead.
- */
-class BsdFindAdapter extends AbstractFindAdapter
-{
-    /**
-     * {@inheritdoc}
-     */
-    public function getName()
-    {
-        return 'bsd_find';
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function canBeUsed()
-    {
-        return \in_array($this->shell->getType(), array(Shell::TYPE_BSD, Shell::TYPE_DARWIN)) && parent::canBeUsed();
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function buildFormatSorting(Command $command, $sort)
-    {
-        switch ($sort) {
-            case SortableIterator::SORT_BY_NAME:
-                $command->ins('sort')->add('| sort');
-
-                return;
-            case SortableIterator::SORT_BY_TYPE:
-                $format = '%HT';
-                break;
-            case SortableIterator::SORT_BY_ACCESSED_TIME:
-                $format = '%a';
-                break;
-            case SortableIterator::SORT_BY_CHANGED_TIME:
-                $format = '%c';
-                break;
-            case SortableIterator::SORT_BY_MODIFIED_TIME:
-                $format = '%m';
-                break;
-            default:
-                throw new \InvalidArgumentException(sprintf('Unknown sort options: %s.', $sort));
-        }
-
-        $command
-            ->add('-print0 | xargs -0 stat -f')
-            ->arg($format.'%t%N')
-            ->add('| sort | cut -f 2');
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function buildFindCommand(Command $command, $dir)
-    {
-        parent::buildFindCommand($command, $dir)->addAtIndex('-E', 1);
-
-        return $command;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function buildContentFiltering(Command $command, array $contains, $not = false)
-    {
-        foreach ($contains as $contain) {
-            $expr = Expression::create($contain);
-
-            // todo: avoid forking process for each $pattern by using multiple -e options
-            $command
-                ->add('| grep -v \'^$\'')
-                ->add('| xargs -I{} grep -I')
-                ->add($expr->isCaseSensitive() ? null : '-i')
-                ->add($not ? '-L' : '-l')
-                ->add('-Ee')->arg($expr->renderPattern())
-                ->add('{}')
-            ;
-        }
-    }
-}
diff --git a/civicrm/vendor/symfony/finder/Adapter/GnuFindAdapter.php b/civicrm/vendor/symfony/finder/Adapter/GnuFindAdapter.php
deleted file mode 100644
index 140c5665c9ae931b03b2a15570313a29b1ee9c98..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/finder/Adapter/GnuFindAdapter.php
+++ /dev/null
@@ -1,108 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Finder\Adapter;
-
-@trigger_error('The '.__NAMESPACE__.'\GnuFindAdapter class is deprecated since Symfony 2.8 and will be removed in 3.0. Use directly the Finder class instead.', E_USER_DEPRECATED);
-
-use Symfony\Component\Finder\Expression\Expression;
-use Symfony\Component\Finder\Iterator\SortableIterator;
-use Symfony\Component\Finder\Shell\Command;
-use Symfony\Component\Finder\Shell\Shell;
-
-/**
- * Shell engine implementation using GNU find command.
- *
- * @author Jean-François Simon <contact@jfsimon.fr>
- *
- * @deprecated since 2.8, to be removed in 3.0. Use Finder instead.
- */
-class GnuFindAdapter extends AbstractFindAdapter
-{
-    /**
-     * {@inheritdoc}
-     */
-    public function getName()
-    {
-        return 'gnu_find';
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function buildFormatSorting(Command $command, $sort)
-    {
-        switch ($sort) {
-            case SortableIterator::SORT_BY_NAME:
-                $command->ins('sort')->add('| sort');
-
-                return;
-            case SortableIterator::SORT_BY_TYPE:
-                $format = '%y';
-                break;
-            case SortableIterator::SORT_BY_ACCESSED_TIME:
-                $format = '%A@';
-                break;
-            case SortableIterator::SORT_BY_CHANGED_TIME:
-                $format = '%C@';
-                break;
-            case SortableIterator::SORT_BY_MODIFIED_TIME:
-                $format = '%T@';
-                break;
-            default:
-                throw new \InvalidArgumentException(sprintf('Unknown sort options: %s.', $sort));
-        }
-
-        $command
-            ->get('find')
-            ->add('-printf')
-            ->arg($format.' %h/%f\\n')
-            ->add('| sort | cut')
-            ->arg('-d ')
-            ->arg('-f2-')
-        ;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function canBeUsed()
-    {
-        return Shell::TYPE_UNIX === $this->shell->getType() && parent::canBeUsed();
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function buildFindCommand(Command $command, $dir)
-    {
-        return parent::buildFindCommand($command, $dir)->add('-regextype posix-extended');
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function buildContentFiltering(Command $command, array $contains, $not = false)
-    {
-        foreach ($contains as $contain) {
-            $expr = Expression::create($contain);
-
-            // todo: avoid forking process for each $pattern by using multiple -e options
-            $command
-                ->add('| xargs -I{} -r grep -I')
-                ->add($expr->isCaseSensitive() ? null : '-i')
-                ->add($not ? '-L' : '-l')
-                ->add('-Ee')->arg($expr->renderPattern())
-                ->add('{}')
-            ;
-        }
-    }
-}
diff --git a/civicrm/vendor/symfony/finder/Adapter/PhpAdapter.php b/civicrm/vendor/symfony/finder/Adapter/PhpAdapter.php
deleted file mode 100644
index c2fb66cf5ef57208fe56f9ef5a16d4e8dad8bfaf..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/finder/Adapter/PhpAdapter.php
+++ /dev/null
@@ -1,101 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Finder\Adapter;
-
-@trigger_error('The '.__NAMESPACE__.'\PhpAdapter class is deprecated since Symfony 2.8 and will be removed in 3.0. Use directly the Finder class instead.', E_USER_DEPRECATED);
-
-use Symfony\Component\Finder\Iterator;
-
-/**
- * PHP finder engine implementation.
- *
- * @author Jean-François Simon <contact@jfsimon.fr>
- *
- * @deprecated since 2.8, to be removed in 3.0. Use Finder instead.
- */
-class PhpAdapter extends AbstractAdapter
-{
-    /**
-     * {@inheritdoc}
-     */
-    public function searchInDirectory($dir)
-    {
-        $flags = \RecursiveDirectoryIterator::SKIP_DOTS;
-
-        if ($this->followLinks) {
-            $flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS;
-        }
-
-        $iterator = new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs);
-
-        if ($this->exclude) {
-            $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude);
-        }
-
-        $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST);
-
-        if ($this->minDepth > 0 || $this->maxDepth < PHP_INT_MAX) {
-            $iterator = new Iterator\DepthRangeFilterIterator($iterator, $this->minDepth, $this->maxDepth);
-        }
-
-        if ($this->mode) {
-            $iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode);
-        }
-
-        if ($this->names || $this->notNames) {
-            $iterator = new Iterator\FilenameFilterIterator($iterator, $this->names, $this->notNames);
-        }
-
-        if ($this->contains || $this->notContains) {
-            $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains);
-        }
-
-        if ($this->sizes) {
-            $iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes);
-        }
-
-        if ($this->dates) {
-            $iterator = new Iterator\DateRangeFilterIterator($iterator, $this->dates);
-        }
-
-        if ($this->filters) {
-            $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
-        }
-
-        if ($this->paths || $this->notPaths) {
-            $iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $this->notPaths);
-        }
-
-        if ($this->sort) {
-            $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort);
-            $iterator = $iteratorAggregate->getIterator();
-        }
-
-        return $iterator;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function getName()
-    {
-        return 'php';
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function canBeUsed()
-    {
-        return true;
-    }
-}
diff --git a/civicrm/vendor/symfony/finder/CHANGELOG.md b/civicrm/vendor/symfony/finder/CHANGELOG.md
index a45c20825f5fc2b4ac841933da2e18a3ff10ad8e..53c34be860c565ecd4e088479246cb9a0d269f9a 100644
--- a/civicrm/vendor/symfony/finder/CHANGELOG.md
+++ b/civicrm/vendor/symfony/finder/CHANGELOG.md
@@ -1,6 +1,22 @@
 CHANGELOG
 =========
 
+3.4.0
+-----
+
+ * deprecated `Symfony\Component\Finder\Iterator\FilterIterator`
+ * added Finder::hasResults() method to check if any results were found
+
+3.3.0
+-----
+
+ * added double-star matching to Glob::toRegex()
+
+3.0.0
+-----
+
+ * removed deprecated classes
+
 2.8.0
 -----
 
diff --git a/civicrm/vendor/symfony/finder/Comparator/Comparator.php b/civicrm/vendor/symfony/finder/Comparator/Comparator.php
index ea37566df8887da2fa67fe19f8edad058c56140e..6aee21cf09290e08e8308e2d9677a483823c5121 100644
--- a/civicrm/vendor/symfony/finder/Comparator/Comparator.php
+++ b/civicrm/vendor/symfony/finder/Comparator/Comparator.php
@@ -64,7 +64,7 @@ class Comparator
             $operator = '==';
         }
 
-        if (!\in_array($operator, array('>', '<', '>=', '<=', '==', '!='))) {
+        if (!\in_array($operator, ['>', '<', '>=', '<=', '==', '!='])) {
             throw new \InvalidArgumentException(sprintf('Invalid operator "%s".', $operator));
         }
 
diff --git a/civicrm/vendor/symfony/finder/Exception/AdapterFailureException.php b/civicrm/vendor/symfony/finder/Exception/AdapterFailureException.php
deleted file mode 100644
index 594940a47ed10f238b08e3c5f21c1a8e2071f4d0..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/finder/Exception/AdapterFailureException.php
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Finder\Exception;
-
-@trigger_error('The '.__NAMESPACE__.'\AdapterFailureException class is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-
-use Symfony\Component\Finder\Adapter\AdapterInterface;
-
-/**
- * Base exception for all adapter failures.
- *
- * @author Jean-François Simon <contact@jfsimon.fr>
- *
- * @deprecated since 2.8, to be removed in 3.0.
- */
-class AdapterFailureException extends \RuntimeException implements ExceptionInterface
-{
-    private $adapter;
-
-    /**
-     * @param AdapterInterface $adapter
-     * @param string|null      $message
-     * @param \Exception|null  $previous
-     */
-    public function __construct(AdapterInterface $adapter, $message = null, \Exception $previous = null)
-    {
-        $this->adapter = $adapter;
-        parent::__construct($message ?: 'Search failed with "'.$adapter->getName().'" adapter.', $previous);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function getAdapter()
-    {
-        return $this->adapter;
-    }
-}
diff --git a/civicrm/vendor/symfony/finder/Exception/ExceptionInterface.php b/civicrm/vendor/symfony/finder/Exception/ExceptionInterface.php
index bff02142dff98ddf3e88056422ca72e2bee2f501..161e9686d2b70f4d89d241008f0edf7f9f3be9af 100644
--- a/civicrm/vendor/symfony/finder/Exception/ExceptionInterface.php
+++ b/civicrm/vendor/symfony/finder/Exception/ExceptionInterface.php
@@ -13,6 +13,8 @@ namespace Symfony\Component\Finder\Exception;
 
 /**
  * @author Jean-François Simon <contact@jfsimon.fr>
+ *
+ * @deprecated since 3.3, to be removed in 4.0.
  */
 interface ExceptionInterface
 {
diff --git a/civicrm/vendor/symfony/finder/Exception/OperationNotPermitedException.php b/civicrm/vendor/symfony/finder/Exception/OperationNotPermitedException.php
deleted file mode 100644
index 4c668582cb5e9fd557020d7d9c694f33ed78cd99..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/finder/Exception/OperationNotPermitedException.php
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Finder\Exception;
-
-@trigger_error('The '.__NAMESPACE__.'\OperationNotPermitedException class is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-
-/**
- * @author Jean-François Simon <contact@jfsimon.fr>
- *
- * @deprecated since 2.8, to be removed in 3.0.
- */
-class OperationNotPermitedException extends AdapterFailureException
-{
-}
diff --git a/civicrm/vendor/symfony/finder/Exception/ShellCommandFailureException.php b/civicrm/vendor/symfony/finder/Exception/ShellCommandFailureException.php
deleted file mode 100644
index db85e682d91117c02eb6f5412e9e2378394feed0..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/finder/Exception/ShellCommandFailureException.php
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Finder\Exception;
-
-@trigger_error('The '.__NAMESPACE__.'\ShellCommandFailureException class is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-
-use Symfony\Component\Finder\Adapter\AdapterInterface;
-use Symfony\Component\Finder\Shell\Command;
-
-/**
- * @author Jean-François Simon <contact@jfsimon.fr>
- *
- * @deprecated since 2.8, to be removed in 3.0.
- */
-class ShellCommandFailureException extends AdapterFailureException
-{
-    private $command;
-
-    public function __construct(AdapterInterface $adapter, Command $command, \Exception $previous = null)
-    {
-        $this->command = $command;
-        parent::__construct($adapter, 'Shell command failed: "'.$command->join().'".', $previous);
-    }
-
-    /**
-     * @return Command
-     */
-    public function getCommand()
-    {
-        return $this->command;
-    }
-}
diff --git a/civicrm/vendor/symfony/finder/Expression/Expression.php b/civicrm/vendor/symfony/finder/Expression/Expression.php
deleted file mode 100644
index e83c771609dca69fe8ebc1d67a5fe515e9eeb4aa..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/finder/Expression/Expression.php
+++ /dev/null
@@ -1,148 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Finder\Expression;
-
-@trigger_error('The '.__NAMESPACE__.'\Expression class is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-
-/**
- * @author Jean-François Simon <contact@jfsimon.fr>
- */
-class Expression implements ValueInterface
-{
-    const TYPE_REGEX = 1;
-    const TYPE_GLOB = 2;
-
-    /**
-     * @var ValueInterface
-     */
-    private $value;
-
-    /**
-     * @param string $expr
-     *
-     * @return self
-     */
-    public static function create($expr)
-    {
-        return new self($expr);
-    }
-
-    /**
-     * @param string $expr
-     */
-    public function __construct($expr)
-    {
-        try {
-            $this->value = Regex::create($expr);
-        } catch (\InvalidArgumentException $e) {
-            $this->value = new Glob($expr);
-        }
-    }
-
-    /**
-     * @return string
-     */
-    public function __toString()
-    {
-        return $this->render();
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function render()
-    {
-        return $this->value->render();
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function renderPattern()
-    {
-        return $this->value->renderPattern();
-    }
-
-    /**
-     * @return bool
-     */
-    public function isCaseSensitive()
-    {
-        return $this->value->isCaseSensitive();
-    }
-
-    /**
-     * @return int
-     */
-    public function getType()
-    {
-        return $this->value->getType();
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function prepend($expr)
-    {
-        $this->value->prepend($expr);
-
-        return $this;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function append($expr)
-    {
-        $this->value->append($expr);
-
-        return $this;
-    }
-
-    /**
-     * @return bool
-     */
-    public function isRegex()
-    {
-        return self::TYPE_REGEX === $this->value->getType();
-    }
-
-    /**
-     * @return bool
-     */
-    public function isGlob()
-    {
-        return self::TYPE_GLOB === $this->value->getType();
-    }
-
-    /**
-     * @return Glob
-     *
-     * @throws \LogicException
-     */
-    public function getGlob()
-    {
-        if (self::TYPE_GLOB !== $this->value->getType()) {
-            throw new \LogicException('Regex can\'t be transformed to glob.');
-        }
-
-        return $this->value;
-    }
-
-    /**
-     * @return Regex
-     */
-    public function getRegex()
-    {
-        return self::TYPE_REGEX === $this->value->getType() ? $this->value : $this->value->toRegex();
-    }
-}
diff --git a/civicrm/vendor/symfony/finder/Expression/Glob.php b/civicrm/vendor/symfony/finder/Expression/Glob.php
deleted file mode 100644
index e80578ecef9dfd8184787af37fb24103f6c576b1..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/finder/Expression/Glob.php
+++ /dev/null
@@ -1,108 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Finder\Expression;
-
-@trigger_error('The '.__NAMESPACE__.'\Glob class is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-
-use Symfony\Component\Finder\Glob as FinderGlob;
-
-/**
- * @author Jean-François Simon <contact@jfsimon.fr>
- */
-class Glob implements ValueInterface
-{
-    private $pattern;
-
-    /**
-     * @param string $pattern
-     */
-    public function __construct($pattern)
-    {
-        $this->pattern = $pattern;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function render()
-    {
-        return $this->pattern;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function renderPattern()
-    {
-        return $this->pattern;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function getType()
-    {
-        return Expression::TYPE_GLOB;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function isCaseSensitive()
-    {
-        return true;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function prepend($expr)
-    {
-        $this->pattern = $expr.$this->pattern;
-
-        return $this;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function append($expr)
-    {
-        $this->pattern .= $expr;
-
-        return $this;
-    }
-
-    /**
-     * Tests if glob is expandable ("*.{a,b}" syntax).
-     *
-     * @return bool
-     */
-    public function isExpandable()
-    {
-        return false !== strpos($this->pattern, '{')
-            && false !== strpos($this->pattern, '}');
-    }
-
-    /**
-     * @param bool $strictLeadingDot
-     * @param bool $strictWildcardSlash
-     *
-     * @return Regex
-     */
-    public function toRegex($strictLeadingDot = true, $strictWildcardSlash = true)
-    {
-        $regex = FinderGlob::toRegex($this->pattern, $strictLeadingDot, $strictWildcardSlash, '');
-
-        return new Regex($regex);
-    }
-}
diff --git a/civicrm/vendor/symfony/finder/Expression/Regex.php b/civicrm/vendor/symfony/finder/Expression/Regex.php
deleted file mode 100644
index 2f1ab3db03fc40abdc3d757e057781673fb8b91c..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/finder/Expression/Regex.php
+++ /dev/null
@@ -1,321 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Finder\Expression;
-
-@trigger_error('The '.__NAMESPACE__.'\Regex class is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-
-/**
- * @author Jean-François Simon <contact@jfsimon.fr>
- */
-class Regex implements ValueInterface
-{
-    const START_FLAG = '^';
-    const END_FLAG = '$';
-    const BOUNDARY = '~';
-    const JOKER = '.*';
-    const ESCAPING = '\\';
-
-    /**
-     * @var string
-     */
-    private $pattern;
-
-    /**
-     * @var string
-     */
-    private $options;
-
-    /**
-     * @var bool
-     */
-    private $startFlag;
-
-    /**
-     * @var bool
-     */
-    private $endFlag;
-
-    /**
-     * @var bool
-     */
-    private $startJoker;
-
-    /**
-     * @var bool
-     */
-    private $endJoker;
-
-    /**
-     * @param string $expr
-     *
-     * @return self
-     *
-     * @throws \InvalidArgumentException
-     */
-    public static function create($expr)
-    {
-        if (preg_match('/^(.{3,}?)([imsxuADU]*)$/', $expr, $m)) {
-            $start = substr($m[1], 0, 1);
-            $end = substr($m[1], -1);
-
-            if (
-                ($start === $end && !preg_match('/[*?[:alnum:] \\\\]/', $start))
-                || ('{' === $start && '}' === $end)
-                || ('(' === $start && ')' === $end)
-            ) {
-                return new self(substr($m[1], 1, -1), $m[2], $end);
-            }
-        }
-
-        throw new \InvalidArgumentException('Given expression is not a regex.');
-    }
-
-    /**
-     * @param string $pattern
-     * @param string $options
-     * @param string $delimiter
-     */
-    public function __construct($pattern, $options = '', $delimiter = null)
-    {
-        if (null !== $delimiter) {
-            // removes delimiter escaping
-            $pattern = str_replace('\\'.$delimiter, $delimiter, $pattern);
-        }
-
-        $this->parsePattern($pattern);
-        $this->options = $options;
-    }
-
-    /**
-     * @return string
-     */
-    public function __toString()
-    {
-        return $this->render();
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function render()
-    {
-        return self::BOUNDARY
-            .$this->renderPattern()
-            .self::BOUNDARY
-            .$this->options;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function renderPattern()
-    {
-        return ($this->startFlag ? self::START_FLAG : '')
-            .($this->startJoker ? self::JOKER : '')
-            .str_replace(self::BOUNDARY, '\\'.self::BOUNDARY, $this->pattern)
-            .($this->endJoker ? self::JOKER : '')
-            .($this->endFlag ? self::END_FLAG : '');
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function isCaseSensitive()
-    {
-        return !$this->hasOption('i');
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function getType()
-    {
-        return Expression::TYPE_REGEX;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function prepend($expr)
-    {
-        $this->pattern = $expr.$this->pattern;
-
-        return $this;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function append($expr)
-    {
-        $this->pattern .= $expr;
-
-        return $this;
-    }
-
-    /**
-     * @param string $option
-     *
-     * @return bool
-     */
-    public function hasOption($option)
-    {
-        return false !== strpos($this->options, $option);
-    }
-
-    /**
-     * @param string $option
-     *
-     * @return $this
-     */
-    public function addOption($option)
-    {
-        if (!$this->hasOption($option)) {
-            $this->options .= $option;
-        }
-
-        return $this;
-    }
-
-    /**
-     * @param string $option
-     *
-     * @return $this
-     */
-    public function removeOption($option)
-    {
-        $this->options = str_replace($option, '', $this->options);
-
-        return $this;
-    }
-
-    /**
-     * @param bool $startFlag
-     *
-     * @return $this
-     */
-    public function setStartFlag($startFlag)
-    {
-        $this->startFlag = $startFlag;
-
-        return $this;
-    }
-
-    /**
-     * @return bool
-     */
-    public function hasStartFlag()
-    {
-        return $this->startFlag;
-    }
-
-    /**
-     * @param bool $endFlag
-     *
-     * @return $this
-     */
-    public function setEndFlag($endFlag)
-    {
-        $this->endFlag = (bool) $endFlag;
-
-        return $this;
-    }
-
-    /**
-     * @return bool
-     */
-    public function hasEndFlag()
-    {
-        return $this->endFlag;
-    }
-
-    /**
-     * @param bool $startJoker
-     *
-     * @return $this
-     */
-    public function setStartJoker($startJoker)
-    {
-        $this->startJoker = $startJoker;
-
-        return $this;
-    }
-
-    /**
-     * @return bool
-     */
-    public function hasStartJoker()
-    {
-        return $this->startJoker;
-    }
-
-    /**
-     * @param bool $endJoker
-     *
-     * @return $this
-     */
-    public function setEndJoker($endJoker)
-    {
-        $this->endJoker = (bool) $endJoker;
-
-        return $this;
-    }
-
-    /**
-     * @return bool
-     */
-    public function hasEndJoker()
-    {
-        return $this->endJoker;
-    }
-
-    /**
-     * @return $this
-     */
-    public function replaceJokers($replacement)
-    {
-        $replace = function ($subject) use ($replacement) {
-            $subject = $subject[0];
-            $replace = 0 === substr_count($subject, '\\') % 2;
-
-            return $replace ? str_replace('.', $replacement, $subject) : $subject;
-        };
-
-        $this->pattern = preg_replace_callback('~[\\\\]*\\.~', $replace, $this->pattern);
-
-        return $this;
-    }
-
-    /**
-     * @param string $pattern
-     */
-    private function parsePattern($pattern)
-    {
-        if ($this->startFlag = self::START_FLAG === substr($pattern, 0, 1)) {
-            $pattern = substr($pattern, 1);
-        }
-
-        if ($this->startJoker = self::JOKER === substr($pattern, 0, 2)) {
-            $pattern = substr($pattern, 2);
-        }
-
-        if ($this->endFlag = (self::END_FLAG === substr($pattern, -1) && self::ESCAPING !== substr($pattern, -2, -1))) {
-            $pattern = substr($pattern, 0, -1);
-        }
-
-        if ($this->endJoker = (self::JOKER === substr($pattern, -2) && self::ESCAPING !== substr($pattern, -3, -2))) {
-            $pattern = substr($pattern, 0, -2);
-        }
-
-        $this->pattern = $pattern;
-    }
-}
diff --git a/civicrm/vendor/symfony/finder/Expression/ValueInterface.php b/civicrm/vendor/symfony/finder/Expression/ValueInterface.php
deleted file mode 100644
index 08ede1422ecdf24a4889873317b43c809010090d..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/finder/Expression/ValueInterface.php
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Finder\Expression;
-
-@trigger_error('The '.__NAMESPACE__.'\ValueInterface interface is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-
-/**
- * @author Jean-François Simon <contact@jfsimon.fr>
- */
-interface ValueInterface
-{
-    /**
-     * Renders string representation of expression.
-     *
-     * @return string
-     */
-    public function render();
-
-    /**
-     * Renders string representation of pattern.
-     *
-     * @return string
-     */
-    public function renderPattern();
-
-    /**
-     * Returns value case sensitivity.
-     *
-     * @return bool
-     */
-    public function isCaseSensitive();
-
-    /**
-     * Returns expression type.
-     *
-     * @return int
-     */
-    public function getType();
-
-    /**
-     * @param string $expr
-     *
-     * @return $this
-     */
-    public function prepend($expr);
-
-    /**
-     * @param string $expr
-     *
-     * @return $this
-     */
-    public function append($expr);
-}
diff --git a/civicrm/vendor/symfony/finder/Finder.php b/civicrm/vendor/symfony/finder/Finder.php
index 1ee9017356903d7c618ec2fdbf3b3db41572dc5c..33a76cc976f867dc17e107e05a5da85b96a544a8 100644
--- a/civicrm/vendor/symfony/finder/Finder.php
+++ b/civicrm/vendor/symfony/finder/Finder.php
@@ -11,13 +11,8 @@
 
 namespace Symfony\Component\Finder;
 
-use Symfony\Component\Finder\Adapter\AdapterInterface;
-use Symfony\Component\Finder\Adapter\BsdFindAdapter;
-use Symfony\Component\Finder\Adapter\GnuFindAdapter;
-use Symfony\Component\Finder\Adapter\PhpAdapter;
 use Symfony\Component\Finder\Comparator\DateComparator;
 use Symfony\Component\Finder\Comparator\NumberComparator;
-use Symfony\Component\Finder\Exception\ExceptionInterface;
 use Symfony\Component\Finder\Iterator\CustomFilterIterator;
 use Symfony\Component\Finder\Iterator\DateRangeFilterIterator;
 use Symfony\Component\Finder\Iterator\DepthRangeFilterIterator;
@@ -34,7 +29,7 @@ use Symfony\Component\Finder\Iterator\SortableIterator;
  *
  * All rules may be invoked several times.
  *
- * All methods return the current Finder object to allow easy chaining:
+ * All methods return the current Finder object to allow chaining:
  *
  *     $finder = Finder::create()->files()->name('*.php')->in(__DIR__);
  *
@@ -46,26 +41,25 @@ class Finder implements \IteratorAggregate, \Countable
     const IGNORE_DOT_FILES = 2;
 
     private $mode = 0;
-    private $names = array();
-    private $notNames = array();
-    private $exclude = array();
-    private $filters = array();
-    private $depths = array();
-    private $sizes = array();
+    private $names = [];
+    private $notNames = [];
+    private $exclude = [];
+    private $filters = [];
+    private $depths = [];
+    private $sizes = [];
     private $followLinks = false;
     private $sort = false;
     private $ignore = 0;
-    private $dirs = array();
-    private $dates = array();
-    private $iterators = array();
-    private $contains = array();
-    private $notContains = array();
-    private $adapters = null;
-    private $paths = array();
-    private $notPaths = array();
+    private $dirs = [];
+    private $dates = [];
+    private $iterators = [];
+    private $contains = [];
+    private $notContains = [];
+    private $paths = [];
+    private $notPaths = [];
     private $ignoreUnreadableDirs = false;
 
-    private static $vcsPatterns = array('.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg');
+    private static $vcsPatterns = ['.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg'];
 
     public function __construct()
     {
@@ -82,110 +76,6 @@ class Finder implements \IteratorAggregate, \Countable
         return new static();
     }
 
-    /**
-     * Registers a finder engine implementation.
-     *
-     * @param AdapterInterface $adapter  An adapter instance
-     * @param int              $priority Highest is selected first
-     *
-     * @return $this
-     *
-     * @deprecated since 2.8, to be removed in 3.0.
-     */
-    public function addAdapter(AdapterInterface $adapter, $priority = 0)
-    {
-        @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-
-        $this->initDefaultAdapters();
-
-        $this->adapters[$adapter->getName()] = array(
-            'adapter' => $adapter,
-            'priority' => $priority,
-            'selected' => false,
-        );
-
-        return $this->sortAdapters();
-    }
-
-    /**
-     * Sets the selected adapter to the best one according to the current platform the code is run on.
-     *
-     * @return $this
-     *
-     * @deprecated since 2.8, to be removed in 3.0.
-     */
-    public function useBestAdapter()
-    {
-        @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-
-        $this->initDefaultAdapters();
-
-        $this->resetAdapterSelection();
-
-        return $this->sortAdapters();
-    }
-
-    /**
-     * Selects the adapter to use.
-     *
-     * @param string $name
-     *
-     * @return $this
-     *
-     * @throws \InvalidArgumentException
-     *
-     * @deprecated since 2.8, to be removed in 3.0.
-     */
-    public function setAdapter($name)
-    {
-        @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-
-        $this->initDefaultAdapters();
-
-        if (!isset($this->adapters[$name])) {
-            throw new \InvalidArgumentException(sprintf('Adapter "%s" does not exist.', $name));
-        }
-
-        $this->resetAdapterSelection();
-        $this->adapters[$name]['selected'] = true;
-
-        return $this->sortAdapters();
-    }
-
-    /**
-     * Removes all adapters registered in the finder.
-     *
-     * @return $this
-     *
-     * @deprecated since 2.8, to be removed in 3.0.
-     */
-    public function removeAdapters()
-    {
-        @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-
-        $this->adapters = array();
-
-        return $this;
-    }
-
-    /**
-     * Returns registered adapters ordered by priority without extra information.
-     *
-     * @return AdapterInterface[]
-     *
-     * @deprecated since 2.8, to be removed in 3.0.
-     */
-    public function getAdapters()
-    {
-        @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-
-        $this->initDefaultAdapters();
-
-        return array_values(array_map(function (array $adapter) {
-            return $adapter['adapter'];
-        }, $this->adapters));
-    }
-
     /**
      * Restricts the matching to directories only.
      *
@@ -638,7 +528,7 @@ class Finder implements \IteratorAggregate, \Countable
     /**
      * Searches files and directories which match defined rules.
      *
-     * @param string|array $dirs A directory path or an array of directories
+     * @param string|string[] $dirs A directory path or an array of directories
      *
      * @return $this
      *
@@ -646,13 +536,14 @@ class Finder implements \IteratorAggregate, \Countable
      */
     public function in($dirs)
     {
-        $resolvedDirs = array();
+        $resolvedDirs = [];
 
         foreach ((array) $dirs as $dir) {
             if (is_dir($dir)) {
                 $resolvedDirs[] = $this->normalizeDir($dir);
-            } elseif ($glob = glob($dir, (\defined('GLOB_BRACE') ? GLOB_BRACE : 0) | GLOB_ONLYDIR)) {
-                $resolvedDirs = array_merge($resolvedDirs, array_map(array($this, 'normalizeDir'), $glob));
+            } elseif ($glob = glob($dir, (\defined('GLOB_BRACE') ? GLOB_BRACE : 0) | GLOB_ONLYDIR | GLOB_NOSORT)) {
+                sort($glob);
+                $resolvedDirs = array_merge($resolvedDirs, array_map([$this, 'normalizeDir'], $glob));
             } else {
                 throw new \InvalidArgumentException(sprintf('The "%s" directory does not exist.', $dir));
             }
@@ -725,29 +616,27 @@ class Finder implements \IteratorAggregate, \Countable
     }
 
     /**
-     * Counts all the results collected by the iterators.
+     * Check if the any results were found.
      *
-     * @return int
+     * @return bool
      */
-    public function count()
+    public function hasResults()
     {
-        return iterator_count($this->getIterator());
+        foreach ($this->getIterator() as $_) {
+            return true;
+        }
+
+        return false;
     }
 
     /**
-     * @return $this
+     * Counts all the results collected by the iterators.
+     *
+     * @return int
      */
-    private function sortAdapters()
+    public function count()
     {
-        uasort($this->adapters, function (array $a, array $b) {
-            if ($a['selected'] || $b['selected']) {
-                return $a['selected'] ? -1 : 1;
-            }
-
-            return $a['priority'] > $b['priority'] ? -1 : 1;
-        });
-
-        return $this;
+        return iterator_count($this->getIterator());
     }
 
     /**
@@ -757,25 +646,15 @@ class Finder implements \IteratorAggregate, \Countable
      */
     private function searchInDirectory($dir)
     {
+        $exclude = $this->exclude;
+        $notPaths = $this->notPaths;
+
         if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) {
-            $this->exclude = array_merge($this->exclude, self::$vcsPatterns);
+            $exclude = array_merge($exclude, self::$vcsPatterns);
         }
 
         if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) {
-            $this->notPaths[] = '#(^|/)\..+(/|$)#';
-        }
-
-        if ($this->adapters) {
-            foreach ($this->adapters as $adapter) {
-                if ($adapter['adapter']->isSupported()) {
-                    try {
-                        return $this
-                            ->buildAdapter($adapter['adapter'])
-                            ->searchInDirectory($dir);
-                    } catch (ExceptionInterface $e) {
-                    }
-                }
-            }
+            $notPaths[] = '#(^|/)\..+(/|$)#';
         }
 
         $minDepth = 0;
@@ -808,8 +687,8 @@ class Finder implements \IteratorAggregate, \Countable
 
         $iterator = new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs);
 
-        if ($this->exclude) {
-            $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude);
+        if ($exclude) {
+            $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $exclude);
         }
 
         $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST);
@@ -842,8 +721,8 @@ class Finder implements \IteratorAggregate, \Countable
             $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
         }
 
-        if ($this->paths || $this->notPaths) {
-            $iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $this->notPaths);
+        if ($this->paths || $notPaths) {
+            $iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $notPaths);
         }
 
         if ($this->sort) {
@@ -854,63 +733,27 @@ class Finder implements \IteratorAggregate, \Countable
         return $iterator;
     }
 
-    /**
-     * @return AdapterInterface
-     */
-    private function buildAdapter(AdapterInterface $adapter)
-    {
-        return $adapter
-            ->setFollowLinks($this->followLinks)
-            ->setDepths($this->depths)
-            ->setMode($this->mode)
-            ->setExclude($this->exclude)
-            ->setNames($this->names)
-            ->setNotNames($this->notNames)
-            ->setContains($this->contains)
-            ->setNotContains($this->notContains)
-            ->setSizes($this->sizes)
-            ->setDates($this->dates)
-            ->setFilters($this->filters)
-            ->setSort($this->sort)
-            ->setPath($this->paths)
-            ->setNotPath($this->notPaths)
-            ->ignoreUnreadableDirs($this->ignoreUnreadableDirs);
-    }
-
-    /**
-     * Unselects all adapters.
-     */
-    private function resetAdapterSelection()
-    {
-        $this->adapters = array_map(function (array $properties) {
-            $properties['selected'] = false;
-
-            return $properties;
-        }, $this->adapters);
-    }
-
-    private function initDefaultAdapters()
-    {
-        if (null === $this->adapters) {
-            $this->adapters = array();
-            $this
-                ->addAdapter(new GnuFindAdapter())
-                ->addAdapter(new BsdFindAdapter())
-                ->addAdapter(new PhpAdapter(), -50)
-                ->setAdapter('php')
-            ;
-        }
-    }
-
     /**
      * Normalizes given directory names by removing trailing slashes.
      *
+     * Excluding: (s)ftp:// or ssh2.(s)ftp:// wrapper
+     *
      * @param string $dir
      *
      * @return string
      */
     private function normalizeDir($dir)
     {
-        return rtrim($dir, '/'.\DIRECTORY_SEPARATOR);
+        if ('/' === $dir) {
+            return $dir;
+        }
+
+        $dir = rtrim($dir, '/'.\DIRECTORY_SEPARATOR);
+
+        if (preg_match('#^(ssh2\.)?s?ftp://#', $dir)) {
+            $dir .= '/';
+        }
+
+        return $dir;
     }
 }
diff --git a/civicrm/vendor/symfony/finder/Glob.php b/civicrm/vendor/symfony/finder/Glob.php
index e2988f25768ececf60db1a57c573624c5c250d13..ea76d51ae0f9f33a8792ee4e28479d65e11b8401 100644
--- a/civicrm/vendor/symfony/finder/Glob.php
+++ b/civicrm/vendor/symfony/finder/Glob.php
@@ -18,7 +18,7 @@ namespace Symfony\Component\Finder;
  *
  *     // prints foo.bar and foo.baz
  *     $regex = glob_to_regex("foo.*");
- *     for (array('foo.bar', 'foo.baz', 'foo', 'bar') as $t)
+ *     for (['foo.bar', 'foo.baz', 'foo', 'bar'] as $t)
  *     {
  *         if (/$regex/) echo "matched: $car\n";
  *     }
@@ -54,16 +54,28 @@ class Glob
         $sizeGlob = \strlen($glob);
         for ($i = 0; $i < $sizeGlob; ++$i) {
             $car = $glob[$i];
-            if ($firstByte) {
-                if ($strictLeadingDot && '.' !== $car) {
-                    $regex .= '(?=[^\.])';
+            if ($firstByte && $strictLeadingDot && '.' !== $car) {
+                $regex .= '(?=[^\.])';
+            }
+
+            $firstByte = '/' === $car;
+
+            if ($firstByte && $strictWildcardSlash && isset($glob[$i + 2]) && '**' === $glob[$i + 1].$glob[$i + 2] && (!isset($glob[$i + 3]) || '/' === $glob[$i + 3])) {
+                $car = '[^/]++/';
+                if (!isset($glob[$i + 3])) {
+                    $car .= '?';
                 }
 
-                $firstByte = false;
-            }
+                if ($strictLeadingDot) {
+                    $car = '(?=[^\.])'.$car;
+                }
+
+                $car = '/(?:'.$car.')*';
+                $i += 2 + isset($glob[$i + 3]);
 
-            if ('/' === $car) {
-                $firstByte = true;
+                if ('/' === $delimiter) {
+                    $car = str_replace('/', '\\/', $car);
+                }
             }
 
             if ($delimiter === $car || '.' === $car || '(' === $car || ')' === $car || '|' === $car || '+' === $car || '^' === $car || '$' === $car) {
diff --git a/civicrm/vendor/symfony/finder/Iterator/CustomFilterIterator.php b/civicrm/vendor/symfony/finder/Iterator/CustomFilterIterator.php
index 6666e07ec54bba96f73bc46fd4d756bf2ac4e6ae..896f7e912f087563a55b7592a48a6123918b7285 100644
--- a/civicrm/vendor/symfony/finder/Iterator/CustomFilterIterator.php
+++ b/civicrm/vendor/symfony/finder/Iterator/CustomFilterIterator.php
@@ -21,7 +21,7 @@ namespace Symfony\Component\Finder\Iterator;
  */
 class CustomFilterIterator extends FilterIterator
 {
-    private $filters = array();
+    private $filters = [];
 
     /**
      * @param \Iterator  $iterator The Iterator to filter
diff --git a/civicrm/vendor/symfony/finder/Iterator/DateRangeFilterIterator.php b/civicrm/vendor/symfony/finder/Iterator/DateRangeFilterIterator.php
index b01e5e3f5d8857c4e9a43b7b3afcac9aa6d3ce33..8a47321a3c3709911149637621fab481241b6163 100644
--- a/civicrm/vendor/symfony/finder/Iterator/DateRangeFilterIterator.php
+++ b/civicrm/vendor/symfony/finder/Iterator/DateRangeFilterIterator.php
@@ -20,7 +20,7 @@ use Symfony\Component\Finder\Comparator\DateComparator;
  */
 class DateRangeFilterIterator extends FilterIterator
 {
-    private $comparators = array();
+    private $comparators = [];
 
     /**
      * @param \Iterator        $iterator    The Iterator to filter
diff --git a/civicrm/vendor/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php b/civicrm/vendor/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php
index c57128c278492d8526539bc1442d4f96aac680c7..60bc4e814c9d287d22fca01c8222dfd971f900fd 100644
--- a/civicrm/vendor/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php
+++ b/civicrm/vendor/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php
@@ -20,18 +20,18 @@ class ExcludeDirectoryFilterIterator extends FilterIterator implements \Recursiv
 {
     private $iterator;
     private $isRecursive;
-    private $excludedDirs = array();
+    private $excludedDirs = [];
     private $excludedPattern;
 
     /**
      * @param \Iterator $iterator    The Iterator to filter
-     * @param array     $directories An array of directories to exclude
+     * @param string[]  $directories An array of directories to exclude
      */
     public function __construct(\Iterator $iterator, array $directories)
     {
         $this->iterator = $iterator;
         $this->isRecursive = $iterator instanceof \RecursiveIterator;
-        $patterns = array();
+        $patterns = [];
         foreach ($directories as $directory) {
             $directory = rtrim($directory, '/');
             if (!$this->isRecursive || false !== strpos($directory, '/')) {
@@ -75,7 +75,7 @@ class ExcludeDirectoryFilterIterator extends FilterIterator implements \Recursiv
 
     public function getChildren()
     {
-        $children = new self($this->iterator->getChildren(), array());
+        $children = new self($this->iterator->getChildren(), []);
         $children->excludedDirs = $this->excludedDirs;
         $children->excludedPattern = $this->excludedPattern;
 
diff --git a/civicrm/vendor/symfony/finder/Iterator/FilePathsIterator.php b/civicrm/vendor/symfony/finder/Iterator/FilePathsIterator.php
deleted file mode 100644
index 9c4e5c4aab28fcb246abe680ad0bef668ca699cc..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/finder/Iterator/FilePathsIterator.php
+++ /dev/null
@@ -1,135 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Finder\Iterator;
-
-@trigger_error('The '.__NAMESPACE__.'\FilePathsIterator class is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-
-use Symfony\Component\Finder\SplFileInfo;
-
-/**
- * Iterate over shell command result.
- *
- * @author Jean-François Simon <contact@jfsimon.fr>
- *
- * @deprecated since 2.8, to be removed in 3.0.
- */
-class FilePathsIterator extends \ArrayIterator
-{
-    /**
-     * @var string
-     */
-    private $baseDir;
-
-    /**
-     * @var int
-     */
-    private $baseDirLength;
-
-    /**
-     * @var string
-     */
-    private $subPath;
-
-    /**
-     * @var string
-     */
-    private $subPathname;
-
-    /**
-     * @var SplFileInfo
-     */
-    private $current;
-
-    /**
-     * @param array  $paths   List of paths returned by shell command
-     * @param string $baseDir Base dir for relative path building
-     */
-    public function __construct(array $paths, $baseDir)
-    {
-        $this->baseDir = $baseDir;
-        $this->baseDirLength = \strlen($baseDir);
-
-        parent::__construct($paths);
-    }
-
-    /**
-     * @param string $name
-     * @param array  $arguments
-     *
-     * @return mixed
-     */
-    public function __call($name, array $arguments)
-    {
-        return \call_user_func_array(array($this->current(), $name), $arguments);
-    }
-
-    /**
-     * Return an instance of SplFileInfo with support for relative paths.
-     *
-     * @return SplFileInfo File information
-     */
-    public function current()
-    {
-        return $this->current;
-    }
-
-    /**
-     * @return string
-     */
-    public function key()
-    {
-        return $this->current->getPathname();
-    }
-
-    public function next()
-    {
-        parent::next();
-        $this->buildProperties();
-    }
-
-    public function rewind()
-    {
-        parent::rewind();
-        $this->buildProperties();
-    }
-
-    /**
-     * @return string
-     */
-    public function getSubPath()
-    {
-        return $this->subPath;
-    }
-
-    /**
-     * @return string
-     */
-    public function getSubPathname()
-    {
-        return $this->subPathname;
-    }
-
-    private function buildProperties()
-    {
-        $absolutePath = parent::current();
-
-        if ($this->baseDir === substr($absolutePath, 0, $this->baseDirLength)) {
-            $this->subPathname = ltrim(substr($absolutePath, $this->baseDirLength), '/\\');
-            $dir = \dirname($this->subPathname);
-            $this->subPath = '.' === $dir ? '' : $dir;
-        } else {
-            $this->subPath = $this->subPathname = '';
-        }
-
-        $this->current = new SplFileInfo(parent::current(), $this->subPath, $this->subPathname);
-    }
-}
diff --git a/civicrm/vendor/symfony/finder/Iterator/FilterIterator.php b/civicrm/vendor/symfony/finder/Iterator/FilterIterator.php
index acb98361c96430c86a8528d0997479b40130ee44..c16dd8fa9e4a0b15fb2e4e57c8fda9401ffa62b8 100644
--- a/civicrm/vendor/symfony/finder/Iterator/FilterIterator.php
+++ b/civicrm/vendor/symfony/finder/Iterator/FilterIterator.php
@@ -18,6 +18,8 @@ namespace Symfony\Component\Finder\Iterator;
  * @see https://bugs.php.net/68557
  *
  * @author Alex Bogomazov
+ *
+ * @deprecated since 3.4, to be removed in 4.0.
  */
 abstract class FilterIterator extends \FilterIterator
 {
diff --git a/civicrm/vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.php b/civicrm/vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.php
index fc8854047e3422953a85fbbdd8a08674f3d0032f..ee365a58b084f60e9f6311075cb529b41d4ffa8f 100644
--- a/civicrm/vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.php
+++ b/civicrm/vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.php
@@ -18,8 +18,8 @@ namespace Symfony\Component\Finder\Iterator;
  */
 abstract class MultiplePcreFilterIterator extends FilterIterator
 {
-    protected $matchRegexps = array();
-    protected $noMatchRegexps = array();
+    protected $matchRegexps = [];
+    protected $noMatchRegexps = [];
 
     /**
      * @param \Iterator $iterator        The Iterator to filter
@@ -91,7 +91,7 @@ abstract class MultiplePcreFilterIterator extends FilterIterator
                 return !preg_match('/[*?[:alnum:] \\\\]/', $start);
             }
 
-            foreach (array(array('{', '}'), array('(', ')'), array('[', ']'), array('<', '>')) as $delimiters) {
+            foreach ([['{', '}'], ['(', ')'], ['[', ']'], ['<', '>']] as $delimiters) {
                 if ($start === $delimiters[0] && $end === $delimiters[1]) {
                     return true;
                 }
diff --git a/civicrm/vendor/symfony/finder/Iterator/RecursiveDirectoryIterator.php b/civicrm/vendor/symfony/finder/Iterator/RecursiveDirectoryIterator.php
index cb2964f8f29f4d11233e2c1531e8e0f6bfdce10b..63764d407d30fc669a7ab2815db77087a3a571ce 100644
--- a/civicrm/vendor/symfony/finder/Iterator/RecursiveDirectoryIterator.php
+++ b/civicrm/vendor/symfony/finder/Iterator/RecursiveDirectoryIterator.php
@@ -51,7 +51,7 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
 
         parent::__construct($path, $flags);
         $this->ignoreUnreadableDirs = $ignoreUnreadableDirs;
-        $this->rootPath = (string) $path;
+        $this->rootPath = $path;
         if ('/' !== \DIRECTORY_SEPARATOR && !($flags & self::UNIX_PATHS)) {
             $this->directorySeparator = \DIRECTORY_SEPARATOR;
         }
@@ -74,7 +74,11 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
         }
         $subPathname .= $this->getFilename();
 
-        return new SplFileInfo($this->rootPath.$this->directorySeparator.$subPathname, $this->subPath, $subPathname);
+        if ('/' !== $basePath = $this->rootPath) {
+            $basePath .= $this->directorySeparator;
+        }
+
+        return new SplFileInfo($basePath.$subPathname, $this->subPath, $subPathname);
     }
 
     /**
@@ -100,7 +104,7 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
         } catch (\UnexpectedValueException $e) {
             if ($this->ignoreUnreadableDirs) {
                 // If directory is unreadable and finder is set to ignore it, a fake empty content is returned.
-                return new \RecursiveArrayIterator(array());
+                return new \RecursiveArrayIterator([]);
             } else {
                 throw new AccessDeniedException($e->getMessage(), $e->getCode(), $e);
             }
diff --git a/civicrm/vendor/symfony/finder/Iterator/SizeRangeFilterIterator.php b/civicrm/vendor/symfony/finder/Iterator/SizeRangeFilterIterator.php
index bd1a7fb7004819273329563388d9f1eff3306b61..4e521c8c9040c9fb90fc94344194b4c82ff02f09 100644
--- a/civicrm/vendor/symfony/finder/Iterator/SizeRangeFilterIterator.php
+++ b/civicrm/vendor/symfony/finder/Iterator/SizeRangeFilterIterator.php
@@ -20,7 +20,7 @@ use Symfony\Component\Finder\Comparator\NumberComparator;
  */
 class SizeRangeFilterIterator extends FilterIterator
 {
-    private $comparators = array();
+    private $comparators = [];
 
     /**
      * @param \Iterator          $iterator    The Iterator to filter
diff --git a/civicrm/vendor/symfony/finder/Iterator/SortableIterator.php b/civicrm/vendor/symfony/finder/Iterator/SortableIterator.php
index 53f8e31c6c4291db03ce77281bdccd416ccaef79..e67997d117a933e7e5906168da359afc74ff9173 100644
--- a/civicrm/vendor/symfony/finder/Iterator/SortableIterator.php
+++ b/civicrm/vendor/symfony/finder/Iterator/SortableIterator.php
@@ -38,29 +38,29 @@ class SortableIterator implements \IteratorAggregate
         $this->iterator = $iterator;
 
         if (self::SORT_BY_NAME === $sort) {
-            $this->sort = function ($a, $b) {
-                return strcmp($a->getRealpath() ?: $a->getPathname(), $b->getRealpath() ?: $b->getPathname());
+            $this->sort = static function ($a, $b) {
+                return strcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname());
             };
         } elseif (self::SORT_BY_TYPE === $sort) {
-            $this->sort = function ($a, $b) {
+            $this->sort = static function ($a, $b) {
                 if ($a->isDir() && $b->isFile()) {
                     return -1;
                 } elseif ($a->isFile() && $b->isDir()) {
                     return 1;
                 }
 
-                return strcmp($a->getRealpath() ?: $a->getPathname(), $b->getRealpath() ?: $b->getPathname());
+                return strcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname());
             };
         } elseif (self::SORT_BY_ACCESSED_TIME === $sort) {
-            $this->sort = function ($a, $b) {
+            $this->sort = static function ($a, $b) {
                 return $a->getATime() - $b->getATime();
             };
         } elseif (self::SORT_BY_CHANGED_TIME === $sort) {
-            $this->sort = function ($a, $b) {
+            $this->sort = static function ($a, $b) {
                 return $a->getCTime() - $b->getCTime();
             };
         } elseif (self::SORT_BY_MODIFIED_TIME === $sort) {
-            $this->sort = function ($a, $b) {
+            $this->sort = static function ($a, $b) {
                 return $a->getMTime() - $b->getMTime();
             };
         } elseif (\is_callable($sort)) {
diff --git a/civicrm/vendor/symfony/finder/LICENSE b/civicrm/vendor/symfony/finder/LICENSE
index 21d7fb9e2f29b50caca3a76f0647e94e2cc8ddc1..9e936ec0448b8549e5edf08e5ac5f01491a8bfc8 100644
--- a/civicrm/vendor/symfony/finder/LICENSE
+++ b/civicrm/vendor/symfony/finder/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2018 Fabien Potencier
+Copyright (c) 2004-2020 Fabien Potencier
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/civicrm/vendor/symfony/finder/Shell/Command.php b/civicrm/vendor/symfony/finder/Shell/Command.php
deleted file mode 100644
index 8219078723476295be584fc3f278924c2f91ccaf..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/finder/Shell/Command.php
+++ /dev/null
@@ -1,278 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Finder\Shell;
-
-@trigger_error('The '.__NAMESPACE__.'\Command class is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-
-/**
- * @author Jean-François Simon <contact@jfsimon.fr>
- *
- * @deprecated since 2.8, to be removed in 3.0.
- */
-class Command
-{
-    private $parent;
-    private $bits = array();
-    private $labels = array();
-
-    /**
-     * @var \Closure|null
-     */
-    private $errorHandler;
-
-    public function __construct(Command $parent = null)
-    {
-        $this->parent = $parent;
-    }
-
-    /**
-     * Returns command as string.
-     *
-     * @return string
-     */
-    public function __toString()
-    {
-        return $this->join();
-    }
-
-    /**
-     * Creates a new Command instance.
-     *
-     * @return self
-     */
-    public static function create(Command $parent = null)
-    {
-        return new self($parent);
-    }
-
-    /**
-     * Escapes special chars from input.
-     *
-     * @param string $input A string to escape
-     *
-     * @return string The escaped string
-     */
-    public static function escape($input)
-    {
-        return escapeshellcmd($input);
-    }
-
-    /**
-     * Quotes input.
-     *
-     * @param string $input An argument string
-     *
-     * @return string The quoted string
-     */
-    public static function quote($input)
-    {
-        return escapeshellarg($input);
-    }
-
-    /**
-     * Appends a string or a Command instance.
-     *
-     * @param string|Command $bit
-     *
-     * @return $this
-     */
-    public function add($bit)
-    {
-        $this->bits[] = $bit;
-
-        return $this;
-    }
-
-    /**
-     * Prepends a string or a command instance.
-     *
-     * @param string|Command $bit
-     *
-     * @return $this
-     */
-    public function top($bit)
-    {
-        array_unshift($this->bits, $bit);
-
-        foreach ($this->labels as $label => $index) {
-            ++$this->labels[$label];
-        }
-
-        return $this;
-    }
-
-    /**
-     * Appends an argument, will be quoted.
-     *
-     * @param string $arg
-     *
-     * @return $this
-     */
-    public function arg($arg)
-    {
-        $this->bits[] = self::quote($arg);
-
-        return $this;
-    }
-
-    /**
-     * Appends escaped special command chars.
-     *
-     * @param string $esc
-     *
-     * @return $this
-     */
-    public function cmd($esc)
-    {
-        $this->bits[] = self::escape($esc);
-
-        return $this;
-    }
-
-    /**
-     * Inserts a labeled command to feed later.
-     *
-     * @param string $label The unique label
-     *
-     * @return self|string
-     *
-     * @throws \RuntimeException If label already exists
-     */
-    public function ins($label)
-    {
-        if (isset($this->labels[$label])) {
-            throw new \RuntimeException(sprintf('Label "%s" already exists.', $label));
-        }
-
-        $this->bits[] = self::create($this);
-        $this->labels[$label] = \count($this->bits) - 1;
-
-        return $this->bits[$this->labels[$label]];
-    }
-
-    /**
-     * Retrieves a previously labeled command.
-     *
-     * @param string $label
-     *
-     * @return self|string
-     *
-     * @throws \RuntimeException
-     */
-    public function get($label)
-    {
-        if (!isset($this->labels[$label])) {
-            throw new \RuntimeException(sprintf('Label "%s" does not exist.', $label));
-        }
-
-        return $this->bits[$this->labels[$label]];
-    }
-
-    /**
-     * Returns parent command (if any).
-     *
-     * @return self
-     *
-     * @throws \RuntimeException If command has no parent
-     */
-    public function end()
-    {
-        if (null === $this->parent) {
-            throw new \RuntimeException('Calling end on root command doesn\'t make sense.');
-        }
-
-        return $this->parent;
-    }
-
-    /**
-     * Counts bits stored in command.
-     *
-     * @return int The bits count
-     */
-    public function length()
-    {
-        return \count($this->bits);
-    }
-
-    /**
-     * @return $this
-     */
-    public function setErrorHandler(\Closure $errorHandler)
-    {
-        $this->errorHandler = $errorHandler;
-
-        return $this;
-    }
-
-    /**
-     * @return \Closure|null
-     */
-    public function getErrorHandler()
-    {
-        return $this->errorHandler;
-    }
-
-    /**
-     * Executes current command.
-     *
-     * @return array The command result
-     *
-     * @throws \RuntimeException
-     */
-    public function execute()
-    {
-        if (null === $errorHandler = $this->errorHandler) {
-            exec($this->join(), $output);
-        } else {
-            $process = proc_open($this->join(), array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w')), $pipes);
-            $output = preg_split('~(\r\n|\r|\n)~', stream_get_contents($pipes[1]), -1, PREG_SPLIT_NO_EMPTY);
-
-            if ($error = stream_get_contents($pipes[2])) {
-                $errorHandler($error);
-            }
-
-            proc_close($process);
-        }
-
-        return $output ?: array();
-    }
-
-    /**
-     * Joins bits.
-     *
-     * @return string
-     */
-    public function join()
-    {
-        return implode(' ', array_filter(
-            array_map(function ($bit) {
-                return $bit instanceof Command ? $bit->join() : ($bit ?: null);
-            }, $this->bits),
-            function ($bit) { return null !== $bit; }
-        ));
-    }
-
-    /**
-     * Insert a string or a Command instance before the bit at given position $index (index starts from 0).
-     *
-     * @param string|Command $bit
-     * @param int            $index
-     *
-     * @return $this
-     */
-    public function addAtIndex($bit, $index)
-    {
-        array_splice($this->bits, $index, 0, $bit instanceof self ? array($bit) : $bit);
-
-        return $this;
-    }
-}
diff --git a/civicrm/vendor/symfony/finder/Shell/Shell.php b/civicrm/vendor/symfony/finder/Shell/Shell.php
deleted file mode 100644
index fa6ca4b9403fcbffed290d18fd34bfcf4a96a0a2..0000000000000000000000000000000000000000
--- a/civicrm/vendor/symfony/finder/Shell/Shell.php
+++ /dev/null
@@ -1,101 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Finder\Shell;
-
-@trigger_error('The '.__NAMESPACE__.'\Shell class is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
-
-/**
- * @author Jean-François Simon <contact@jfsimon.fr>
- *
- * @deprecated since 2.8, to be removed in 3.0.
- */
-class Shell
-{
-    const TYPE_UNIX = 1;
-    const TYPE_DARWIN = 2;
-    const TYPE_CYGWIN = 3;
-    const TYPE_WINDOWS = 4;
-    const TYPE_BSD = 5;
-
-    /**
-     * @var string|null
-     */
-    private $type;
-
-    /**
-     * Returns guessed OS type.
-     *
-     * @return int
-     */
-    public function getType()
-    {
-        if (null === $this->type) {
-            $this->type = $this->guessType();
-        }
-
-        return $this->type;
-    }
-
-    /**
-     * Tests if a command is available.
-     *
-     * @param string $command
-     *
-     * @return bool
-     */
-    public function testCommand($command)
-    {
-        if (!\function_exists('exec')) {
-            return false;
-        }
-
-        // todo: find a better way (command could not be available)
-        $testCommand = 'which ';
-        if (self::TYPE_WINDOWS === $this->type) {
-            $testCommand = 'where ';
-        }
-
-        $command = escapeshellcmd($command);
-
-        exec($testCommand.$command, $output, $code);
-
-        return 0 === $code && \count($output) > 0;
-    }
-
-    /**
-     * Guesses OS type.
-     *
-     * @return int
-     */
-    private function guessType()
-    {
-        $os = strtolower(PHP_OS);
-
-        if (false !== strpos($os, 'cygwin')) {
-            return self::TYPE_CYGWIN;
-        }
-
-        if (false !== strpos($os, 'darwin')) {
-            return self::TYPE_DARWIN;
-        }
-
-        if (false !== strpos($os, 'bsd')) {
-            return self::TYPE_BSD;
-        }
-
-        if (0 === strpos($os, 'win')) {
-            return self::TYPE_WINDOWS;
-        }
-
-        return self::TYPE_UNIX;
-    }
-}
diff --git a/civicrm/vendor/symfony/finder/composer.json b/civicrm/vendor/symfony/finder/composer.json
index 53dc98a9b27995398a0bf4fae542790a887e3f81..de19826f73b311cb2add5e53faa1f62c615d82c4 100644
--- a/civicrm/vendor/symfony/finder/composer.json
+++ b/civicrm/vendor/symfony/finder/composer.json
@@ -16,7 +16,7 @@
         }
     ],
     "require": {
-        "php": ">=5.3.9"
+        "php": "^5.5.9|>=7.0.8"
     },
     "autoload": {
         "psr-4": { "Symfony\\Component\\Finder\\": "" },
@@ -27,7 +27,7 @@
     "minimum-stability": "dev",
     "extra": {
         "branch-alias": {
-            "dev-master": "2.8-dev"
+            "dev-master": "3.4-dev"
         }
     }
 }
diff --git a/civicrm/vendor/symfony/polyfill-ctype/bootstrap.php b/civicrm/vendor/symfony/polyfill-ctype/bootstrap.php
index 14d1d0faa37151ccb47e6067f6d3e96b8207db48..8d6fc4beccaf4a1ba46a30fe0847b76be08a1c15 100644
--- a/civicrm/vendor/symfony/polyfill-ctype/bootstrap.php
+++ b/civicrm/vendor/symfony/polyfill-ctype/bootstrap.php
@@ -13,14 +13,34 @@ use Symfony\Polyfill\Ctype as p;
 
 if (!function_exists('ctype_alnum')) {
     function ctype_alnum($text) { return p\Ctype::ctype_alnum($text); }
+}
+if (!function_exists('ctype_alpha')) {
     function ctype_alpha($text) { return p\Ctype::ctype_alpha($text); }
+}
+if (!function_exists('ctype_cntrl')) {
     function ctype_cntrl($text) { return p\Ctype::ctype_cntrl($text); }
+}
+if (!function_exists('ctype_digit')) {
     function ctype_digit($text) { return p\Ctype::ctype_digit($text); }
+}
+if (!function_exists('ctype_graph')) {
     function ctype_graph($text) { return p\Ctype::ctype_graph($text); }
+}
+if (!function_exists('ctype_lower')) {
     function ctype_lower($text) { return p\Ctype::ctype_lower($text); }
+}
+if (!function_exists('ctype_print')) {
     function ctype_print($text) { return p\Ctype::ctype_print($text); }
+}
+if (!function_exists('ctype_punct')) {
     function ctype_punct($text) { return p\Ctype::ctype_punct($text); }
+}
+if (!function_exists('ctype_space')) {
     function ctype_space($text) { return p\Ctype::ctype_space($text); }
+}
+if (!function_exists('ctype_upper')) {
     function ctype_upper($text) { return p\Ctype::ctype_upper($text); }
+}
+if (!function_exists('ctype_xdigit')) {
     function ctype_xdigit($text) { return p\Ctype::ctype_xdigit($text); }
 }
diff --git a/civicrm/vendor/symfony/polyfill-ctype/composer.json b/civicrm/vendor/symfony/polyfill-ctype/composer.json
index 090f923ef1dcb151a058806d94a8c658e4889136..afb2a443e903379c696996c2cc5c11d6fb911d1b 100644
--- a/civicrm/vendor/symfony/polyfill-ctype/composer.json
+++ b/civicrm/vendor/symfony/polyfill-ctype/composer.json
@@ -28,7 +28,7 @@
     "minimum-stability": "dev",
     "extra": {
         "branch-alias": {
-            "dev-master": "1.12-dev"
+            "dev-master": "1.17-dev"
         }
     }
 }
diff --git a/civicrm/vendor/symfony/polyfill-iconv/README.md b/civicrm/vendor/symfony/polyfill-iconv/README.md
index 2421831c6b80cea0ea85e27b77cc853fbb603c09..b0c8984cd6d85441d6a7da75156b33abad48f2f1 100644
--- a/civicrm/vendor/symfony/polyfill-iconv/README.md
+++ b/civicrm/vendor/symfony/polyfill-iconv/README.md
@@ -2,8 +2,8 @@ Symfony Polyfill / Iconv
 ========================
 
 This component provides a native PHP implementation of the
-[php.net/iconv](http://php.net/iconv) functions
-(short of [`ob_iconv_handler`](http://php.net/manual/en/function.ob-iconv-handler.php)).
+[php.net/iconv](https://php.net/iconv) functions
+(short of [`ob_iconv_handler`](https://php.net/ob-iconv-handler)).
 
 More information can be found in the
 [main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md).
diff --git a/civicrm/vendor/symfony/polyfill-iconv/Resources/charset/translit.php b/civicrm/vendor/symfony/polyfill-iconv/Resources/charset/translit.php
index 829ea127595175645282934f798c4f5c16098ede..ed59858dc4ee1bb7b94ef4c5f1ab633722e795ce 100644
--- a/civicrm/vendor/symfony/polyfill-iconv/Resources/charset/translit.php
+++ b/civicrm/vendor/symfony/polyfill-iconv/Resources/charset/translit.php
@@ -866,6 +866,7 @@ static $data = array (
   '㋼' => '(ヰ)',
   '㋽' => '(ヱ)',
   '㋾' => '(ヲ)',
+  '㋿' => '令和',
   '㌀' => 'アパート',
   '㌁' => 'アルファ',
   '㌂' => 'アンペア',
@@ -3133,6 +3134,7 @@ static $data = array (
   '🈸' => '申',
   '🈹' => '割',
   '🈺' => '営',
+  '🈻' => '配',
   '🉀' => '〔本〕',
   '🉁' => '〔三〕',
   '🉂' => '〔二〕',
@@ -3878,6 +3880,104 @@ static $data = array (
   'ỽ' => 'v',
   'Ỿ' => 'Y',
   'ỿ' => 'y',
+  'â± ' => 'L',
+  'ⱡ' => 'l',
+  'â±¢' => 'L',
+  'â±£' => 'P',
+  'Ɽ' => 'R',
+  'â±¥' => 'a',
+  'ⱦ' => 't',
+  'Ⱨ' => 'H',
+  'ⱨ' => 'h',
+  'Ⱪ' => 'K',
+  'ⱪ' => 'k',
+  'Ⱬ' => 'Z',
+  'ⱬ' => 'z',
+  'â±®' => 'M',
+  'â±±' => 'v',
+  'â±²' => 'W',
+  'â±³' => 'w',
+  'â±´' => 'v',
+  'ⱸ' => 'e',
+  'ⱺ' => 'o',
+  'â±¾' => 'S',
+  'Ɀ' => 'Z',
+  'ꜰ' => 'F',
+  'ꜱ' => 'S',
+  'Ꜳ' => 'AA',
+  'ꜳ' => 'aa',
+  'Ꜵ' => 'AO',
+  'ꜵ' => 'ao',
+  'Ꜷ' => 'AU',
+  'ꜷ' => 'au',
+  'Ꜹ' => 'AV',
+  'ꜹ' => 'av',
+  'Ꜻ' => 'AV',
+  'ꜻ' => 'av',
+  'Ꜽ' => 'AY',
+  'ꜽ' => 'ay',
+  'Ꝁ' => 'K',
+  'ꝁ' => 'k',
+  'Ꝃ' => 'K',
+  'ꝃ' => 'k',
+  'Ꝅ' => 'K',
+  'ꝅ' => 'k',
+  'Ꝇ' => 'L',
+  'ꝇ' => 'l',
+  'Ꝉ' => 'L',
+  'ꝉ' => 'l',
+  'Ꝋ' => 'O',
+  'ꝋ' => 'o',
+  'Ꝍ' => 'O',
+  'ꝍ' => 'o',
+  'Ꝏ' => 'OO',
+  'ꝏ' => 'oo',
+  'Ꝑ' => 'P',
+  'ꝑ' => 'p',
+  'Ꝓ' => 'P',
+  'ꝓ' => 'p',
+  'Ꝕ' => 'P',
+  'ꝕ' => 'p',
+  'Ꝗ' => 'Q',
+  'ꝗ' => 'q',
+  'Ꝙ' => 'Q',
+  'ꝙ' => 'q',
+  'Ꝟ' => 'V',
+  'ꝟ' => 'v',
+  'Ꝡ' => 'VY',
+  'ꝡ' => 'vy',
+  'Ꝥ' => 'TH',
+  'ꝥ' => 'th',
+  'Ꝧ' => 'TH',
+  'ꝧ' => 'th',
+  'ꝱ' => 'd',
+  'ꝲ' => 'l',
+  'ꝳ' => 'm',
+  'ꝴ' => 'n',
+  'ꝵ' => 'r',
+  'ꝶ' => 'R',
+  'ꝷ' => 't',
+  'Ꝺ' => 'D',
+  'ꝺ' => 'd',
+  'Ꝼ' => 'F',
+  'ꝼ' => 'f',
+  'Ꞇ' => 'T',
+  'ꞇ' => 't',
+  'Ꞑ' => 'N',
+  'êž‘' => 'n',
+  'êž’' => 'C',
+  'êž“' => 'c',
+  'êž ' => 'G',
+  'êž¡' => 'g',
+  'Ꞣ' => 'K',
+  'ꞣ' => 'k',
+  'Ꞥ' => 'N',
+  'ꞥ' => 'n',
+  'Ꞧ' => 'R',
+  'ꞧ' => 'r',
+  'Ꞩ' => 'S',
+  'êž©' => 's',
+  'Ɦ' => 'H',
   '©' => '(C)',
   '®' => '(R)',
   'â‚ ' => 'CE',
@@ -3887,8 +3987,28 @@ static $data = array (
   '₧' => 'Pts',
   '₺' => 'TL',
   '₹' => 'Rs',
+  'â„—' => '(P)',
+  '℘' => 'P',
   'â„ž' => 'Rx',
   '〇' => '0',
+  ' ' => ' ',
+  ' ' => ' ',
+  ' ' => ' ',
+  ' ' => ' ',
+  ' ' => ' ',
+  ' ' => ' ',
+  ' ' => ' ',
+  ' ' => ' ',
+  ' ' => ' ',
+  ' ' => ' ',
+  ' ' => ' ',
+  'ʹ' => '\'',
+  'ʺ' => '"',
+  'Ê»' => '\'',
+  'ʼ' => '\'',
+  'ʽ' => '\'',
+  'ˈ' => '\'',
+  'Ë‹' => '`',
   '‘' => '\'',
   '’' => '\'',
   '‚' => ',',
@@ -3904,6 +4024,7 @@ static $data = array (
   '»' => '>>',
   '‹' => '<',
   '›' => '>',
+  '­' => '-',
   '‐' => '-',
   '‑' => '-',
   '‒' => '-',
@@ -3912,6 +4033,12 @@ static $data = array (
   '―' => '-',
   '︱' => '-',
   '︲' => '-',
+  'Ë‚' => '<',
+  '˃' => '>',
+  'Ë„' => '^',
+  'ˆ' => '^',
+  'ː' => ':',
+  '˜' => '~',
   '‖' => '||',
   '⁄' => '/',
   '⁅' => '[',
@@ -3952,6 +4079,8 @@ static $data = array (
   '﹈' => ']',
   '×' => '*',
   '÷' => '/',
+  'Ë–' => '+',
+  'Ë—' => '-',
   '−' => '-',
   '∕' => '/',
   '∖' => '\\',
diff --git a/civicrm/vendor/symfony/polyfill-iconv/bootstrap.php b/civicrm/vendor/symfony/polyfill-iconv/bootstrap.php
index 52747329d19bbd00debbd11bdbaf35da7c810338..14b5003a8bdc97b8487595e8a33badc72f6c6e9b 100644
--- a/civicrm/vendor/symfony/polyfill-iconv/bootstrap.php
+++ b/civicrm/vendor/symfony/polyfill-iconv/bootstrap.php
@@ -11,34 +11,74 @@
 
 use Symfony\Polyfill\Iconv as p;
 
-if (!function_exists('iconv')) {
+if (extension_loaded('iconv')) {
+    return;
+}
+
+if (!defined('ICONV_IMPL')) {
     define('ICONV_IMPL', 'Symfony');
+}
+if (!defined('ICONV_VERSION')) {
     define('ICONV_VERSION', '1.0');
+}
+if (!defined('ICONV_MIME_DECODE_STRICT')) {
     define('ICONV_MIME_DECODE_STRICT', 1);
+}
+if (!defined('ICONV_MIME_DECODE_CONTINUE_ON_ERROR')) {
     define('ICONV_MIME_DECODE_CONTINUE_ON_ERROR', 2);
+}
 
+if (!function_exists('iconv')) {
     function iconv($from, $to, $s) { return p\Iconv::iconv($from, $to, $s); }
+}
+if (!function_exists('iconv_get_encoding')) {
     function iconv_get_encoding($type = 'all') { return p\Iconv::iconv_get_encoding($type); }
+}
+if (!function_exists('iconv_set_encoding')) {
     function iconv_set_encoding($type, $charset) { return p\Iconv::iconv_set_encoding($type, $charset); }
+}
+if (!function_exists('iconv_mime_encode')) {
     function iconv_mime_encode($name, $value, $pref = null) { return p\Iconv::iconv_mime_encode($name, $value, $pref); }
+}
+if (!function_exists('iconv_mime_decode_headers')) {
     function iconv_mime_decode_headers($encodedHeaders, $mode = 0, $enc = null) { return p\Iconv::iconv_mime_decode_headers($encodedHeaders, $mode, $enc); }
+}
 
-    if (extension_loaded('mbstring')) {
+if (extension_loaded('mbstring')) {
+    if (!function_exists('iconv_strlen')) {
         function iconv_strlen($s, $enc = null) { null === $enc and $enc = p\Iconv::$internalEncoding; return mb_strlen($s, $enc); }
+    }
+    if (!function_exists('iconv_strpos')) {
         function iconv_strpos($s, $needle, $offset = 0, $enc = null) { null === $enc and $enc = p\Iconv::$internalEncoding; return mb_strpos($s, $needle, $offset, $enc); }
+    }
+    if (!function_exists('iconv_strrpos')) {
         function iconv_strrpos($s, $needle, $enc = null) { null === $enc and $enc = p\Iconv::$internalEncoding; return mb_strrpos($s, $needle, 0, $enc); }
+    }
+    if (!function_exists('iconv_substr')) {
         function iconv_substr($s, $start, $length = 2147483647, $enc = null) { null === $enc and $enc = p\Iconv::$internalEncoding; return mb_substr($s, $start, $length, $enc); }
+    }
+    if (!function_exists('iconv_mime_decode')) {
         function iconv_mime_decode($encodedHeaders, $mode = 0, $enc = null) { null === $enc and $enc = p\Iconv::$internalEncoding; return mb_decode_mimeheader($encodedHeaders, $mode, $enc); }
-    } else {
+    }
+} else {
+    if (!function_exists('iconv_strlen')) {
         if (extension_loaded('xml')) {
             function iconv_strlen($s, $enc = null) { return p\Iconv::strlen1($s, $enc); }
         } else {
             function iconv_strlen($s, $enc = null) { return p\Iconv::strlen2($s, $enc); }
         }
+    }
 
+    if (!function_exists('iconv_strpos')) {
         function iconv_strpos($s, $needle, $offset = 0, $enc = null) { return p\Iconv::iconv_strpos($s, $needle, $offset, $enc); }
+    }
+    if (!function_exists('iconv_strrpos')) {
         function iconv_strrpos($s, $needle, $enc = null) { return p\Iconv::iconv_strrpos($s, $needle, $enc); }
+    }
+    if (!function_exists('iconv_substr')) {
         function iconv_substr($s, $start, $length = 2147483647, $enc = null) { return p\Iconv::iconv_substr($s, $start, $length, $enc); }
+    }
+    if (!function_exists('iconv_mime_decode')) {
         function iconv_mime_decode($encodedHeaders, $mode = 0, $enc = null) { return p\Iconv::iconv_mime_decode($encodedHeaders, $mode, $enc); }
     }
 }
diff --git a/civicrm/vendor/symfony/polyfill-iconv/composer.json b/civicrm/vendor/symfony/polyfill-iconv/composer.json
index 0c23267cbe3400460de51b8c711e9bc3d1ab9435..3df179e35178ede96d2020fb39da59bcbfd9b6c0 100644
--- a/civicrm/vendor/symfony/polyfill-iconv/composer.json
+++ b/civicrm/vendor/symfony/polyfill-iconv/composer.json
@@ -28,7 +28,7 @@
     "minimum-stability": "dev",
     "extra": {
         "branch-alias": {
-            "dev-master": "1.12-dev"
+            "dev-master": "1.17-dev"
         }
     }
 }
diff --git a/civicrm/vendor/symfony/process/CHANGELOG.md b/civicrm/vendor/symfony/process/CHANGELOG.md
index 2f3c1beb74b7e819608c4f34c36b8122c329eb1f..c5cdb9944164baa2c123b262b57ef33e37b0bc34 100644
--- a/civicrm/vendor/symfony/process/CHANGELOG.md
+++ b/civicrm/vendor/symfony/process/CHANGELOG.md
@@ -1,6 +1,23 @@
 CHANGELOG
 =========
 
+3.4.0
+-----
+
+ * deprecated the ProcessBuilder class
+ * deprecated calling `Process::start()` without setting a valid working directory beforehand (via `setWorkingDirectory()` or constructor)
+
+3.3.0
+-----
+
+ * added command line arrays in the `Process` class
+ * added `$env` argument to `Process::start()`, `run()`, `mustRun()` and `restart()` methods
+ * deprecated the `ProcessUtils::escapeArgument()` method
+ * deprecated not inheriting environment variables
+ * deprecated configuring `proc_open()` options
+ * deprecated configuring enhanced Windows compatibility
+ * deprecated configuring enhanced sigchild compatibility
+
 2.5.0
 -----
 
diff --git a/civicrm/vendor/symfony/process/ExecutableFinder.php b/civicrm/vendor/symfony/process/ExecutableFinder.php
index ccfa4c09b09ae0849b21ab160ea0129ef4dba482..cb4345e7bb4246ee4fdad4a12f6476037a3e7a10 100644
--- a/civicrm/vendor/symfony/process/ExecutableFinder.php
+++ b/civicrm/vendor/symfony/process/ExecutableFinder.php
@@ -19,7 +19,7 @@ namespace Symfony\Component\Process;
  */
 class ExecutableFinder
 {
-    private $suffixes = array('.exe', '.bat', '.cmd', '.com');
+    private $suffixes = ['.exe', '.bat', '.cmd', '.com'];
 
     /**
      * Replaces default suffixes of executable.
@@ -42,17 +42,17 @@ class ExecutableFinder
     /**
      * Finds an executable by name.
      *
-     * @param string $name      The executable name (without the extension)
-     * @param string $default   The default to return if no executable is found
-     * @param array  $extraDirs Additional dirs to check into
+     * @param string      $name      The executable name (without the extension)
+     * @param string|null $default   The default to return if no executable is found
+     * @param array       $extraDirs Additional dirs to check into
      *
-     * @return string The executable path or default value
+     * @return string|null The executable path or default value
      */
-    public function find($name, $default = null, array $extraDirs = array())
+    public function find($name, $default = null, array $extraDirs = [])
     {
         if (ini_get('open_basedir')) {
-            $searchPath = explode(PATH_SEPARATOR, ini_get('open_basedir'));
-            $dirs = array();
+            $searchPath = array_merge(explode(PATH_SEPARATOR, ini_get('open_basedir')), $extraDirs);
+            $dirs = [];
             foreach ($searchPath as $path) {
                 // Silencing against https://bugs.php.net/69240
                 if (@is_dir($path)) {
@@ -70,7 +70,7 @@ class ExecutableFinder
             );
         }
 
-        $suffixes = array('');
+        $suffixes = [''];
         if ('\\' === \DIRECTORY_SEPARATOR) {
             $pathExt = getenv('PATHEXT');
             $suffixes = array_merge($pathExt ? explode(PATH_SEPARATOR, $pathExt) : $this->suffixes, $suffixes);
diff --git a/civicrm/vendor/symfony/process/InputStream.php b/civicrm/vendor/symfony/process/InputStream.php
new file mode 100644
index 0000000000000000000000000000000000000000..ef38dd7bfd41c9b8a0cdcb4afd3006c10a6cedd1
--- /dev/null
+++ b/civicrm/vendor/symfony/process/InputStream.php
@@ -0,0 +1,92 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Process;
+
+use Symfony\Component\Process\Exception\RuntimeException;
+
+/**
+ * Provides a way to continuously write to the input of a Process until the InputStream is closed.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class InputStream implements \IteratorAggregate
+{
+    /** @var callable|null */
+    private $onEmpty = null;
+    private $input = [];
+    private $open = true;
+
+    /**
+     * Sets a callback that is called when the write buffer becomes empty.
+     */
+    public function onEmpty(callable $onEmpty = null)
+    {
+        $this->onEmpty = $onEmpty;
+    }
+
+    /**
+     * Appends an input to the write buffer.
+     *
+     * @param resource|string|int|float|bool|\Traversable|null $input The input to append as scalar,
+     *                                                                stream resource or \Traversable
+     */
+    public function write($input)
+    {
+        if (null === $input) {
+            return;
+        }
+        if ($this->isClosed()) {
+            throw new RuntimeException(sprintf('"%s" is closed.', static::class));
+        }
+        $this->input[] = ProcessUtils::validateInput(__METHOD__, $input);
+    }
+
+    /**
+     * Closes the write buffer.
+     */
+    public function close()
+    {
+        $this->open = false;
+    }
+
+    /**
+     * Tells whether the write buffer is closed or not.
+     */
+    public function isClosed()
+    {
+        return !$this->open;
+    }
+
+    public function getIterator()
+    {
+        $this->open = true;
+
+        while ($this->open || $this->input) {
+            if (!$this->input) {
+                yield '';
+                continue;
+            }
+            $current = array_shift($this->input);
+
+            if ($current instanceof \Iterator) {
+                foreach ($current as $cur) {
+                    yield $cur;
+                }
+            } else {
+                yield $current;
+            }
+            if (!$this->input && $this->open && null !== $onEmpty = $this->onEmpty) {
+                $this->write($onEmpty($this));
+            }
+        }
+    }
+}
diff --git a/civicrm/vendor/symfony/process/LICENSE b/civicrm/vendor/symfony/process/LICENSE
index 21d7fb9e2f29b50caca3a76f0647e94e2cc8ddc1..9e936ec0448b8549e5edf08e5ac5f01491a8bfc8 100644
--- a/civicrm/vendor/symfony/process/LICENSE
+++ b/civicrm/vendor/symfony/process/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2018 Fabien Potencier
+Copyright (c) 2004-2020 Fabien Potencier
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/civicrm/vendor/symfony/process/PhpExecutableFinder.php b/civicrm/vendor/symfony/process/PhpExecutableFinder.php
index 933c02b3ecc3a72f766a3975c3cf0a4dd0676617..a97aa12cbff46bee850e0b2193a92b1b7990f040 100644
--- a/civicrm/vendor/symfony/process/PhpExecutableFinder.php
+++ b/civicrm/vendor/symfony/process/PhpExecutableFinder.php
@@ -44,7 +44,7 @@ class PhpExecutableFinder
         }
 
         // PHP_BINARY return the current sapi executable
-        if (\defined('PHP_BINARY') && PHP_BINARY && \in_array(\PHP_SAPI, array('cli', 'cli-server', 'phpdbg'), true)) {
+        if (PHP_BINARY && \in_array(\PHP_SAPI, ['cli', 'cli-server', 'phpdbg'], true)) {
             return PHP_BINARY.$args;
         }
 
@@ -66,7 +66,7 @@ class PhpExecutableFinder
             return $php;
         }
 
-        $dirs = array(PHP_BINDIR);
+        $dirs = [PHP_BINDIR];
         if ('\\' === \DIRECTORY_SEPARATOR) {
             $dirs[] = 'C:\xampp\php\\';
         }
@@ -81,7 +81,7 @@ class PhpExecutableFinder
      */
     public function findArguments()
     {
-        $arguments = array();
+        $arguments = [];
 
         if (\defined('HHVM_VERSION')) {
             $arguments[] = '--php';
diff --git a/civicrm/vendor/symfony/process/PhpProcess.php b/civicrm/vendor/symfony/process/PhpProcess.php
index 31a855d943af2fbf61ce4f3ca7843b03c8b76b61..f0c47b285a164611e3761b21394c6e79ef331e40 100644
--- a/civicrm/vendor/symfony/process/PhpProcess.php
+++ b/civicrm/vendor/symfony/process/PhpProcess.php
@@ -31,24 +31,23 @@ class PhpProcess extends Process
      * @param int         $timeout The timeout in seconds
      * @param array       $options An array of options for proc_open
      */
-    public function __construct($script, $cwd = null, array $env = null, $timeout = 60, array $options = array())
+    public function __construct($script, $cwd = null, array $env = null, $timeout = 60, array $options = null)
     {
         $executableFinder = new PhpExecutableFinder();
-        if (false === $php = $executableFinder->find()) {
+        if (false === $php = $executableFinder->find(false)) {
             $php = null;
+        } else {
+            $php = array_merge([$php], $executableFinder->findArguments());
         }
         if ('phpdbg' === \PHP_SAPI) {
             $file = tempnam(sys_get_temp_dir(), 'dbg');
             file_put_contents($file, $script);
             register_shutdown_function('unlink', $file);
-            $php .= ' '.ProcessUtils::escapeArgument($file);
+            $php[] = $file;
             $script = null;
         }
-        if ('\\' !== \DIRECTORY_SEPARATOR && null !== $php) {
-            // exec is mandatory to deal with sending a signal to the process
-            // see https://github.com/symfony/symfony/issues/5030 about prepending
-            // command with exec
-            $php = 'exec '.$php;
+        if (null !== $options) {
+            @trigger_error(sprintf('The $options parameter of the %s constructor is deprecated since Symfony 3.3 and will be removed in 4.0.', __CLASS__), E_USER_DEPRECATED);
         }
 
         parent::__construct($php, $cwd, $env, $script, $timeout, $options);
@@ -65,12 +64,13 @@ class PhpProcess extends Process
     /**
      * {@inheritdoc}
      */
-    public function start($callback = null)
+    public function start(callable $callback = null/*, array $env = []*/)
     {
         if (null === $this->getCommandLine()) {
             throw new RuntimeException('Unable to find the PHP executable.');
         }
+        $env = 1 < \func_num_args() ? func_get_arg(1) : null;
 
-        parent::start($callback);
+        parent::start($callback, $env);
     }
 }
diff --git a/civicrm/vendor/symfony/process/Pipes/AbstractPipes.php b/civicrm/vendor/symfony/process/Pipes/AbstractPipes.php
index bd4a459c4a65d279ca36c3f67ea75d04908d9d82..cdffaaffe4aee6492b7e34fabff603c4183a9c8a 100644
--- a/civicrm/vendor/symfony/process/Pipes/AbstractPipes.php
+++ b/civicrm/vendor/symfony/process/Pipes/AbstractPipes.php
@@ -11,6 +11,8 @@
 
 namespace Symfony\Component\Process\Pipes;
 
+use Symfony\Component\Process\Exception\InvalidArgumentException;
+
 /**
  * @author Romain Neutron <imprec@gmail.com>
  *
@@ -18,7 +20,7 @@ namespace Symfony\Component\Process\Pipes;
  */
 abstract class AbstractPipes implements PipesInterface
 {
-    public $pipes = array();
+    public $pipes = [];
 
     private $inputBuffer = '';
     private $input;
@@ -26,11 +28,11 @@ abstract class AbstractPipes implements PipesInterface
     private $lastError;
 
     /**
-     * @param resource|null $input
+     * @param resource|string|int|float|bool|\Iterator|null $input
      */
     public function __construct($input)
     {
-        if (\is_resource($input)) {
+        if (\is_resource($input) || $input instanceof \Iterator) {
             $this->input = $input;
         } elseif (\is_string($input)) {
             $this->inputBuffer = $input;
@@ -47,7 +49,7 @@ abstract class AbstractPipes implements PipesInterface
         foreach ($this->pipes as $pipe) {
             fclose($pipe);
         }
-        $this->pipes = array();
+        $this->pipes = [];
     }
 
     /**
@@ -76,7 +78,7 @@ abstract class AbstractPipes implements PipesInterface
         foreach ($this->pipes as $pipe) {
             stream_set_blocking($pipe, 0);
         }
-        if (null !== $this->input) {
+        if (\is_resource($this->input)) {
             stream_set_blocking($this->input, 0);
         }
 
@@ -85,19 +87,44 @@ abstract class AbstractPipes implements PipesInterface
 
     /**
      * Writes input to stdin.
+     *
+     * @return array|null
+     *
+     * @throws InvalidArgumentException When an input iterator yields a non supported value
      */
     protected function write()
     {
         if (!isset($this->pipes[0])) {
-            return;
+            return null;
         }
         $input = $this->input;
-        $r = $e = array();
-        $w = array($this->pipes[0]);
+
+        if ($input instanceof \Iterator) {
+            if (!$input->valid()) {
+                $input = null;
+            } elseif (\is_resource($input = $input->current())) {
+                stream_set_blocking($input, 0);
+            } elseif (!isset($this->inputBuffer[0])) {
+                if (!\is_string($input)) {
+                    if (!is_scalar($input)) {
+                        throw new InvalidArgumentException(sprintf('"%s" yielded a value of type "%s", but only scalars and stream resources are supported.', \get_class($this->input), \gettype($input)));
+                    }
+                    $input = (string) $input;
+                }
+                $this->inputBuffer = $input;
+                $this->input->next();
+                $input = null;
+            } else {
+                $input = null;
+            }
+        }
+
+        $r = $e = [];
+        $w = [$this->pipes[0]];
 
         // let's have a look if something changed in streams
         if (false === @stream_select($r, $w, $e, 0, 0)) {
-            return;
+            return null;
         }
 
         foreach ($w as $stdin) {
@@ -105,7 +132,7 @@ abstract class AbstractPipes implements PipesInterface
                 $written = fwrite($stdin, $this->inputBuffer);
                 $this->inputBuffer = substr($this->inputBuffer, $written);
                 if (isset($this->inputBuffer[0])) {
-                    return array($this->pipes[0]);
+                    return [$this->pipes[0]];
                 }
             }
 
@@ -120,24 +147,29 @@ abstract class AbstractPipes implements PipesInterface
                     if (isset($data[0])) {
                         $this->inputBuffer = $data;
 
-                        return array($this->pipes[0]);
+                        return [$this->pipes[0]];
                     }
                 }
                 if (feof($input)) {
-                    // no more data to read on input resource
-                    // use an empty buffer in the next reads
-                    $this->input = null;
+                    if ($this->input instanceof \Iterator) {
+                        $this->input->next();
+                    } else {
+                        $this->input = null;
+                    }
                 }
             }
         }
 
         // no input to read on resource, buffer is empty
-        if (null === $this->input && !isset($this->inputBuffer[0])) {
+        if (!isset($this->inputBuffer[0]) && !($this->input instanceof \Iterator ? $this->input->valid() : $this->input)) {
+            $this->input = null;
             fclose($this->pipes[0]);
             unset($this->pipes[0]);
         } elseif (!$w) {
-            return array($this->pipes[0]);
+            return [$this->pipes[0]];
         }
+
+        return null;
     }
 
     /**
diff --git a/civicrm/vendor/symfony/process/Pipes/PipesInterface.php b/civicrm/vendor/symfony/process/Pipes/PipesInterface.php
index b91c393d870e4fc7b2136ff048da33774a04667a..52bbe76b8f67b76ebd83fa5d1b82a7c7c26371e1 100644
--- a/civicrm/vendor/symfony/process/Pipes/PipesInterface.php
+++ b/civicrm/vendor/symfony/process/Pipes/PipesInterface.php
@@ -53,6 +53,13 @@ interface PipesInterface
      */
     public function areOpen();
 
+    /**
+     * Returns if pipes are able to read output.
+     *
+     * @return bool
+     */
+    public function haveReadSupport();
+
     /**
      * Closes file handles and pipes.
      */
diff --git a/civicrm/vendor/symfony/process/Pipes/UnixPipes.php b/civicrm/vendor/symfony/process/Pipes/UnixPipes.php
index 935c43209d9dafdb06be58089c94559639e97962..1ebf2138a56facd592ce31774b5bbfd918b3b13f 100644
--- a/civicrm/vendor/symfony/process/Pipes/UnixPipes.php
+++ b/civicrm/vendor/symfony/process/Pipes/UnixPipes.php
@@ -24,13 +24,13 @@ class UnixPipes extends AbstractPipes
 {
     private $ttyMode;
     private $ptyMode;
-    private $disableOutput;
+    private $haveReadSupport;
 
-    public function __construct($ttyMode, $ptyMode, $input, $disableOutput)
+    public function __construct($ttyMode, $ptyMode, $input, $haveReadSupport)
     {
         $this->ttyMode = (bool) $ttyMode;
         $this->ptyMode = (bool) $ptyMode;
-        $this->disableOutput = (bool) $disableOutput;
+        $this->haveReadSupport = (bool) $haveReadSupport;
 
         parent::__construct($input);
     }
@@ -45,37 +45,37 @@ class UnixPipes extends AbstractPipes
      */
     public function getDescriptors()
     {
-        if ($this->disableOutput) {
+        if (!$this->haveReadSupport) {
             $nullstream = fopen('/dev/null', 'c');
 
-            return array(
-                array('pipe', 'r'),
+            return [
+                ['pipe', 'r'],
                 $nullstream,
                 $nullstream,
-            );
+            ];
         }
 
         if ($this->ttyMode) {
-            return array(
-                array('file', '/dev/tty', 'r'),
-                array('file', '/dev/tty', 'w'),
-                array('file', '/dev/tty', 'w'),
-            );
+            return [
+                ['file', '/dev/tty', 'r'],
+                ['file', '/dev/tty', 'w'],
+                ['file', '/dev/tty', 'w'],
+            ];
         }
 
         if ($this->ptyMode && Process::isPtySupported()) {
-            return array(
-                array('pty'),
-                array('pty'),
-                array('pty'),
-            );
+            return [
+                ['pty'],
+                ['pty'],
+                ['pty'],
+            ];
         }
 
-        return array(
-            array('pipe', 'r'),
-            array('pipe', 'w'), // stdout
-            array('pipe', 'w'), // stderr
-        );
+        return [
+            ['pipe', 'r'],
+            ['pipe', 'w'], // stdout
+            ['pipe', 'w'], // stderr
+        ];
     }
 
     /**
@@ -83,7 +83,7 @@ class UnixPipes extends AbstractPipes
      */
     public function getFiles()
     {
-        return array();
+        return [];
     }
 
     /**
@@ -94,18 +94,18 @@ class UnixPipes extends AbstractPipes
         $this->unblock();
         $w = $this->write();
 
-        $read = $e = array();
+        $read = $e = [];
         $r = $this->pipes;
         unset($r[0]);
 
         // let's have a look if something changed in streams
-        set_error_handler(array($this, 'handleError'));
+        set_error_handler([$this, 'handleError']);
         if (($r || $w) && false === stream_select($r, $w, $e, 0, $blocking ? Process::TIMEOUT_PRECISION * 1E6 : 0)) {
             restore_error_handler();
             // if a system call has been interrupted, forget about it, let's try again
             // otherwise, an error occurred, let's reset pipes
             if (!$this->hasSystemCallBeenInterrupted()) {
-                $this->pipes = array();
+                $this->pipes = [];
             }
 
             return $read;
@@ -138,21 +138,16 @@ class UnixPipes extends AbstractPipes
     /**
      * {@inheritdoc}
      */
-    public function areOpen()
+    public function haveReadSupport()
     {
-        return (bool) $this->pipes;
+        return $this->haveReadSupport;
     }
 
     /**
-     * Creates a new UnixPipes instance.
-     *
-     * @param Process         $process
-     * @param string|resource $input
-     *
-     * @return static
+     * {@inheritdoc}
      */
-    public static function create(Process $process, $input)
+    public function areOpen()
     {
-        return new static($process->isTty(), $process->isPty(), $input, $process->isOutputDisabled());
+        return (bool) $this->pipes;
     }
 }
diff --git a/civicrm/vendor/symfony/process/Pipes/WindowsPipes.php b/civicrm/vendor/symfony/process/Pipes/WindowsPipes.php
index 0b2a76387fdf90873ad08a85006e271b549c91e0..302b0509a86b7408c42fcccb378fb20bee3712d3 100644
--- a/civicrm/vendor/symfony/process/Pipes/WindowsPipes.php
+++ b/civicrm/vendor/symfony/process/Pipes/WindowsPipes.php
@@ -17,8 +17,8 @@ use Symfony\Component\Process\Process;
 /**
  * WindowsPipes implementation uses temporary files as handles.
  *
- * @see https://bugs.php.net/bug.php?id=51800
- * @see https://bugs.php.net/bug.php?id=65650
+ * @see https://bugs.php.net/51800
+ * @see https://bugs.php.net/65650
  *
  * @author Romain Neutron <imprec@gmail.com>
  *
@@ -26,28 +26,28 @@ use Symfony\Component\Process\Process;
  */
 class WindowsPipes extends AbstractPipes
 {
-    private $files = array();
-    private $fileHandles = array();
-    private $lockHandles = array();
-    private $readBytes = array(
+    private $files = [];
+    private $fileHandles = [];
+    private $lockHandles = [];
+    private $readBytes = [
         Process::STDOUT => 0,
         Process::STDERR => 0,
-    );
-    private $disableOutput;
+    ];
+    private $haveReadSupport;
 
-    public function __construct($disableOutput, $input)
+    public function __construct($input, $haveReadSupport)
     {
-        $this->disableOutput = (bool) $disableOutput;
+        $this->haveReadSupport = (bool) $haveReadSupport;
 
-        if (!$this->disableOutput) {
+        if ($this->haveReadSupport) {
             // Fix for PHP bug #51800: reading from STDOUT pipe hangs forever on Windows if the output is too big.
             // Workaround for this problem is to use temporary files instead of pipes on Windows platform.
             //
-            // @see https://bugs.php.net/bug.php?id=51800
-            $pipes = array(
+            // @see https://bugs.php.net/51800
+            $pipes = [
                 Process::STDOUT => Process::OUT,
                 Process::STDERR => Process::ERR,
-            );
+            ];
             $tmpDir = sys_get_temp_dir();
             $lastError = 'unknown reason';
             set_error_handler(function ($type, $msg) use (&$lastError) { $lastError = $msg; });
@@ -57,7 +57,7 @@ class WindowsPipes extends AbstractPipes
 
                     if (!$h = fopen($file.'.lock', 'w')) {
                         restore_error_handler();
-                        throw new RuntimeException(sprintf('A temporary file could not be opened to write the process output: %s', $lastError));
+                        throw new RuntimeException('A temporary file could not be opened to write the process output: '.$lastError);
                     }
                     if (!flock($h, LOCK_EX | LOCK_NB)) {
                         continue 2;
@@ -95,24 +95,24 @@ class WindowsPipes extends AbstractPipes
      */
     public function getDescriptors()
     {
-        if ($this->disableOutput) {
+        if (!$this->haveReadSupport) {
             $nullstream = fopen('NUL', 'c');
 
-            return array(
-                array('pipe', 'r'),
+            return [
+                ['pipe', 'r'],
                 $nullstream,
                 $nullstream,
-            );
+            ];
         }
 
-        // We're not using pipe on Windows platform as it hangs (https://bugs.php.net/bug.php?id=51800)
-        // We're not using file handles as it can produce corrupted output https://bugs.php.net/bug.php?id=65650
+        // We're not using pipe on Windows platform as it hangs (https://bugs.php.net/51800)
+        // We're not using file handles as it can produce corrupted output https://bugs.php.net/65650
         // So we redirect output within the commandline and pass the nul device to the process
-        return array(
-            array('pipe', 'r'),
-            array('file', 'NUL', 'w'),
-            array('file', 'NUL', 'w'),
-        );
+        return [
+            ['pipe', 'r'],
+            ['file', 'NUL', 'w'],
+            ['file', 'NUL', 'w'],
+        ];
     }
 
     /**
@@ -130,7 +130,7 @@ class WindowsPipes extends AbstractPipes
     {
         $this->unblock();
         $w = $this->write();
-        $read = $r = $e = array();
+        $read = $r = $e = [];
 
         if ($blocking) {
             if ($w) {
@@ -158,6 +158,14 @@ class WindowsPipes extends AbstractPipes
         return $read;
     }
 
+    /**
+     * {@inheritdoc}
+     */
+    public function haveReadSupport()
+    {
+        return $this->haveReadSupport;
+    }
+
     /**
      * {@inheritdoc}
      */
@@ -178,19 +186,6 @@ class WindowsPipes extends AbstractPipes
             flock($this->lockHandles[$type], LOCK_UN);
             fclose($this->lockHandles[$type]);
         }
-        $this->fileHandles = $this->lockHandles = array();
-    }
-
-    /**
-     * Creates a new WindowsPipes instance.
-     *
-     * @param Process $process The process
-     * @param $input
-     *
-     * @return static
-     */
-    public static function create(Process $process, $input)
-    {
-        return new static($process->isOutputDisabled(), $input);
+        $this->fileHandles = $this->lockHandles = [];
     }
 }
diff --git a/civicrm/vendor/symfony/process/Process.php b/civicrm/vendor/symfony/process/Process.php
index a261ea5340b1fcfbc5f94371e8e9f75c36eb70bb..68d52512ed76d37dac6c36fe0c680b0565ca7031 100644
--- a/civicrm/vendor/symfony/process/Process.php
+++ b/civicrm/vendor/symfony/process/Process.php
@@ -27,7 +27,7 @@ use Symfony\Component\Process\Pipes\WindowsPipes;
  * @author Fabien Potencier <fabien@symfony.com>
  * @author Romain Neutron <imprec@gmail.com>
  */
-class Process
+class Process implements \IteratorAggregate
 {
     const ERR = 'err';
     const OUT = 'out';
@@ -43,7 +43,13 @@ class Process
     // Timeout Precision in seconds.
     const TIMEOUT_PRECISION = 0.2;
 
+    const ITER_NON_BLOCKING = 1; // By default, iterating over outputs is a blocking call, use this flag to make it non-blocking
+    const ITER_KEEP_OUTPUT = 2;  // By default, outputs are cleared while iterating, use this flag to keep them in memory
+    const ITER_SKIP_OUT = 4;     // Use this flag to skip STDOUT while iterating
+    const ITER_SKIP_ERR = 8;     // Use this flag to skip STDERR while iterating
+
     private $callback;
+    private $hasCallback = false;
     private $commandline;
     private $cwd;
     private $env;
@@ -52,9 +58,9 @@ class Process
     private $lastOutputTime;
     private $timeout;
     private $idleTimeout;
-    private $options;
+    private $options = ['suppress_errors' => true];
     private $exitcode;
-    private $fallbackStatus = array();
+    private $fallbackStatus = [];
     private $processInformation;
     private $outputDisabled = false;
     private $stdout;
@@ -65,8 +71,9 @@ class Process
     private $status = self::STATUS_READY;
     private $incrementalOutputOffset = 0;
     private $incrementalErrorOutputOffset = 0;
-    private $tty;
+    private $tty = false;
     private $pty;
+    private $inheritEnv = false;
 
     private $useFileHandles = false;
     /** @var PipesInterface */
@@ -81,7 +88,7 @@ class Process
      *
      * User-defined errors must use exit codes in the 64-113 range.
      */
-    public static $exitCodes = array(
+    public static $exitCodes = [
         0 => 'OK',
         1 => 'General error',
         2 => 'Misuse of shell builtins',
@@ -122,19 +129,19 @@ class Process
         157 => 'Pollable event',
         // 158 - not defined
         159 => 'Bad syscall',
-    );
+    ];
 
     /**
-     * @param string         $commandline The command line to run
+     * @param string|array   $commandline The command line to run
      * @param string|null    $cwd         The working directory or null to use the working dir of the current PHP process
      * @param array|null     $env         The environment variables or null to use the same environment as the current PHP process
-     * @param string|null    $input       The input
+     * @param mixed|null     $input       The input as stream resource, scalar or \Traversable, or null for no input
      * @param int|float|null $timeout     The timeout in seconds or null to disable
      * @param array          $options     An array of options for proc_open
      *
      * @throws RuntimeException When proc_open is not installed
      */
-    public function __construct($commandline, $cwd = null, array $env = null, $input = null, $timeout = 60, array $options = array())
+    public function __construct($commandline, $cwd = null, array $env = null, $input = null, $timeout = 60, array $options = null)
     {
         if (!\function_exists('proc_open')) {
             throw new RuntimeException('The Process class relies on proc_open, which is not available on your PHP installation.');
@@ -145,8 +152,8 @@ class Process
 
         // on Windows, if the cwd changed via chdir(), proc_open defaults to the dir where PHP was started
         // on Gnu/Linux, PHP builds with --enable-maintainer-zts are also affected
-        // @see : https://bugs.php.net/bug.php?id=51800
-        // @see : https://bugs.php.net/bug.php?id=50524
+        // @see : https://bugs.php.net/51800
+        // @see : https://bugs.php.net/50524
         if (null === $this->cwd && (\defined('ZEND_THREAD_SAFE') || '\\' === \DIRECTORY_SEPARATOR)) {
             $this->cwd = getcwd();
         }
@@ -159,7 +166,10 @@ class Process
         $this->useFileHandles = '\\' === \DIRECTORY_SEPARATOR;
         $this->pty = false;
         $this->enhanceSigchildCompatibility = '\\' !== \DIRECTORY_SEPARATOR && $this->isSigchildEnabled();
-        $this->options = array_replace(array('suppress_errors' => true, 'binary_pipes' => true), $options);
+        if (null !== $options) {
+            @trigger_error(sprintf('The $options parameter of the %s constructor is deprecated since Symfony 3.3 and will be removed in 4.0.', __CLASS__), E_USER_DEPRECATED);
+            $this->options = array_replace($this->options, $options);
+        }
     }
 
     public function __destruct()
@@ -190,10 +200,13 @@ class Process
      * @throws RuntimeException When process can't be launched
      * @throws RuntimeException When process stopped after receiving signal
      * @throws LogicException   In case a callback is provided and output has been disabled
+     *
+     * @final since version 3.3
      */
-    public function run($callback = null)
+    public function run($callback = null/*, array $env = []*/)
     {
-        $this->start($callback);
+        $env = 1 < \func_num_args() ? func_get_arg(1) : null;
+        $this->start($callback, $env);
 
         return $this->wait();
     }
@@ -204,20 +217,21 @@ class Process
      * This is identical to run() except that an exception is thrown if the process
      * exits with a non-zero exit code.
      *
-     * @param callable|null $callback
-     *
-     * @return self
+     * @return $this
      *
      * @throws RuntimeException       if PHP was compiled with --enable-sigchild and the enhanced sigchild compatibility mode is not enabled
      * @throws ProcessFailedException if the process didn't terminate successfully
+     *
+     * @final since version 3.3
      */
-    public function mustRun($callback = null)
+    public function mustRun(callable $callback = null/*, array $env = []*/)
     {
         if (!$this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) {
             throw new RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.');
         }
+        $env = 1 < \func_num_args() ? func_get_arg(1) : null;
 
-        if (0 !== $this->run($callback)) {
+        if (0 !== $this->run($callback, $env)) {
             throw new ProcessFailedException($this);
         }
 
@@ -243,46 +257,86 @@ class Process
      * @throws RuntimeException When process is already running
      * @throws LogicException   In case a callback is provided and output has been disabled
      */
-    public function start($callback = null)
+    public function start(callable $callback = null/*, array $env = [*/)
     {
         if ($this->isRunning()) {
-            throw new RuntimeException('Process is already running');
+            throw new RuntimeException('Process is already running.');
         }
-        if ($this->outputDisabled && null !== $callback) {
-            throw new LogicException('Output has been disabled, enable it to allow the use of a callback.');
+        if (2 <= \func_num_args()) {
+            $env = func_get_arg(1);
+        } else {
+            if (__CLASS__ !== static::class) {
+                $r = new \ReflectionMethod($this, __FUNCTION__);
+                if (__CLASS__ !== $r->getDeclaringClass()->getName() && (2 > $r->getNumberOfParameters() || 'env' !== $r->getParameters()[1]->name)) {
+                    @trigger_error(sprintf('The %s::start() method expects a second "$env" argument since Symfony 3.3. It will be made mandatory in 4.0.', static::class), E_USER_DEPRECATED);
+                }
+            }
+            $env = null;
         }
 
         $this->resetProcessData();
         $this->starttime = $this->lastOutputTime = microtime(true);
         $this->callback = $this->buildCallback($callback);
+        $this->hasCallback = null !== $callback;
         $descriptors = $this->getDescriptors();
+        $inheritEnv = $this->inheritEnv;
 
-        $commandline = $this->commandline;
+        if (\is_array($commandline = $this->commandline)) {
+            $commandline = implode(' ', array_map([$this, 'escapeArgument'], $commandline));
 
-        if ('\\' === \DIRECTORY_SEPARATOR && $this->enhanceWindowsCompatibility) {
-            $commandline = 'cmd /V:ON /E:ON /D /C "('.$commandline.')';
-            foreach ($this->processPipes->getFiles() as $offset => $filename) {
-                $commandline .= ' '.$offset.'>'.ProcessUtils::escapeArgument($filename);
+            if ('\\' !== \DIRECTORY_SEPARATOR) {
+                // exec is mandatory to deal with sending a signal to the process
+                $commandline = 'exec '.$commandline;
             }
-            $commandline .= '"';
+        }
 
-            if (!isset($this->options['bypass_shell'])) {
-                $this->options['bypass_shell'] = true;
+        if (null === $env) {
+            $env = $this->env;
+        } else {
+            if ($this->env) {
+                $env += $this->env;
             }
+            $inheritEnv = true;
+        }
+
+        if (null !== $env && $inheritEnv) {
+            $env += $this->getDefaultEnv();
+        } elseif (null !== $env) {
+            @trigger_error('Not inheriting environment variables is deprecated since Symfony 3.3 and will always happen in 4.0. Set "Process::inheritEnvironmentVariables()" to true instead.', E_USER_DEPRECATED);
+        } else {
+            $env = $this->getDefaultEnv();
+        }
+        if ('\\' === \DIRECTORY_SEPARATOR && $this->enhanceWindowsCompatibility) {
+            $this->options['bypass_shell'] = true;
+            $commandline = $this->prepareWindowsCommandLine($commandline, $env);
         } elseif (!$this->useFileHandles && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) {
             // last exit code is output on the fourth pipe and caught to work around --enable-sigchild
-            $descriptors[3] = array('pipe', 'w');
+            $descriptors[3] = ['pipe', 'w'];
 
             // See https://unix.stackexchange.com/questions/71205/background-process-pipe-input
-            $commandline = '{ ('.$this->commandline.') <&3 3<&- 3>/dev/null & } 3<&0;';
+            $commandline = '{ ('.$commandline.') <&3 3<&- 3>/dev/null & } 3<&0;';
             $commandline .= 'pid=$!; echo $pid >&3; wait $pid; code=$?; echo $code >&3; exit $code';
 
             // Workaround for the bug, when PTS functionality is enabled.
             // @see : https://bugs.php.net/69442
             $ptsWorkaround = fopen(__FILE__, 'r');
         }
+        if (\defined('HHVM_VERSION')) {
+            $envPairs = $env;
+        } else {
+            $envPairs = [];
+            foreach ($env as $k => $v) {
+                if (false !== $v) {
+                    $envPairs[] = $k.'='.$v;
+                }
+            }
+        }
 
-        $this->process = proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $this->env, $this->options);
+        if (!is_dir($this->cwd)) {
+            @trigger_error('The provided cwd does not exist. Command is currently ran against getcwd(). This behavior is deprecated since Symfony 3.4 and will be removed in 4.0.', E_USER_DEPRECATED);
+        }
+
+        $this->process = @proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $envPairs, $this->options);
 
         if (!\is_resource($this->process)) {
             throw new RuntimeException('Unable to launch a new process.');
@@ -309,21 +363,24 @@ class Process
      * @param callable|null $callback A PHP callback to run whenever there is some
      *                                output available on STDOUT or STDERR
      *
-     * @return $this
+     * @return static
      *
      * @throws RuntimeException When process can't be launched
      * @throws RuntimeException When process is already running
      *
      * @see start()
+     *
+     * @final since version 3.3
      */
-    public function restart($callback = null)
+    public function restart(callable $callback = null/*, array $env = []*/)
     {
         if ($this->isRunning()) {
-            throw new RuntimeException('Process is already running');
+            throw new RuntimeException('Process is already running.');
         }
+        $env = 1 < \func_num_args() ? func_get_arg(1) : null;
 
         $process = clone $this;
-        $process->start($callback);
+        $process->start($callback, $env);
 
         return $process;
     }
@@ -343,12 +400,17 @@ class Process
      * @throws RuntimeException When process stopped after receiving signal
      * @throws LogicException   When process is not yet started
      */
-    public function wait($callback = null)
+    public function wait(callable $callback = null)
     {
         $this->requireProcessIsStarted(__FUNCTION__);
 
         $this->updateStatus(false);
+
         if (null !== $callback) {
+            if (!$this->processPipes->haveReadSupport()) {
+                $this->stop(0);
+                throw new \LogicException('Pass the callback to the Process::start method or enableOutput to use a callback with Process::wait.');
+            }
             $this->callback = $this->buildCallback($callback);
         }
 
@@ -359,6 +421,7 @@ class Process
         } while ($running);
 
         while ($this->isRunning()) {
+            $this->checkTimeout();
             usleep(1000);
         }
 
@@ -382,7 +445,7 @@ class Process
     /**
      * Sends a POSIX signal to the process.
      *
-     * @param int $signal A valid POSIX signal (see http://www.php.net/manual/en/pcntl.constants.php)
+     * @param int $signal A valid POSIX signal (see https://php.net/pcntl.constants)
      *
      * @return $this
      *
@@ -491,6 +554,63 @@ class Process
         return $latest;
     }
 
+    /**
+     * Returns an iterator to the output of the process, with the output type as keys (Process::OUT/ERR).
+     *
+     * @param int $flags A bit field of Process::ITER_* flags
+     *
+     * @throws LogicException in case the output has been disabled
+     * @throws LogicException In case the process is not started
+     *
+     * @return \Generator
+     */
+    public function getIterator($flags = 0)
+    {
+        $this->readPipesForOutput(__FUNCTION__, false);
+
+        $clearOutput = !(self::ITER_KEEP_OUTPUT & $flags);
+        $blocking = !(self::ITER_NON_BLOCKING & $flags);
+        $yieldOut = !(self::ITER_SKIP_OUT & $flags);
+        $yieldErr = !(self::ITER_SKIP_ERR & $flags);
+
+        while (null !== $this->callback || ($yieldOut && !feof($this->stdout)) || ($yieldErr && !feof($this->stderr))) {
+            if ($yieldOut) {
+                $out = stream_get_contents($this->stdout, -1, $this->incrementalOutputOffset);
+
+                if (isset($out[0])) {
+                    if ($clearOutput) {
+                        $this->clearOutput();
+                    } else {
+                        $this->incrementalOutputOffset = ftell($this->stdout);
+                    }
+
+                    yield self::OUT => $out;
+                }
+            }
+
+            if ($yieldErr) {
+                $err = stream_get_contents($this->stderr, -1, $this->incrementalErrorOutputOffset);
+
+                if (isset($err[0])) {
+                    if ($clearOutput) {
+                        $this->clearErrorOutput();
+                    } else {
+                        $this->incrementalErrorOutputOffset = ftell($this->stderr);
+                    }
+
+                    yield self::ERR => $err;
+                }
+            }
+
+            if (!$blocking && !isset($out[0]) && !isset($err[0])) {
+                yield self::OUT => '';
+            }
+
+            $this->checkTimeout();
+            $this->readPipesForOutput(__FUNCTION__, $blocking);
+        }
+    }
+
     /**
      * Clears the process output.
      *
@@ -596,7 +716,7 @@ class Process
     public function getExitCodeText()
     {
         if (null === $exitcode = $this->getExitCode()) {
-            return;
+            return null;
         }
 
         return isset(self::$exitCodes[$exitcode]) ? self::$exitCodes[$exitcode] : 'Unknown error';
@@ -744,7 +864,7 @@ class Process
      * @param int|float $timeout The timeout in seconds
      * @param int       $signal  A POSIX signal to send in case the process has not stop at timeout, default is SIGKILL (9)
      *
-     * @return int The exit-code of the process
+     * @return int|null The exit-code of the process or null if it's not running
      */
     public function stop($timeout = 10, $signal = null)
     {
@@ -814,15 +934,15 @@ class Process
      */
     public function getCommandLine()
     {
-        return $this->commandline;
+        return \is_array($this->commandline) ? implode(' ', array_map([$this, 'escapeArgument'], $this->commandline)) : $this->commandline;
     }
 
     /**
      * Sets the command line to be executed.
      *
-     * @param string $commandline The command to execute
+     * @param string|array $commandline The command to execute
      *
-     * @return self The current Process instance
+     * @return $this
      */
     public function setCommandLine($commandline)
     {
@@ -852,13 +972,13 @@ class Process
     }
 
     /**
-     * Sets the process timeout (max. runtime).
+     * Sets the process timeout (max. runtime) in seconds.
      *
      * To disable the timeout, set this value to null.
      *
      * @param int|float|null $timeout The timeout in seconds
      *
-     * @return self The current Process instance
+     * @return $this
      *
      * @throws InvalidArgumentException if the timeout is negative
      */
@@ -876,7 +996,7 @@ class Process
      *
      * @param int|float|null $timeout The timeout in seconds
      *
-     * @return self The current Process instance
+     * @return $this
      *
      * @throws LogicException           if the output is disabled
      * @throws InvalidArgumentException if the timeout is negative
@@ -897,7 +1017,7 @@ class Process
      *
      * @param bool $tty True to enabled and false to disable
      *
-     * @return self The current Process instance
+     * @return $this
      *
      * @throws RuntimeException In case the TTY mode is not supported
      */
@@ -910,7 +1030,7 @@ class Process
             static $isTtySupported;
 
             if (null === $isTtySupported) {
-                $isTtySupported = (bool) @proc_open('echo 1 >/dev/null', array(array('file', '/dev/tty', 'r'), array('file', '/dev/tty', 'w'), array('file', '/dev/tty', 'w')), $pipes);
+                $isTtySupported = (bool) @proc_open('echo 1 >/dev/null', [['file', '/dev/tty', 'r'], ['file', '/dev/tty', 'w'], ['file', '/dev/tty', 'w']], $pipes);
             }
 
             if (!$isTtySupported) {
@@ -938,7 +1058,7 @@ class Process
      *
      * @param bool $bool
      *
-     * @return self
+     * @return $this
      */
     public function setPty($bool)
     {
@@ -978,7 +1098,7 @@ class Process
      *
      * @param string $cwd The new working directory
      *
-     * @return self The current Process instance
+     * @return $this
      */
     public function setWorkingDirectory($cwd)
     {
@@ -1002,13 +1122,15 @@ class Process
      *
      * Each environment variable value should be a string.
      * If it is an array, the variable is ignored.
+     * If it is false or null, it will be removed when
+     * env vars are otherwise inherited.
      *
      * That happens in PHP when 'argv' is registered into
      * the $_ENV array for instance.
      *
      * @param array $env The new environment variables
      *
-     * @return self The current Process instance
+     * @return $this
      */
     public function setEnv(array $env)
     {
@@ -1017,72 +1139,31 @@ class Process
             return !\is_array($value);
         });
 
-        $this->env = array();
-        foreach ($env as $key => $value) {
-            $this->env[$key] = (string) $value;
-        }
+        $this->env = $env;
 
         return $this;
     }
 
-    /**
-     * Gets the contents of STDIN.
-     *
-     * @return string|null The current contents
-     *
-     * @deprecated since version 2.5, to be removed in 3.0.
-     *             Use setInput() instead.
-     *             This method is deprecated in favor of getInput.
-     */
-    public function getStdin()
-    {
-        @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.5 and will be removed in 3.0. Use the getInput() method instead.', E_USER_DEPRECATED);
-
-        return $this->getInput();
-    }
-
     /**
      * Gets the Process input.
      *
-     * @return string|null The Process input
+     * @return resource|string|\Iterator|null The Process input
      */
     public function getInput()
     {
         return $this->input;
     }
 
-    /**
-     * Sets the contents of STDIN.
-     *
-     * @param string|null $stdin The new contents
-     *
-     * @return self The current Process instance
-     *
-     * @deprecated since version 2.5, to be removed in 3.0.
-     *             Use setInput() instead.
-     *
-     * @throws LogicException           In case the process is running
-     * @throws InvalidArgumentException In case the argument is invalid
-     */
-    public function setStdin($stdin)
-    {
-        @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.5 and will be removed in 3.0. Use the setInput() method instead.', E_USER_DEPRECATED);
-
-        return $this->setInput($stdin);
-    }
-
     /**
      * Sets the input.
      *
      * This content will be passed to the underlying process standard input.
      *
-     * @param mixed $input The content
+     * @param string|int|float|bool|resource|\Traversable|null $input The content
      *
-     * @return self The current Process instance
+     * @return $this
      *
      * @throws LogicException In case the process is running
-     *
-     * Passing an object as an input is deprecated since version 2.5 and will be removed in 3.0.
      */
     public function setInput($input)
     {
@@ -1099,9 +1180,13 @@ class Process
      * Gets the options for proc_open.
      *
      * @return array The current options
+     *
+     * @deprecated since version 3.3, to be removed in 4.0.
      */
     public function getOptions()
     {
+        @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED);
+
         return $this->options;
     }
 
@@ -1110,10 +1195,14 @@ class Process
      *
      * @param array $options The new options
      *
-     * @return self The current Process instance
+     * @return $this
+     *
+     * @deprecated since version 3.3, to be removed in 4.0.
      */
     public function setOptions(array $options)
     {
+        @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED);
+
         $this->options = $options;
 
         return $this;
@@ -1125,9 +1214,13 @@ class Process
      * This is true by default.
      *
      * @return bool
+     *
+     * @deprecated since version 3.3, to be removed in 4.0. Enhanced Windows compatibility will always be enabled.
      */
     public function getEnhanceWindowsCompatibility()
     {
+        @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Enhanced Windows compatibility will always be enabled.', __METHOD__), E_USER_DEPRECATED);
+
         return $this->enhanceWindowsCompatibility;
     }
 
@@ -1136,10 +1229,14 @@ class Process
      *
      * @param bool $enhance
      *
-     * @return self The current Process instance
+     * @return $this
+     *
+     * @deprecated since version 3.3, to be removed in 4.0. Enhanced Windows compatibility will always be enabled.
      */
     public function setEnhanceWindowsCompatibility($enhance)
     {
+        @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Enhanced Windows compatibility will always be enabled.', __METHOD__), E_USER_DEPRECATED);
+
         $this->enhanceWindowsCompatibility = (bool) $enhance;
 
         return $this;
@@ -1149,9 +1246,13 @@ class Process
      * Returns whether sigchild compatibility mode is activated or not.
      *
      * @return bool
+     *
+     * @deprecated since version 3.3, to be removed in 4.0. Sigchild compatibility will always be enabled.
      */
     public function getEnhanceSigchildCompatibility()
     {
+        @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Sigchild compatibility will always be enabled.', __METHOD__), E_USER_DEPRECATED);
+
         return $this->enhanceSigchildCompatibility;
     }
 
@@ -1164,15 +1265,51 @@ class Process
      *
      * @param bool $enhance
      *
-     * @return self The current Process instance
+     * @return $this
+     *
+     * @deprecated since version 3.3, to be removed in 4.0.
      */
     public function setEnhanceSigchildCompatibility($enhance)
     {
+        @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Sigchild compatibility will always be enabled.', __METHOD__), E_USER_DEPRECATED);
+
         $this->enhanceSigchildCompatibility = (bool) $enhance;
 
         return $this;
     }
 
+    /**
+     * Sets whether environment variables will be inherited or not.
+     *
+     * @param bool $inheritEnv
+     *
+     * @return $this
+     */
+    public function inheritEnvironmentVariables($inheritEnv = true)
+    {
+        if (!$inheritEnv) {
+            @trigger_error('Not inheriting environment variables is deprecated since Symfony 3.3 and will always happen in 4.0. Set "Process::inheritEnvironmentVariables()" to true instead.', E_USER_DEPRECATED);
+        }
+
+        $this->inheritEnv = (bool) $inheritEnv;
+
+        return $this;
+    }
+
+    /**
+     * Returns whether environment variables will be inherited or not.
+     *
+     * @return bool
+     *
+     * @deprecated since version 3.3, to be removed in 4.0. Environment variables will always be inherited.
+     */
+    public function areEnvironmentVariablesInherited()
+    {
+        @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Environment variables will always be inherited.', __METHOD__), E_USER_DEPRECATED);
+
+        return $this->inheritEnv;
+    }
+
     /**
      * Performs a check between the timeout definition and the time the process started.
      *
@@ -1217,7 +1354,7 @@ class Process
             return $result = false;
         }
 
-        return $result = (bool) @proc_open('echo 1 >/dev/null', array(array('pty'), array('pty'), array('pty')), $pipes);
+        return $result = (bool) @proc_open('echo 1 >/dev/null', [['pty'], ['pty'], ['pty']], $pipes);
     }
 
     /**
@@ -1227,10 +1364,13 @@ class Process
      */
     private function getDescriptors()
     {
+        if ($this->input instanceof \Iterator) {
+            $this->input->rewind();
+        }
         if ('\\' === \DIRECTORY_SEPARATOR) {
-            $this->processPipes = WindowsPipes::create($this, $this->input);
+            $this->processPipes = new WindowsPipes($this->input, !$this->outputDisabled || $this->hasCallback);
         } else {
-            $this->processPipes = UnixPipes::create($this, $this->input);
+            $this->processPipes = new UnixPipes($this->isTty(), $this->isPty(), $this->input, !$this->outputDisabled || $this->hasCallback);
         }
 
         return $this->processPipes->getDescriptors();
@@ -1246,23 +1386,29 @@ class Process
      *
      * @return \Closure A PHP closure
      */
-    protected function buildCallback($callback)
+    protected function buildCallback(callable $callback = null)
     {
-        $that = $this;
+        if ($this->outputDisabled) {
+            return function ($type, $data) use ($callback) {
+                if (null !== $callback) {
+                    \call_user_func($callback, $type, $data);
+                }
+            };
+        }
+
         $out = self::OUT;
-        $callback = function ($type, $data) use ($that, $callback, $out) {
+
+        return function ($type, $data) use ($callback, $out) {
             if ($out == $type) {
-                $that->addOutput($data);
+                $this->addOutput($data);
             } else {
-                $that->addErrorOutput($data);
+                $this->addErrorOutput($data);
             }
 
             if (null !== $callback) {
                 \call_user_func($callback, $type, $data);
             }
         };
-
-        return $callback;
     }
 
     /**
@@ -1314,11 +1460,12 @@ class Process
     /**
      * Reads pipes for the freshest output.
      *
-     * @param string $caller The name of the method that needs fresh outputs
+     * @param string $caller   The name of the method that needs fresh outputs
+     * @param bool   $blocking Whether to use blocking calls or not
      *
      * @throws LogicException in case output has been disabled or process is not started
      */
-    private function readPipesForOutput($caller)
+    private function readPipesForOutput($caller, $blocking = false)
     {
         if ($this->outputDisabled) {
             throw new LogicException('Output has been disabled.');
@@ -1326,7 +1473,7 @@ class Process
 
         $this->requireProcessIsStarted($caller);
 
-        $this->updateStatus(false);
+        $this->updateStatus($blocking);
     }
 
     /**
@@ -1411,7 +1558,7 @@ class Process
         $this->starttime = null;
         $this->callback = null;
         $this->exitcode = null;
-        $this->fallbackStatus = array();
+        $this->fallbackStatus = [];
         $this->processInformation = null;
         $this->stdout = fopen('php://temp/maxmemory:'.(1024 * 1024), 'w+b');
         $this->stderr = fopen('php://temp/maxmemory:'.(1024 * 1024), 'w+b');
@@ -1425,7 +1572,7 @@ class Process
     /**
      * Sends a POSIX signal to the process.
      *
-     * @param int  $signal         A valid POSIX signal (see http://www.php.net/manual/en/pcntl.constants.php)
+     * @param int  $signal         A valid POSIX signal (see https://php.net/pcntl.constants)
      * @param bool $throwException Whether to throw exception in case signal failed
      *
      * @return bool True if the signal was sent successfully, false otherwise
@@ -1458,7 +1605,7 @@ class Process
                 $ok = @proc_terminate($this->process, $signal);
             } elseif (\function_exists('posix_kill')) {
                 $ok = @posix_kill($pid, $signal);
-            } elseif ($ok = proc_open(sprintf('kill -%d %d', $signal, $pid), array(2 => array('pipe', 'w')), $pipes)) {
+            } elseif ($ok = proc_open(sprintf('kill -%d %d', $signal, $pid), [2 => ['pipe', 'w']], $pipes)) {
                 $ok = false === fgets($pipes[2]);
             }
             if (!$ok) {
@@ -1478,6 +1625,52 @@ class Process
         return true;
     }
 
+    private function prepareWindowsCommandLine($cmd, array &$env)
+    {
+        $uid = uniqid('', true);
+        $varCount = 0;
+        $varCache = [];
+        $cmd = preg_replace_callback(
+            '/"(?:(
+                [^"%!^]*+
+                (?:
+                    (?: !LF! | "(?:\^[%!^])?+" )
+                    [^"%!^]*+
+                )++
+            ) | [^"]*+ )"/x',
+            function ($m) use (&$env, &$varCache, &$varCount, $uid) {
+                if (!isset($m[1])) {
+                    return $m[0];
+                }
+                if (isset($varCache[$m[0]])) {
+                    return $varCache[$m[0]];
+                }
+                if (false !== strpos($value = $m[1], "\0")) {
+                    $value = str_replace("\0", '?', $value);
+                }
+                if (false === strpbrk($value, "\"%!\n")) {
+                    return '"'.$value.'"';
+                }
+
+                $value = str_replace(['!LF!', '"^!"', '"^%"', '"^^"', '""'], ["\n", '!', '%', '^', '"'], $value);
+                $value = '"'.preg_replace('/(\\\\*)"/', '$1$1\\"', $value).'"';
+                $var = $uid.++$varCount;
+
+                $env[$var] = $value;
+
+                return $varCache[$m[0]] = '!'.$var.'!';
+            },
+            $cmd
+        );
+
+        $cmd = 'cmd /V:ON /E:ON /D /C ('.str_replace("\n", ' ', $cmd).')';
+        foreach ($this->processPipes->getFiles() as $offset => $filename) {
+            $cmd .= ' '.$offset.'>"'.$filename.'"';
+        }
+
+        return $cmd;
+    }
+
     /**
      * Ensures the process is running or terminated, throws a LogicException if the process has a not started.
      *
@@ -1488,7 +1681,7 @@ class Process
     private function requireProcessIsStarted($functionName)
     {
         if (!$this->isStarted()) {
-            throw new LogicException(sprintf('Process must be started before calling %s.', $functionName));
+            throw new LogicException(sprintf('Process must be started before calling "%s()".', $functionName));
         }
     }
 
@@ -1502,7 +1695,52 @@ class Process
     private function requireProcessIsTerminated($functionName)
     {
         if (!$this->isTerminated()) {
-            throw new LogicException(sprintf('Process must be terminated before calling %s.', $functionName));
+            throw new LogicException(sprintf('Process must be terminated before calling "%s()".', $functionName));
+        }
+    }
+
+    /**
+     * Escapes a string to be used as a shell argument.
+     *
+     * @param string $argument The argument that will be escaped
+     *
+     * @return string The escaped argument
+     */
+    private function escapeArgument($argument)
+    {
+        if ('\\' !== \DIRECTORY_SEPARATOR) {
+            return "'".str_replace("'", "'\\''", $argument)."'";
+        }
+        if ('' === $argument = (string) $argument) {
+            return '""';
+        }
+        if (false !== strpos($argument, "\0")) {
+            $argument = str_replace("\0", '?', $argument);
+        }
+        if (!preg_match('/[\/()%!^"<>&|\s]/', $argument)) {
+            return $argument;
+        }
+        $argument = preg_replace('/(\\\\+)$/', '$1$1', $argument);
+
+        return '"'.str_replace(['"', '^', '%', '!', "\n"], ['""', '"^^"', '"^%"', '"^!"', '!LF!'], $argument).'"';
+    }
+
+    private function getDefaultEnv()
+    {
+        $env = [];
+
+        foreach ($_SERVER as $k => $v) {
+            if (\is_string($v) && false !== $v = getenv($k)) {
+                $env[$k] = $v;
+            }
         }
+
+        foreach ($_ENV as $k => $v) {
+            if (\is_string($v)) {
+                $env[$k] = $v;
+            }
+        }
+
+        return $env;
     }
 }
diff --git a/civicrm/vendor/symfony/process/ProcessBuilder.php b/civicrm/vendor/symfony/process/ProcessBuilder.php
index 1bac780d6329ba440d68d21b28262dd330683138..69d13c3f1bca5f3dd36bd113f6935096472c8a18 100644
--- a/civicrm/vendor/symfony/process/ProcessBuilder.php
+++ b/civicrm/vendor/symfony/process/ProcessBuilder.php
@@ -11,28 +11,32 @@
 
 namespace Symfony\Component\Process;
 
+@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use the Process class instead.', ProcessBuilder::class), E_USER_DEPRECATED);
+
 use Symfony\Component\Process\Exception\InvalidArgumentException;
 use Symfony\Component\Process\Exception\LogicException;
 
 /**
  * @author Kris Wallsmith <kris@symfony.com>
+ *
+ * @deprecated since version 3.4, to be removed in 4.0. Use the Process class instead.
  */
 class ProcessBuilder
 {
     private $arguments;
     private $cwd;
-    private $env = array();
+    private $env = [];
     private $input;
     private $timeout = 60;
-    private $options = array();
+    private $options;
     private $inheritEnv = true;
-    private $prefix = array();
+    private $prefix = [];
     private $outputDisabled = false;
 
     /**
      * @param string[] $arguments An array of arguments
      */
-    public function __construct(array $arguments = array())
+    public function __construct(array $arguments = [])
     {
         $this->arguments = $arguments;
     }
@@ -44,7 +48,7 @@ class ProcessBuilder
      *
      * @return static
      */
-    public static function create(array $arguments = array())
+    public static function create(array $arguments = [])
     {
         return new static($arguments);
     }
@@ -74,7 +78,7 @@ class ProcessBuilder
      */
     public function setPrefix($prefix)
     {
-        $this->prefix = \is_array($prefix) ? $prefix : array($prefix);
+        $this->prefix = \is_array($prefix) ? $prefix : [$prefix];
 
         return $this;
     }
@@ -163,13 +167,11 @@ class ProcessBuilder
     /**
      * Sets the input of the process.
      *
-     * @param mixed $input The input as a string
+     * @param resource|string|int|float|bool|\Traversable|null $input The input content
      *
      * @return $this
      *
      * @throws InvalidArgumentException In case the argument is invalid
-     *
-     * Passing an object as an input is deprecated since version 2.5 and will be removed in 3.0.
      */
     public function setInput($input)
     {
@@ -260,20 +262,15 @@ class ProcessBuilder
             throw new LogicException('You must add() command arguments before calling getProcess().');
         }
 
-        $options = $this->options;
-
         $arguments = array_merge($this->prefix, $this->arguments);
-        $script = implode(' ', array_map(array(__NAMESPACE__.'\\ProcessUtils', 'escapeArgument'), $arguments));
+        $process = new Process($arguments, $this->cwd, $this->env, $this->input, $this->timeout, $this->options);
+        // to preserve the BC with symfony <3.3, we convert the array structure
+        // to a string structure to avoid the prefixing with the exec command
+        $process->setCommandLine($process->getCommandLine());
 
         if ($this->inheritEnv) {
-            // include $_ENV for BC purposes
-            $env = array_replace($_ENV, $_SERVER, $this->env);
-        } else {
-            $env = $this->env;
+            $process->inheritEnvironmentVariables();
         }
-
-        $process = new Process($script, $this->cwd, $env, $this->input, $this->timeout, $options);
-
         if ($this->outputDisabled) {
             $process->disableOutput();
         }
diff --git a/civicrm/vendor/symfony/process/ProcessUtils.php b/civicrm/vendor/symfony/process/ProcessUtils.php
index 21e6471c06bda6ce4ed0d1ff21cf2e7518fd3ade..74f2d8654a5a825c06ee4ff9c81b53c3066042bb 100644
--- a/civicrm/vendor/symfony/process/ProcessUtils.php
+++ b/civicrm/vendor/symfony/process/ProcessUtils.php
@@ -35,13 +35,17 @@ class ProcessUtils
      * @param string $argument The argument that will be escaped
      *
      * @return string The escaped argument
+     *
+     * @deprecated since version 3.3, to be removed in 4.0. Use a command line array or give env vars to the `Process::start/run()` method instead.
      */
     public static function escapeArgument($argument)
     {
+        @trigger_error('The '.__METHOD__.'() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use a command line array or give env vars to the Process::start/run() method instead.', E_USER_DEPRECATED);
+
         //Fix for PHP bug #43784 escapeshellarg removes % from given string
         //Fix for PHP bug #49446 escapeshellarg doesn't work on Windows
-        //@see https://bugs.php.net/bug.php?id=43784
-        //@see https://bugs.php.net/bug.php?id=49446
+        //@see https://bugs.php.net/43784
+        //@see https://bugs.php.net/49446
         if ('\\' === \DIRECTORY_SEPARATOR) {
             if ('' === $argument) {
                 return escapeshellarg($argument);
@@ -83,8 +87,6 @@ class ProcessUtils
      * @return mixed The validated input
      *
      * @throws InvalidArgumentException In case the input is not valid
-     *
-     * Passing an object as an input is deprecated since version 2.5 and will be removed in 3.0.
      */
     public static function validateInput($caller, $input)
     {
@@ -98,14 +100,17 @@ class ProcessUtils
             if (is_scalar($input)) {
                 return (string) $input;
             }
-            // deprecated as of Symfony 2.5, to be removed in 3.0
-            if (\is_object($input) && method_exists($input, '__toString')) {
-                @trigger_error('Passing an object as an input is deprecated since Symfony 2.5 and will be removed in 3.0.', E_USER_DEPRECATED);
-
-                return (string) $input;
+            if ($input instanceof Process) {
+                return $input->getIterator($input::ITER_SKIP_ERR);
+            }
+            if ($input instanceof \Iterator) {
+                return $input;
+            }
+            if ($input instanceof \Traversable) {
+                return new \IteratorIterator($input);
             }
 
-            throw new InvalidArgumentException(sprintf('%s only accepts strings or stream resources.', $caller));
+            throw new InvalidArgumentException(sprintf('"%s" only accepts strings, Traversable objects or stream resources.', $caller));
         }
 
         return $input;
diff --git a/civicrm/vendor/symfony/process/composer.json b/civicrm/vendor/symfony/process/composer.json
index b3cb5186fc9408d5849399c1e2b958899eb82ba7..b8867db368038cbe11cc5120991981fc90a6cb48 100644
--- a/civicrm/vendor/symfony/process/composer.json
+++ b/civicrm/vendor/symfony/process/composer.json
@@ -16,7 +16,7 @@
         }
     ],
     "require": {
-        "php": ">=5.3.9"
+        "php": "^5.5.9|>=7.0.8"
     },
     "autoload": {
         "psr-4": { "Symfony\\Component\\Process\\": "" },
@@ -27,7 +27,7 @@
     "minimum-stability": "dev",
     "extra": {
         "branch-alias": {
-            "dev-master": "2.8-dev"
+            "dev-master": "3.4-dev"
         }
     }
 }
diff --git a/civicrm/xml/schema/Contribute/Contribution.xml b/civicrm/xml/schema/Contribute/Contribution.xml
index d2f5593b3de657f5ffd4b7ae7fa1511938f87ed7..ad1656a1be5480a7905127c7597d0235608b9b08 100644
--- a/civicrm/xml/schema/Contribute/Contribution.xml
+++ b/civicrm/xml/schema/Contribute/Contribution.xml
@@ -14,6 +14,9 @@
     <import>true</import>
     <title>Contribution ID</title>
     <comment>Contribution ID</comment>
+    <html>
+      <type>Text</type>
+    </html>
     <add>1.3</add>
   </field>
   <primaryKey>
diff --git a/civicrm/xml/schema/Contribute/ContributionRecur.xml b/civicrm/xml/schema/Contribute/ContributionRecur.xml
index 3ac72066cab1c11be657a7fc700b30b7909585e7..d5295ff4557c05bb75489b2a7e6707500094b456 100644
--- a/civicrm/xml/schema/Contribute/ContributionRecur.xml
+++ b/civicrm/xml/schema/Contribute/ContributionRecur.xml
@@ -41,7 +41,7 @@
     <title>Amount</title>
     <type>decimal</type>
     <required>true</required>
-    <comment>Amount to be contributed or charged each recurrence.</comment>
+    <comment>Amount to be collected (including any sales tax) by payment processor each recurrence.</comment>
     <add>1.6</add>
     <html>
       <type>Text</type>
diff --git a/civicrm/xml/schema/Core/CustomField.xml b/civicrm/xml/schema/Core/CustomField.xml
index 0db65072278a4ceee2e3ebe86aba76c2326fa530..2bfd28999a9ec384683b2e4543d6753740f0bf7c 100644
--- a/civicrm/xml/schema/Core/CustomField.xml
+++ b/civicrm/xml/schema/Core/CustomField.xml
@@ -281,6 +281,17 @@
     <add>5.6</add>
     <onDelete>SET NULL</onDelete>
   </foreignKey>
+  <field>
+    <name>serialize</name>
+    <type>int unsigned</type>
+    <title>Serialize</title>
+    <length>255</length>
+    <comment>Serialization method - a non-null value indicates a multi-valued field.</comment>
+    <pseudoconstant>
+      <callback>CRM_Core_SelectValues::fieldSerialization</callback>
+    </pseudoconstant>
+    <add>5.27</add>
+  </field>
   <field>
     <name>filter</name>
     <type>varchar</type>
diff --git a/civicrm/xml/schema/Core/OptionValue.xml b/civicrm/xml/schema/Core/OptionValue.xml
index f3a767d0645a445a4f519db3fddac5b60fc69734..ce81cc6822a5908685ff55cd5a9996b4816d5c6a 100644
--- a/civicrm/xml/schema/Core/OptionValue.xml
+++ b/civicrm/xml/schema/Core/OptionValue.xml
@@ -82,7 +82,7 @@
   <field>
     <name>filter</name>
     <type>int unsigned</type>
-    <default>NULL</default>
+    <default>0</default>
     <comment>Bitwise logic can be used to create subsets of options within an option_group for different uses.</comment>
     <add>1.5</add>
   </field>
diff --git a/civicrm/xml/schema/Mailing/MailingAB.xml b/civicrm/xml/schema/Mailing/MailingAB.xml
index 8bacee1aa718fe0a41965e4247d0d8827f39b590..14d224f98c98ffbe535e770f10ea00a1348135ad 100644
--- a/civicrm/xml/schema/Mailing/MailingAB.xml
+++ b/civicrm/xml/schema/Mailing/MailingAB.xml
@@ -62,6 +62,7 @@
     <title>Domain ID</title>
     <comment>Which site is this mailing for</comment>
     <add>4.6</add>
+    <required>true</required>
   </field>
   <field>
     <name>testing_criteria</name>
diff --git a/civicrm/xml/schema/Member/MembershipType.xml b/civicrm/xml/schema/Member/MembershipType.xml
index 47a623054846f76c078f8c5ebaac8dd1522e1c87..0007eefaf2fe76a2ca347cc4221a357c54a1b49e 100644
--- a/civicrm/xml/schema/Member/MembershipType.xml
+++ b/civicrm/xml/schema/Member/MembershipType.xml
@@ -43,6 +43,7 @@
     <uniqueName>membership_type</uniqueName>
     <title>Membership Type</title>
     <type>varchar</type>
+    <required>true</required>
     <import>true</import>
     <length>128</length>
     <localizable>true</localizable>
diff --git a/civicrm/xml/schema/Price/PriceField.xml b/civicrm/xml/schema/Price/PriceField.xml
index c3ac6dd662ec5894d4134f2b639d46afad3113b4..0c2c1af3c1a811f468e5a33afc5139fe16d32346 100644
--- a/civicrm/xml/schema/Price/PriceField.xml
+++ b/civicrm/xml/schema/Price/PriceField.xml
@@ -27,6 +27,7 @@
     <pseudoconstant>
       <table>civicrm_price_set</table>
       <keyColumn>id</keyColumn>
+      <nameColumn>name</nameColumn>
       <labelColumn>title</labelColumn>
     </pseudoconstant>
     <add>1.8</add>
diff --git a/civicrm/xml/schema/Price/PriceFieldValue.xml b/civicrm/xml/schema/Price/PriceFieldValue.xml
index 2740ab2e649afe0a9ad9008f8d2b35be392981bc..74c649b916729353fbeecdde983ae791901cbbc0 100644
--- a/civicrm/xml/schema/Price/PriceFieldValue.xml
+++ b/civicrm/xml/schema/Price/PriceFieldValue.xml
@@ -37,6 +37,7 @@
     <title>Name</title>
     <length>255</length>
     <comment>Price field option name</comment>
+    <required>true</required>
     <html>
       <type>Text</type>
     </html>
@@ -49,6 +50,7 @@
     <length>255</length>
     <localizable>true</localizable>
     <comment>Price field option label</comment>
+    <required>true</required>
     <html>
       <type>Text</type>
     </html>
diff --git a/civicrm/xml/templates/civicrm_state_province.tpl b/civicrm/xml/templates/civicrm_state_province.tpl
index f2a7496abebe05e8444f951e908183b4f93ce91e..a51b0e0be809f3ec5be0cca43e120d312d77d1c7 100644
--- a/civicrm/xml/templates/civicrm_state_province.tpl
+++ b/civicrm/xml/templates/civicrm_state_province.tpl
@@ -119,6 +119,7 @@ INSERT INTO civicrm_state_province (id, country_id, abbreviation, name) VALUES
 (1231, 1101, "DL", "Delhi"),
 (1232, 1101, "LD", "Lakshadweep"),
 (1233, 1101, "PY", "Pondicherry"),
+-- Note we believe all lower-case is correct for Poland. See https://github.com/civicrm/civicrm-core/pull/17107
 (1300, 1172, "MZ", "mazowieckie"),
 (1301, 1172, "PM", "pomorskie"),
 (1302, 1172, "DS", "dolnośląskie"),
diff --git a/civicrm/xml/version.xml b/civicrm/xml/version.xml
index 9a71db3eb87ad794aa33ebe38f320612158c1b8b..16b756763f240886f3895136a511fb81660b12b6 100644
--- a/civicrm/xml/version.xml
+++ b/civicrm/xml/version.xml
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="iso-8859-1" ?>
 <version>
-  <version_no>5.26.2</version_no>
+  <version_no>5.27.0</version_no>
 </version>
diff --git a/readme.txt b/readme.txt
index 0cfdc42e914a9242442508df0a310a2b8cffbbbb..926e9dddb3ad352e9c21235d24755e05a55aea51 100755
--- a/readme.txt
+++ b/readme.txt
@@ -1,9 +1,9 @@
 === CiviCRM ===
 Contributors: needle
 Tags: civicrm, crm
-Requires at least: 3.4
-Tested up to: 4.0
-Stable tag: 4.7
+Requires at least: 4.9
+Tested up to: 5.5
+Stable tag: 5.26.1
 License: AGPL3
 License URI: http://www.gnu.org/licenses/agpl-3.0.html
 
@@ -27,7 +27,7 @@ CiviCRM is localized in over 20 languages including: Chinese (Taiwan, China), Du
 == Installation ==
 
 1. Download CiviCRM for WordPress from the [CiviCRM website](https://civicrm.org/download)
-1. Extract the plugin archive 
+1. Extract the plugin archive
 1. Upload plugin files to your plugins directory, normally `/wp-content/plugins/`
 1. Follow the instructions on the [CiviCRM website](http://wiki.civicrm.org/confluence/display/CRMDOC/WordPress+Installation+Guide+for+CiviCRM+4.5)
 
@@ -35,12 +35,4 @@ CiviCRM is localized in over 20 languages including: Chinese (Taiwan, China), Du
 
 == Changelog ==
 
-= 4.5 =
-Support for CiviCRM version 4.5.x
-
-= 4.4 =
-Support for CiviCRM version 4.4.x
-
-= 4.3 =
-Support for CiviCRM version 4.3.x
-
+[Full Release Notes](https://github.com/civicrm/civicrm-core/blob/master/release-notes.md)
diff --git a/wp-rest/Civi/Mailing-Hooks.php b/wp-rest/Civi/Mailing-Hooks.php
index 339ab5eccabbfec96f51171638289770dcb9f56c..e7c2bd3817f0f37ebb744fb81d9d526ceface980 100644
--- a/wp-rest/Civi/Mailing-Hooks.php
+++ b/wp-rest/Civi/Mailing-Hooks.php
@@ -80,12 +80,14 @@ class Mailing_Hooks {
 		if ( $path == 'extern/url' ) {
 			$url = $url
 				->withHost( $this->parsed_rest_url['host'] )
+				->withQuery( $query )
 				->withPath( "{$this->parsed_rest_url['path']}civicrm/v3/url" );
 		}
 
 		if ( $path == 'extern/open' ) {
 			$url = $url
 				->withHost( $this->parsed_rest_url['host'] )
+				->withQuery( $query )
 				->withPath( "{$this->parsed_rest_url['path']}civicrm/v3/open" );
 		}