diff --git a/app/code/Magento/Catalog/Model/ProductRepository.php b/app/code/Magento/Catalog/Model/ProductRepository.php
index 4a72539e982f25591d21818954087a8413c7bb3f..4927a191467239c5c3fdaff033856e4e877de8c7 100644
--- a/app/code/Magento/Catalog/Model/ProductRepository.php
+++ b/app/code/Magento/Catalog/Model/ProductRepository.php
@@ -312,7 +312,7 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa
         if ($this->cacheLimit && count($this->instances) > $this->cacheLimit) {
             $offset = round($this->cacheLimit / -2);
             $this->instancesById = array_slice($this->instancesById, $offset, null, true);
-            $this->instances = array_slice($this->instances, $offset);
+            $this->instances = array_slice($this->instances, $offset, null, true);
         }
     }
 
diff --git a/app/code/Magento/Newsletter/Model/Subscriber.php b/app/code/Magento/Newsletter/Model/Subscriber.php
index 7847098083949baa26b81e408c641f0150b23ecc..9e021a21d23b348ea087160afd8962f65ed6590c 100644
--- a/app/code/Magento/Newsletter/Model/Subscriber.php
+++ b/app/code/Magento/Newsletter/Model/Subscriber.php
@@ -395,6 +395,10 @@ class Subscriber extends \Magento\Framework\Model\AbstractModel
     {
         $this->loadByEmail($email);
 
+        if ($this->getId() && $this->getStatus() == self::STATUS_SUBSCRIBED) {
+            return $this->getStatus();
+        }
+
         if (!$this->getId()) {
             $this->setSubscriberConfirmCode($this->randomSequence());
         }
diff --git a/app/code/Magento/Quote/Model/Quote/Address.php b/app/code/Magento/Quote/Model/Quote/Address.php
index 109ca91cb2ae81ca259665b0d8a39f4968f78026..87c5feaba8f2eef4f140b62f04e7ba294493ca55 100644
--- a/app/code/Magento/Quote/Model/Quote/Address.php
+++ b/app/code/Magento/Quote/Model/Quote/Address.php
@@ -1170,7 +1170,8 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress implements
      */
     public function getAppliedTaxes()
     {
-        return $this->serializer->unserialize($this->getData('applied_taxes'));
+        $taxes = $this->getData('applied_taxes');
+        return $taxes ? $this->serializer->unserialize($taxes) : [];
     }
 
     /**
diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/AddressTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/AddressTest.php
index 1557fe420be025c1b003e0477c5737a0c424f0c8..d01ae7304bdc643c90f14844dcc79fa1b6c4b473 100644
--- a/app/code/Magento/Quote/Test/Unit/Model/Quote/AddressTest.php
+++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/AddressTest.php
@@ -9,7 +9,7 @@
 namespace Magento\Quote\Test\Unit\Model\Quote;
 
 use Magento\Directory\Model\Currency;
-use \Magento\Quote\Model\Quote\Address;
+use Magento\Quote\Model\Quote\Address;
 use Magento\Quote\Model\Quote\Address\Rate;
 use Magento\Quote\Model\ResourceModel\Quote\Address\Rate\CollectionFactory as RateCollectionFactory;
 use Magento\Quote\Model\ResourceModel\Quote\Address\Rate\Collection as RatesCollection;
@@ -28,6 +28,7 @@ use Magento\Store\Model\StoreManagerInterface;
 use Magento\Store\Api\Data\StoreInterface;
 use Magento\Store\Api\Data\WebsiteInterface;
 use Magento\Quote\Model\Quote\Address\RateResult\AbstractResult;
+use Magento\Framework\Serialize\Serializer\Json;
 
 /**
  * Test class for sales quote address model
@@ -117,7 +118,7 @@ class AddressTest extends \PHPUnit\Framework\TestCase
         $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
 
         $this->scopeConfig = $this->createMock(\Magento\Framework\App\Config::class);
-        $this->serializer = $this->createMock(\Magento\Framework\Serialize\Serializer\Json::class);
+        $this->serializer = new Json();
 
         $this->requestFactory = $this->getMockBuilder(RateRequestFactory::class)
             ->disableOriginalConstructor()
@@ -273,20 +274,17 @@ class AddressTest extends \PHPUnit\Framework\TestCase
     public function testSetAndGetAppliedTaxes()
     {
         $data = ['data'];
-        $result = json_encode($data);
-
-        $this->serializer->expects($this->once())
-            ->method('serialize')
-            ->with($data)
-            ->willReturn($result);
-
-        $this->serializer->expects($this->once())
-            ->method('unserialize')
-            ->with($result)
-            ->willReturn($data);
+        self::assertInstanceOf(Address::class, $this->address->setAppliedTaxes($data));
+        self::assertEquals($data, $this->address->getAppliedTaxes());
+    }
 
-        $this->assertInstanceOf(\Magento\Quote\Model\Quote\Address::class, $this->address->setAppliedTaxes($data));
-        $this->assertEquals($data, $this->address->getAppliedTaxes());
+    /**
+     * Checks a case, when applied taxes are not provided.
+     */
+    public function testGetAppliedTaxesWithEmptyValue()
+    {
+        $this->address->setData('applied_taxes', null);
+        self::assertEquals([], $this->address->getAppliedTaxes());
     }
 
     /**
diff --git a/app/code/Magento/Reports/Model/ResourceModel/Review/Customer/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Review/Customer/Collection.php
index a83282396764766773edb8b00731c81a9072ad1d..9297a1dda18d8c522fde78e75c7c63d8145581f4 100644
--- a/app/code/Magento/Reports/Model/ResourceModel/Review/Customer/Collection.php
+++ b/app/code/Magento/Reports/Model/ResourceModel/Review/Customer/Collection.php
@@ -110,13 +110,14 @@ class Collection extends \Magento\Review\Model\ResourceModel\Review\Collection
      */
     public function getSelectCountSql()
     {
-        $countSelect = clone $this->_select;
+        $countSelect = clone $this->getSelect();
         $countSelect->reset(\Magento\Framework\DB\Select::ORDER);
         $countSelect->reset(\Magento\Framework\DB\Select::GROUP);
         $countSelect->reset(\Magento\Framework\DB\Select::HAVING);
         $countSelect->reset(\Magento\Framework\DB\Select::LIMIT_COUNT);
         $countSelect->reset(\Magento\Framework\DB\Select::LIMIT_OFFSET);
         $countSelect->reset(\Magento\Framework\DB\Select::COLUMNS);
+        $countSelect->reset(\Magento\Framework\DB\Select::WHERE);
 
         $countSelect->columns(new \Zend_Db_Expr('COUNT(DISTINCT detail.customer_id)'));
 
diff --git a/app/code/Magento/Sales/Model/AdminOrder/Create.php b/app/code/Magento/Sales/Model/AdminOrder/Create.php
index ccc5c134514f64c8f5dca36bfdb93d571f002b88..c8afc6d8a292b9b79173c80bc4bc9eb003abdf39 100644
--- a/app/code/Magento/Sales/Model/AdminOrder/Create.php
+++ b/app/code/Magento/Sales/Model/AdminOrder/Create.php
@@ -10,6 +10,8 @@ namespace Magento\Sales\Model\AdminOrder;
 
 use Magento\Customer\Api\AddressMetadataInterface;
 use Magento\Customer\Model\Metadata\Form as CustomerForm;
+use Magento\Framework\App\ObjectManager;
+use Magento\Quote\Model\Quote\Address;
 use Magento\Quote\Model\Quote\Item;
 
 /**
@@ -323,7 +325,7 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\
         $this->dataObjectHelper = $dataObjectHelper;
         $this->orderManagement = $orderManagement;
         $this->quoteFactory = $quoteFactory;
-        $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance()
+        $this->serializer = $serializer ?: ObjectManager::getInstance()
             ->get(\Magento\Framework\Serialize\Serializer\Json::class);
         parent::__construct($data);
     }
@@ -1449,32 +1451,36 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\
      */
     public function setBillingAddress($address)
     {
-        if (is_array($address)) {
-            $billingAddress = $this->_objectManager->create(
-                \Magento\Quote\Model\Quote\Address::class
-            )->setData(
-                $address
-            )->setAddressType(
-                \Magento\Quote\Model\Quote\Address::TYPE_BILLING
-            );
-            $this->_setQuoteAddress($billingAddress, $address);
-            /**
-             * save_in_address_book is not a valid attribute and is filtered out by _setQuoteAddress,
-             * that is why it should be added after _setQuoteAddress call
-             */
-            $saveInAddressBook = (int)(!empty($address['save_in_address_book']));
-            $billingAddress->setData('save_in_address_book', $saveInAddressBook);
-
-            if (!$this->getQuote()->isVirtual() && $this->getShippingAddress()->getSameAsBilling()) {
-                $shippingAddress = clone $billingAddress;
-                $shippingAddress->setSameAsBilling(true);
-                $shippingAddress->setSaveInAddressBook(false);
-                $address['save_in_address_book'] = 0;
-                $this->setShippingAddress($address);
-            }
+        if (!is_array($address)) {
+            return $this;
+        }
+
+        $billingAddress = $this->_objectManager->create(Address::class)
+            ->setData($address)
+            ->setAddressType(Address::TYPE_BILLING);
+
+        $this->_setQuoteAddress($billingAddress, $address);
+
+        /**
+         * save_in_address_book is not a valid attribute and is filtered out by _setQuoteAddress,
+         * that is why it should be added after _setQuoteAddress call
+         */
+        $saveInAddressBook = (int)(!empty($address['save_in_address_book']));
+        $billingAddress->setData('save_in_address_book', $saveInAddressBook);
+
+        $quote = $this->getQuote();
+        if (!$quote->isVirtual() && $this->getShippingAddress()->getSameAsBilling()) {
+            $address['save_in_address_book'] = 0;
+            $this->setShippingAddress($address);
+        }
 
-            $this->getQuote()->setBillingAddress($billingAddress);
+        // not assigned billing address should be saved as new
+        // but if quote already has the billing address it won't be overridden
+        if (empty($billingAddress->getCustomerAddressId())) {
+            $billingAddress->setCustomerAddressId(null);
+            $quote->getBillingAddress()->setCustomerAddressId(null);
         }
+        $quote->setBillingAddress($billingAddress);
 
         return $this;
     }
@@ -1775,6 +1781,7 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\
             $address = $this->getShippingAddress()->setCustomerId($this->getQuote()->getCustomer()->getId());
             $this->setShippingAddress($address);
         }
+        $this->getBillingAddress()->setCustomerId($customer->getId());
         $this->getQuote()->updateCustomerData($this->getQuote()->getCustomer());
 
         $customer = $this->getQuote()->getCustomer();
diff --git a/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js b/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js
index 1c755b4e08a0d730e0fbc0bf17587c0869ea684f..8e25584627d3b16bbbfa0b42436d13a3a1855d66 100644
--- a/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js
+++ b/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js
@@ -157,6 +157,7 @@ define([
             }
             if(this.addresses[id]){
                 this.fillAddressFields(container, this.addresses[id]);
+
             }
             else{
                 this.fillAddressFields(container, {});
@@ -190,43 +191,53 @@ define([
             }
         },
 
-        changeAddressField : function(event){
-            var field = Event.element(event);
-            var re = /[^\[]*\[([^\]]*)_address\]\[([^\]]*)\](\[(\d)\])?/;
-            var matchRes = field.name.match(re);
+        /**
+         * Triggers on each form's element changes.
+         *
+         * @param {Object} event
+         */
+        changeAddressField: function (event) {
+            var field = Event.element(event),
+                re = /[^\[]*\[([^\]]*)_address\]\[([^\]]*)\](\[(\d)\])?/,
+                matchRes = field.name.match(re),
+                type,
+                name,
+                data;
 
             if (!matchRes) {
                 return;
             }
 
-            var type = matchRes[1];
-            var name = matchRes[2];
-            var data;
+            type = matchRes[1];
+            name = matchRes[2];
 
-            if(this.isBillingField(field.id)){
-                data = this.serializeData(this.billingAddressContainer)
-            }
-            else{
-                data = this.serializeData(this.shippingAddressContainer)
+            if (this.isBillingField(field.id)) {
+                data = this.serializeData(this.billingAddressContainer);
+            } else {
+                data = this.serializeData(this.shippingAddressContainer);
             }
             data = data.toObject();
 
-            if( (type == 'billing' && this.shippingAsBilling)
-                || (type == 'shipping' && !this.shippingAsBilling) ) {
+            if (type === 'billing' && this.shippingAsBilling || type === 'shipping' && !this.shippingAsBilling) {
                 data['reset_shipping'] = true;
             }
 
-            data['order['+type+'_address][customer_address_id]'] = $('order-'+type+'_address_customer_address_id').value;
+            data['order[' + type + '_address][customer_address_id]'] = null;
+            data['shipping_as_billing'] = jQuery('[name="shipping_same_as_billing"]').is(':checked') ? 1 : 0;
+
+            if (name === 'customer_address_id') {
+                data['order[' + type + '_address][customer_address_id]'] =
+                    $('order-' + type + '_address_customer_address_id').value;
+            }
 
             if (data['reset_shipping']) {
                 this.resetShippingMethod(data);
             } else {
                 this.saveData(data);
-                if (name == 'country_id' || name == 'customer_address_id') {
+
+                if (name === 'country_id' || name === 'customer_address_id') {
                     this.loadArea(['shipping_method', 'billing_method', 'totals', 'items'], true, data);
                 }
-                // added for reloading of default sender and default recipient for giftmessages
-                //this.loadArea(['giftmessage'], true, data);
             }
         },
 
diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/WebApiApplication.php b/dev/tests/api-functional/framework/Magento/TestFramework/WebApiApplication.php
index 03364acbb86634b66c4bcddd8589a78c6d68c105..992653a3a65d6d3bb4cb4f4757781881e693be8c 100644
--- a/dev/tests/api-functional/framework/Magento/TestFramework/WebApiApplication.php
+++ b/dev/tests/api-functional/framework/Magento/TestFramework/WebApiApplication.php
@@ -26,8 +26,12 @@ class WebApiApplication extends Application
     /**
      * {@inheritdoc}
      */
-    public function install()
+    public function install($cleanup)
     {
+        if ($cleanup) {
+            $this->cleanup();
+        }
+
         $installOptions = $this->getInstallConfig();
 
         /* Install application */
diff --git a/dev/tests/api-functional/framework/bootstrap.php b/dev/tests/api-functional/framework/bootstrap.php
index 7a7b061858ac75a609d52c20bd6056801797b2c0..942582392e89e8bff1c99ddefedc523e676912df 100644
--- a/dev/tests/api-functional/framework/bootstrap.php
+++ b/dev/tests/api-functional/framework/bootstrap.php
@@ -58,10 +58,8 @@ try {
     );
 
     if (defined('TESTS_MAGENTO_INSTALLATION') && TESTS_MAGENTO_INSTALLATION === 'enabled') {
-        if (defined('TESTS_CLEANUP') && TESTS_CLEANUP === 'enabled') {
-            $application->cleanup();
-        }
-        $application->install();
+        $cleanup = (defined('TESTS_CLEANUP') && TESTS_CLEANUP === 'enabled');
+        $application->install($cleanup);
     }
 
     $bootstrap = new \Magento\TestFramework\Bootstrap(
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Application.php b/dev/tests/integration/framework/Magento/TestFramework/Application.php
index 2f4395b161a69943786ad79c922702e537dd1107..e0cd3c52e2ca31f9ec6f051ff36cd4b9480e4ded 100644
--- a/dev/tests/integration/framework/Magento/TestFramework/Application.php
+++ b/dev/tests/integration/framework/Magento/TestFramework/Application.php
@@ -435,10 +435,11 @@ class Application
     /**
      * Install an application
      *
+     * @param bool $cleanup
      * @return void
      * @throws \Magento\Framework\Exception\LocalizedException
      */
-    public function install()
+    public function install($cleanup)
     {
         $dirs = \Magento\Framework\App\Bootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS;
         $this->_ensureDirExists($this->installDir);
@@ -453,8 +454,9 @@ class Application
         $installParams = $this->getInstallCliParams();
 
         // performance optimization: restore DB from last good dump to make installation on top of it (much faster)
+        // do not restore from the database if the cleanup option is set to ensure we have a clean DB to test on
         $db = $this->getDbInstance();
-        if ($db->isDbDumpExists()) {
+        if ($db->isDbDumpExists() && !$cleanup) {
             $db->restoreFromDbDump();
         }
 
diff --git a/dev/tests/integration/framework/bootstrap.php b/dev/tests/integration/framework/bootstrap.php
index 13008938147b56431223a3c44cd13ac484a8a189..4e14c8113a708bce1b86f81728a6d4745ab44c7f 100644
--- a/dev/tests/integration/framework/bootstrap.php
+++ b/dev/tests/integration/framework/bootstrap.php
@@ -74,7 +74,7 @@ try {
         $application->cleanup();
     }
     if (!$application->isInstalled()) {
-        $application->install();
+        $application->install($settings->getAsBoolean('TESTS_CLEANUP'));
     }
     $application->initialize([]);
 
diff --git a/dev/tests/integration/testsuite/Magento/Reports/Model/ResourceModel/Review/Customer/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Reports/Model/ResourceModel/Review/Customer/CollectionTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..2decae1acf9acf93426fce9c8adea30a9194a787
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Reports/Model/ResourceModel/Review/Customer/CollectionTest.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Reports\Model\ResourceModel\Review\Customer;
+
+/**
+ * @magentoAppArea adminhtml
+ */
+class CollectionTest extends \PHPUnit\Framework\TestCase
+{
+    /**
+     * @var \Magento\Reports\Model\ResourceModel\Review\Customer\Collection
+     */
+    private $collection;
+
+    protected function setUp()
+    {
+        $this->collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
+            \Magento\Reports\Model\ResourceModel\Review\Customer\Collection::class
+        );
+    }
+
+    /**
+     * This tests covers issue described in:
+     * https://github.com/magento/magento2/issues/10301
+     *
+     * @magentoDataFixture Magento/Review/_files/customer_review.php
+     */
+    public function testSelectCountSql()
+    {
+        $this->collection->addFieldToFilter('customer_name', ['like' => '%john%']);
+        $this->assertEquals(1, $this->collection->getSize());
+    }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php
index 5f3dbdabc640ab50c2283743cb02ba8801f17edc..e071dde26a263ec68a94f0b4b0df2241ac055403 100644
--- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php
@@ -5,6 +5,10 @@
  */
 namespace Magento\Sales\Controller\Adminhtml\Order;
 
+use Magento\Customer\Api\CustomerRepositoryInterface;
+use Magento\Backend\Model\Session\Quote;
+use Magento\Quote\Api\CartRepositoryInterface;
+
 /**
  * @magentoAppArea adminhtml
  * @magentoDbIsolation enabled
@@ -158,7 +162,7 @@ class CreateTest extends \Magento\TestFramework\TestCase\AbstractBackendControll
      */
     public function testGetAclResource($actionName, $reordered, $expectedResult)
     {
-        $this->_objectManager->get(\Magento\Backend\Model\Session\Quote::class)->setReordered($reordered);
+        $this->_objectManager->get(Quote::class)->setReordered($reordered);
         $orderController = $this->_objectManager->get(
             \Magento\Sales\Controller\Adminhtml\Order\Stub\OrderCreateStub::class
         );
@@ -229,4 +233,57 @@ class CreateTest extends \Magento\TestFramework\TestCase\AbstractBackendControll
         $this->dispatch('backend/sales/order_create/save');
         $this->assertEquals('403', $this->getResponse()->getHttpResponseCode());
     }
+
+    /**
+     * Checks a case when shipping is the same as billing and billing address details was changed by request.
+     * Both billing and shipping addresses should be updated.
+     *
+     * @magentoAppArea adminhtml
+     * @magentoDataFixture Magento/Sales/_files/quote_with_customer.php
+     */
+    public function testSyncBetweenQuoteAddresses()
+    {
+        /** @var CustomerRepositoryInterface $customerRepository */
+        $customerRepository = $this->_objectManager->get(CustomerRepositoryInterface::class);
+        $customer = $customerRepository->get('customer@example.com');
+
+        /** @var CartRepositoryInterface $quoteRepository */
+        $quoteRepository = $this->_objectManager->get(CartRepositoryInterface::class);
+        $quote = $quoteRepository->getActiveForCustomer($customer->getId());
+
+        $session = $this->_objectManager->get(Quote::class);
+        $session->setQuoteId($quote->getId());
+
+        $data = [
+            'firstname' => 'John',
+            'lastname' => 'Doe',
+            'street' => ['Soborna 23'],
+            'city' => 'Kyiv',
+            'country_id' => 'UA',
+            'region' => 'Kyivska',
+            'region_id' => 1
+        ];
+        $this->getRequest()->setPostValue(
+            [
+                'order' => ['billing_address' => $data],
+                'reset_shipping' => 1,
+                'customer_id' => $customer->getId(),
+                'store_id' => 1,
+                'json' => true
+            ]
+        );
+
+        $this->dispatch('backend/sales/order_create/loadBlock/block/shipping_address');
+        self::assertEquals(200, $this->getResponse()->getHttpResponseCode());
+
+        $updatedQuote = $quoteRepository->get($quote->getId());
+
+        $billingAddress = $updatedQuote->getBillingAddress();
+        self::assertEquals($data['region_id'], $billingAddress->getRegionId());
+        self::assertEquals($data['country_id'], $billingAddress->getCountryId());
+
+        $shippingAddress = $updatedQuote->getShippingAddress();
+        self::assertEquals($data['city'], $shippingAddress->getCity());
+        self::assertEquals($data['street'], $shippingAddress->getStreet());
+    }
 }
diff --git a/lib/internal/Magento/Framework/App/DeploymentConfig/Writer/PhpFormatter.php b/lib/internal/Magento/Framework/App/DeploymentConfig/Writer/PhpFormatter.php
index 4a40e19c818e73d79f2ae46979373dfd78e2c255..319269bd0011de09180ebaff82c3d2ceb82e0f21 100644
--- a/lib/internal/Magento/Framework/App/DeploymentConfig/Writer/PhpFormatter.php
+++ b/lib/internal/Magento/Framework/App/DeploymentConfig/Writer/PhpFormatter.php
@@ -21,21 +21,48 @@ class PhpFormatter implements FormatterInterface
     public function format($data, array $comments = [])
     {
         if (!empty($comments) && is_array($data)) {
-            $elements = [];
+            return "<?php\nreturn array (\n" . $this->formatData($data, $comments, '  ') . "\n);\n";
+        }
+        return "<?php\nreturn " . var_export($data, true) . ";\n";
+    }
+
+    /**
+     * Format supplied data
+     *
+     * @param $data
+     * @param $comments
+     * @param string $prefix
+     * @return string
+     */
+    protected function formatData($data, $comments, $prefix = '')
+    {
+        $elements = [];
+
+        if (is_array($data)) {
             foreach ($data as $key => $value) {
-                $comment = '  ';
                 if (!empty($comments[$key])) {
-                    $section = " * For the section: " . $key . "\n";
-                    $exportedComment = is_string($comments[$key])
-                        ? $comments[$key]
-                        : var_export($comments[$key], true);
-                    $comment = "  /**\n" . $section . " * " . str_replace("\n", "\n * ", $exportedComment) . "\n */\n";
+                    $elements[] = $prefix . '/**';
+                    $elements[] = $prefix . ' * For the section: ' . $key;
+
+                    foreach (explode("\n", $comments[$key]) as $commentLine) {
+                        $elements[] = $prefix . ' * ' . $commentLine;
+                    }
+
+                    $elements[] = $prefix . " */";
+                }
+
+                $elements[] = $prefix . var_export($key, true) . ' => ' .
+                    (!is_array($value) ? var_export($value, true) . ',' : '');
+
+                if (is_array($value)) {
+                    $elements[] = $prefix . 'array (';
+                    $elements[] = $this->formatData($value, [], '  ' . $prefix);
+                    $elements[] = $prefix . '),';
                 }
-                $space = is_array($value) ? " \n" : ' ';
-                $elements[] = $comment . var_export($key, true) . ' =>' . $space . var_export($value, true);
             }
-            return "<?php\nreturn array (\n" . implode(",\n", str_replace("\n", "\n  ", $elements)) . "\n);\n";
+            return implode("\n", $elements);
         }
-        return "<?php\nreturn " . var_export($data, true) . ";\n";
+
+        return var_export($data, true);
     }
 }
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfig/Writer/PhpFormatterTest.php b/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfig/Writer/PhpFormatterTest.php
index d77edbf86aaad7c13190707925a5ed3a86a485e9..fe57a5a7ad8f3e0416390dcf4099aa3acae11aaf 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfig/Writer/PhpFormatterTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfig/Writer/PhpFormatterTest.php
@@ -5,7 +5,7 @@
  */
 namespace Magento\Framework\App\Test\Unit\DeploymentConfig\Writer;
 
-use \Magento\Framework\App\DeploymentConfig\Writer\PhpFormatter;
+use Magento\Framework\App\DeploymentConfig\Writer\PhpFormatter;
 
 class PhpFormatterTest extends \PHPUnit\Framework\TestCase
 {
@@ -81,7 +81,7 @@ return array (
     ),
   ),
   'ns3' => 'just text',
-  'ns4' => 'just text'
+  'ns4' => 'just text',
 );
 
 TEXT;
@@ -126,7 +126,7 @@ return array (
    * For the section: ns4
    * comment for namespace 4
    */
-  'ns4' => 'just text'
+  'ns4' => 'just text',
 );
 
 TEXT;
diff --git a/lib/internal/Magento/Framework/Shell/CommandRenderer.php b/lib/internal/Magento/Framework/Shell/CommandRenderer.php
index 32e75a7420d1eaba91ec454fffd6bf87ee120a7a..6cf91d09479e1e7c4cb51498c097583740c4a340 100644
--- a/lib/internal/Magento/Framework/Shell/CommandRenderer.php
+++ b/lib/internal/Magento/Framework/Shell/CommandRenderer.php
@@ -16,8 +16,8 @@ class CommandRenderer implements CommandRendererInterface
      */
     public function render($command, array $arguments = [])
     {
+        $command = preg_replace('/(\s+2>&1)*(\s*\|)|$/', ' 2>&1$2', $command);
         $arguments = array_map('escapeshellarg', $arguments);
-        $command = preg_replace('/\s?\||$/', ' 2>&1$0', $command);
         if (empty($arguments)) {
             return $command;
         }
diff --git a/lib/internal/Magento/Framework/Shell/Test/Unit/CommandRendererTest.php b/lib/internal/Magento/Framework/Shell/Test/Unit/CommandRendererTest.php
index dcc091473eeec9f040d15b0ccbf35417a3d6389b..213e2afa2c6551903c47559cf127bf004278e90b 100644
--- a/lib/internal/Magento/Framework/Shell/Test/Unit/CommandRendererTest.php
+++ b/lib/internal/Magento/Framework/Shell/Test/Unit/CommandRendererTest.php
@@ -5,27 +5,40 @@
  */
 namespace Magento\Framework\Shell\Test\Unit;
 
-use \Magento\Framework\Shell\CommandRenderer;
+use Magento\Framework\Shell\CommandRenderer;
 
 class CommandRendererTest extends \PHPUnit\Framework\TestCase
 {
-    public function testRender()
+    /**
+     * @param $expectedCommand
+     * @param $actualCommand
+     * @param $testArguments
+     * @dataProvider commandsDataProvider
+     */
+    public function testRender($expectedCommand, $actualCommand, $testArguments)
     {
-        $testArgument  = 'argument';
-        $testArgument2 = 'argument2';
         $commandRenderer = new CommandRenderer();
         $this->assertEquals(
-            "php -r " . escapeshellarg($testArgument) . " 2>&1 | grep " . escapeshellarg($testArgument2) . " 2>&1",
-            $commandRenderer->render('php -r %s | grep %s', [$testArgument, $testArgument2])
+            $expectedCommand,
+            $commandRenderer->render($actualCommand, $testArguments)
         );
     }
 
-    public function testRenderWithoutArguments()
+    public function commandsDataProvider()
     {
-        $commandRenderer = new CommandRenderer();
-        $this->assertEquals(
-            "php -r %s 2>&1 | grep %s 2>&1",
-            $commandRenderer->render('php -r %s | grep %s', [])
-        );
+        $testArgument  = 'argument';
+        $testArgument2 = 'argument2';
+
+        $expectedCommand = "php -r %s 2>&1 | grep %s 2>&1";
+        $expectedCommandArgs = "php -r '" . $testArgument . "' 2>&1 | grep '" . $testArgument2 . "' 2>&1";
+
+        return [
+            [$expectedCommand, 'php -r %s | grep %s', []],
+            [$expectedCommand, 'php -r %s 2>&1 | grep %s', []],
+            [$expectedCommand, 'php -r %s 2>&1 2>&1 | grep %s', []],
+            [$expectedCommandArgs, 'php -r %s | grep %s', [$testArgument, $testArgument2]],
+            [$expectedCommandArgs, 'php -r %s 2>&1 | grep %s', [$testArgument, $testArgument2]],
+            [$expectedCommandArgs, 'php -r %s 2>&1 2>&1 | grep %s', [$testArgument, $testArgument2]],
+        ];
     }
 }
diff --git a/lib/internal/Magento/Framework/View/Page/Config.php b/lib/internal/Magento/Framework/View/Page/Config.php
index 248841e47499dd88fc60ed5a7b601aa066d51511..6fadc91bd73eeca60226ec70108a1479293415f3 100644
--- a/lib/internal/Magento/Framework/View/Page/Config.php
+++ b/lib/internal/Magento/Framework/View/Page/Config.php
@@ -173,7 +173,7 @@ class Config
         $this->setElementAttribute(
             self::ELEMENT_TYPE_HTML,
             self::HTML_ATTRIBUTE_LANG,
-            str_replace('_', '-', $this->localeResolver->getLocale())
+            strstr($this->localeResolver->getLocale(), '_', true)
         );
     }
 
diff --git a/setup/src/Magento/Setup/Console/Command/AdminUserCreateCommand.php b/setup/src/Magento/Setup/Console/Command/AdminUserCreateCommand.php
index 940bcbbd6d7f1a2856637e279830c5040439e5d3..4ad8e7c229bfdedd5084182e4289f801faa7cc62 100644
--- a/setup/src/Magento/Setup/Console/Command/AdminUserCreateCommand.php
+++ b/setup/src/Magento/Setup/Console/Command/AdminUserCreateCommand.php
@@ -6,13 +6,14 @@
 
 namespace Magento\Setup\Console\Command;
 
-use Magento\Setup\Model\AdminAccount;
 use Magento\Framework\Setup\ConsoleLogger;
+use Magento\Setup\Model\AdminAccount;
 use Magento\Setup\Model\InstallerFactory;
 use Magento\User\Model\UserValidationRules;
-use Symfony\Component\Console\Input\InputOption;
 use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
 use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Question\Question;
 
 class AdminUserCreateCommand extends AbstractSetupCommand
 {
@@ -50,6 +51,98 @@ class AdminUserCreateCommand extends AbstractSetupCommand
         parent::configure();
     }
 
+    /**
+     * @param \Symfony\Component\Console\Input\InputInterface $input
+     * @param \Symfony\Component\Console\Output\OutputInterface $output
+     *
+     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+     */
+    protected function interact(InputInterface $input, OutputInterface $output)
+    {
+        /** @var \Symfony\Component\Console\Helper\QuestionHelper $questionHelper */
+        $questionHelper = $this->getHelper('question');
+
+        if (!$input->getOption(AdminAccount::KEY_USER)) {
+            $question = new Question('<question>Admin user:</question> ', '');
+            $this->addNotEmptyValidator($question);
+
+            $input->setOption(
+                AdminAccount::KEY_USER,
+                $questionHelper->ask($input, $output, $question)
+            );
+        }
+
+        if (!$input->getOption(AdminAccount::KEY_PASSWORD)) {
+            $question = new Question('<question>Admin password:</question> ', '');
+            $question->setHidden(true);
+
+            $question->setValidator(function ($value) use ($output) {
+                $user = new \Magento\Framework\DataObject();
+                $user->setPassword($value);
+
+                $validator = new \Magento\Framework\Validator\DataObject();
+                $this->validationRules->addPasswordRules($validator);
+
+                $validator->isValid($user);
+                foreach ($validator->getMessages() as $message) {
+                    throw new \Exception($message);
+                }
+
+                return $value;
+            });
+
+            $input->setOption(
+                AdminAccount::KEY_PASSWORD,
+                $questionHelper->ask($input, $output, $question)
+            );
+        }
+
+        if (!$input->getOption(AdminAccount::KEY_EMAIL)) {
+            $question = new Question('<question>Admin email:</question> ', '');
+            $this->addNotEmptyValidator($question);
+
+            $input->setOption(
+                AdminAccount::KEY_EMAIL,
+                $questionHelper->ask($input, $output, $question)
+            );
+        }
+
+        if (!$input->getOption(AdminAccount::KEY_FIRST_NAME)) {
+            $question = new Question('<question>Admin first name:</question> ', '');
+            $this->addNotEmptyValidator($question);
+
+            $input->setOption(
+                AdminAccount::KEY_FIRST_NAME,
+                $questionHelper->ask($input, $output, $question)
+            );
+        }
+
+        if (!$input->getOption(AdminAccount::KEY_LAST_NAME)) {
+            $question = new Question('<question>Admin last name:</question> ', '');
+            $this->addNotEmptyValidator($question);
+
+            $input->setOption(
+                AdminAccount::KEY_LAST_NAME,
+                $questionHelper->ask($input, $output, $question)
+            );
+        }
+    }
+
+    /**
+     * @param \Symfony\Component\Console\Question\Question $question
+     * @return void
+     */
+    private function addNotEmptyValidator(Question $question)
+    {
+        $question->setValidator(function ($value) {
+            if (trim($value) == '') {
+                throw new \Exception('The value cannot be empty');
+            }
+
+            return $value;
+        });
+    }
+
     /**
      * {@inheritdoc}
      */
@@ -57,7 +150,7 @@ class AdminUserCreateCommand extends AbstractSetupCommand
     {
         $errors = $this->validate($input);
         if ($errors) {
-            $output->writeln('<error>' . implode('</error>' . PHP_EOL .  '<error>', $errors) . '</error>');
+            $output->writeln('<error>' . implode('</error>' . PHP_EOL . '<error>', $errors) . '</error>');
             // we must have an exit code higher than zero to indicate something was wrong
             return \Magento\Framework\Console\Cli::RETURN_FAILURE;
         }
@@ -113,7 +206,7 @@ class AdminUserCreateCommand extends AbstractSetupCommand
                 ? '' : $input->getOption(AdminAccount::KEY_PASSWORD)
             );
 
-        $validator = new \Magento\Framework\Validator\DataObject;
+        $validator = new \Magento\Framework\Validator\DataObject();
         $this->validationRules->addUserInfoRules($validator);
         $this->validationRules->addPasswordRules($validator);
 
diff --git a/setup/src/Magento/Setup/Test/Unit/Console/Command/AdminUserCreateCommandTest.php b/setup/src/Magento/Setup/Test/Unit/Console/Command/AdminUserCreateCommandTest.php
index 1cfd0c9494a519dcf4b22f32033f2fc2a30b3d75..d244f48d4e1ea3b90fad815e4aa8268886cbb690 100644
--- a/setup/src/Magento/Setup/Test/Unit/Console/Command/AdminUserCreateCommandTest.php
+++ b/setup/src/Magento/Setup/Test/Unit/Console/Command/AdminUserCreateCommandTest.php
@@ -5,14 +5,21 @@
  */
 namespace Magento\Setup\Test\Unit\Console\Command;
 
-use Magento\Setup\Model\AdminAccount;
 use Magento\Setup\Console\Command\AdminUserCreateCommand;
+use Magento\Setup\Model\AdminAccount;
 use Magento\Setup\Mvc\Bootstrap\InitParamListener;
 use Magento\User\Model\UserValidationRules;
+use Symfony\Component\Console\Application;
+use Symfony\Component\Console\Helper\QuestionHelper;
 use Symfony\Component\Console\Tester\CommandTester;
 
 class AdminUserCreateCommandTest extends \PHPUnit\Framework\TestCase
 {
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject|\Symfony\Component\Console\Helper\QuestionHelper
+     */
+    private $questionHelperMock;
+
     /**
      * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Setup\Model\InstallerFactory
      */
@@ -27,6 +34,10 @@ class AdminUserCreateCommandTest extends \PHPUnit\Framework\TestCase
     {
         $this->installerFactoryMock = $this->createMock(\Magento\Setup\Model\InstallerFactory::class);
         $this->command = new AdminUserCreateCommand($this->installerFactoryMock, new UserValidationRules());
+
+        $this->questionHelperMock = $this->getMockBuilder(QuestionHelper::class)
+            ->setMethods(['ask'])
+            ->getMock();
     }
 
     public function testExecute()
@@ -50,10 +61,70 @@ class AdminUserCreateCommandTest extends \PHPUnit\Framework\TestCase
         $installerMock = $this->createMock(\Magento\Setup\Model\Installer::class);
         $installerMock->expects($this->once())->method('installAdminUser')->with($data);
         $this->installerFactoryMock->expects($this->once())->method('create')->willReturn($installerMock);
-        $commandTester->execute($options);
+        $commandTester->execute($options, ['interactive' => false]);
         $this->assertEquals('Created Magento administrator user named user' . PHP_EOL, $commandTester->getDisplay());
     }
 
+    public function testInteraction()
+    {
+        $application = new Application();
+        $application->add($this->command);
+
+        $this->questionHelperMock->expects($this->at(0))
+            ->method('ask')
+            ->will($this->returnValue('admin'));
+
+        $this->questionHelperMock->expects($this->at(1))
+            ->method('ask')
+            ->will($this->returnValue('Password123'));
+
+        $this->questionHelperMock->expects($this->at(2))
+            ->method('ask')
+            ->will($this->returnValue('john.doe@example.com'));
+
+        $this->questionHelperMock->expects($this->at(3))
+            ->method('ask')
+            ->will($this->returnValue('John'));
+
+        $this->questionHelperMock->expects($this->at(4))
+            ->method('ask')
+            ->will($this->returnValue('Doe'));
+
+        // We override the standard helper with our mock
+        $this->command->getHelperSet()->set($this->questionHelperMock, 'question');
+
+        $installerMock = $this->createMock(\Magento\Setup\Model\Installer::class);
+
+        $expectedData = [
+            'admin-user' => 'admin',
+            'admin-password' => 'Password123',
+            'admin-email' => 'john.doe@example.com',
+            'admin-firstname' => 'John',
+            'admin-lastname' => 'Doe',
+            'magento-init-params' => null,
+            'help' => false,
+            'quiet' => false,
+            'verbose' => false,
+            'version' => false,
+            'ansi' => false,
+            'no-ansi' => false,
+            'no-interaction' => false,
+        ];
+
+        $installerMock->expects($this->once())->method('installAdminUser')->with($expectedData);
+        $this->installerFactoryMock->expects($this->once())->method('create')->willReturn($installerMock);
+
+        $commandTester = new CommandTester($this->command);
+        $commandTester->execute([
+            'command' => $this->command->getName(),
+        ]);
+
+        $this->assertEquals(
+            'Created Magento administrator user named admin' . PHP_EOL,
+            $commandTester->getDisplay()
+        );
+    }
+
     public function testGetOptionsList()
     {
         /* @var $argsList \Symfony\Component\Console\Input\InputArgument[] */