diff --git a/docs/internals/getting-started.md b/docs/internals/getting-started.md
index 4b5d9de..9d4b8a6 100644
--- a/docs/internals/getting-started.md
+++ b/docs/internals/getting-started.md
@@ -1,43 +1,6 @@
 Getting started with Yii2 development
 =====================================
 
-The best way to have a locally runnable webapp that uses codebase cloned from main repository is to use `yii2-dev`
-Composer package. Here's how to do it:
-
-1. `git clone https://github.com/yiisoft/yii2-app-basic`.
-2. Remove `.git` directory from cloned directory.
-3. Change `composer.json`. Instead of all stable requirements add just one `"yiisoft/yii2-dev": "*"`.
-4. Execute `composer create-project`. Do not add `--prefer-dist` to the command since it will not download git repository
-   then.
-5. Now you have working playground that uses latest code.
-
-Note that requirements of extensions that come with `yii2-dev` are not loaded automatically.
-If you want to use an extension, check if there are dependencies suggested for it and add them
-to your `composer.json`. You can see suggested packages by running `composer show yiisoft/yii2-dev`.
-
-If you're core developer there's no extra step needed. You can change framework code under
-`vendor/yiisoft/yii2-dev` and push it to main repository.
-
-If you're not core developer or want to use your own fork for pull requests:
-
-1. Fork `https://github.com/yiisoft/yii2` and get your own repository address such as
-   `git://github.com/username/yii2.git`.
-2. Edit `vendor/yiisoft/yii2-dev/.git/config`. Change remote `origin` url to your own:
-
-```
-[remote "origin"]
-  url = git://github.com/username/yii2.git
-```
-
-> Hint: The workflow of forking a package and pushing changes back into your fork and then sending a pull-request to the
-  maintainer is the same for all extensions you require via composer.
-
-Please refer to [Git workflow for Yii 2 contributors](git-workflow.md) for details about creating pull requests.
-
-
-An Alternative way
-------------------
-
 1. Clone your fork of yii2 `git clone git@github.com:<yourname>/yii2`.
 2. Change into the repo folder `cd yii2`.
 3. run `./build/build app/link basic` to install composer dependecies for the basic app.
@@ -53,6 +16,8 @@ You may also add the yii2 upstream repo to pull the latest changes:
 git remote add upstream https://github.com/yiisoft/yii2.git
 ```
 
+Please refer to [Git workflow for Yii 2 contributors](git-workflow.md) for details about creating pull requests.
+
 ### Unit tests
 
 To run the unit tests you have to install composer packages for the dev-repo.
diff --git a/extensions/authclient/OAuth1.php b/extensions/authclient/OAuth1.php
index 265c927..2d78410 100644
--- a/extensions/authclient/OAuth1.php
+++ b/extensions/authclient/OAuth1.php
@@ -189,9 +189,7 @@ class OAuth1 extends BaseOAuth
                 }
                 break;
             }
-            case 'HEAD':
-            case 'PUT':
-            case 'DELETE': {
+            case 'HEAD': {
                 $curlOptions[CURLOPT_CUSTOMREQUEST] = $method;
                 if (!empty($params)) {
                     $curlOptions[CURLOPT_URL] = $this->composeUrl($url, $params);
@@ -199,7 +197,10 @@ class OAuth1 extends BaseOAuth
                 break;
             }
             default: {
-                throw new Exception("Unknown request method '{$method}'.");
+                $curlOptions[CURLOPT_CUSTOMREQUEST] = $method;
+                if (!empty($params)) {
+                    $curlOptions[CURLOPT_POSTFIELDS] = $params;
+                }
             }
         }
 
diff --git a/extensions/authclient/OAuth2.php b/extensions/authclient/OAuth2.php
index 76f22a7..40b5192 100644
--- a/extensions/authclient/OAuth2.php
+++ b/extensions/authclient/OAuth2.php
@@ -114,9 +114,7 @@ class OAuth2 extends BaseOAuth
                 $curlOptions[CURLOPT_POSTFIELDS] = http_build_query($params, '', '&', PHP_QUERY_RFC3986);
                 break;
             }
-            case 'HEAD':
-            case 'PUT':
-            case 'DELETE': {
+            case 'HEAD': {
                 $curlOptions[CURLOPT_CUSTOMREQUEST] = $method;
                 if (!empty($params)) {
                     $curlOptions[CURLOPT_URL] = $this->composeUrl($url, $params);
@@ -124,7 +122,10 @@ class OAuth2 extends BaseOAuth
                 break;
             }
             default: {
-                throw new Exception("Unknown request method '{$method}'.");
+                $curlOptions[CURLOPT_CUSTOMREQUEST] = $method;
+                if (!empty($params)) {
+                    $curlOptions[CURLOPT_POSTFIELDS] = $params;
+                }
             }
         }
 
diff --git a/extensions/authclient/OpenId.php b/extensions/authclient/OpenId.php
index 8b8b555..b26e5fe 100644
--- a/extensions/authclient/OpenId.php
+++ b/extensions/authclient/OpenId.php
@@ -229,14 +229,14 @@ class OpenId extends BaseClient implements ClientInterface
     }
 
     /**
-     * Sends HTTP request.
+     * Sends request to the server
      * @param string $url request URL.
      * @param string $method request method.
-     * @param array $params request params.
+     * @param array $params request parameters.
      * @return array|string response.
      * @throws \yii\base\Exception on failure.
      */
-    protected function sendCurlRequest($url, $method = 'GET', $params = [])
+    protected function sendRequest($url, $method = 'GET', $params = [])
     {
         $params = http_build_query($params, '', '&');
         $curl = curl_init($url . ($method == 'GET' && $params ? '?' . $params : ''));
@@ -286,112 +286,6 @@ class OpenId extends BaseClient implements ClientInterface
     }
 
     /**
-     * Sends HTTP request.
-     * @param string $url request URL.
-     * @param string $method request method.
-     * @param array $params request params.
-     * @return array|string response.
-     * @throws \yii\base\Exception on failure.
-     * @throws \yii\base\NotSupportedException if request method is not supported.
-     */
-    protected function sendStreamRequest($url, $method = 'GET', $params = [])
-    {
-        if (!$this->hostExists($url)) {
-            throw new Exception('Invalid request.');
-        }
-
-        $params = http_build_query($params, '', '&');
-        switch ($method) {
-            case 'GET':
-                $options = [
-                    'http' => [
-                        'method' => 'GET',
-                        'header' => 'Accept: application/xrds+xml, */*',
-                        'ignore_errors' => true,
-                    ]
-                ];
-                $url = $url . ($params ? '?' . $params : '');
-                break;
-            case 'POST':
-                $options = [
-                    'http' => [
-                        'method' => 'POST',
-                        'header'  => 'Content-type: application/x-www-form-urlencoded',
-                        'content' => $params,
-                        'ignore_errors' => true,
-                    ]
-                ];
-                break;
-            case 'HEAD':
-                /* We want to send a HEAD request,
-                but since get_headers doesn't accept $context parameter,
-                we have to change the defaults.*/
-                $default = stream_context_get_options(stream_context_get_default());
-                stream_context_get_default([
-                    'http' => [
-                        'method' => 'HEAD',
-                        'header' => 'Accept: application/xrds+xml, */*',
-                        'ignore_errors' => true,
-                    ]
-                ]);
-
-                $url = $url . ($params ? '?' . $params : '');
-                $headersTmp = get_headers($url);
-                if (empty($headersTmp)) {
-                    return [];
-                }
-
-                // Parsing headers.
-                $headers = [];
-                foreach ($headersTmp as $header) {
-                    $pos = strpos($header, ':');
-                    $name = strtolower(trim(substr($header, 0, $pos)));
-                    $headers[$name] = trim(substr($header, $pos + 1));
-                }
-
-                // and restore them
-                stream_context_get_default($default);
-
-                return $headers;
-            default:
-                throw new NotSupportedException("Method {$method} not supported");
-        }
-
-        if ($this->verifyPeer) {
-            $options = array_merge(
-                $options,
-                [
-                    'ssl' => [
-                        'verify_peer' => true,
-                        'capath' => $this->capath,
-                        'cafile' => $this->cainfo,
-                    ]
-                ]
-            );
-        }
-
-        $context = stream_context_create($options);
-
-        return file_get_contents($url, false, $context);
-    }
-
-    /**
-     * Sends request to the server
-     * @param string $url request URL.
-     * @param string $method request method.
-     * @param array $params request parameters.
-     * @return array|string response.
-     */
-    protected function sendRequest($url, $method = 'GET', $params = [])
-    {
-        if (function_exists('curl_init') && !ini_get('safe_mode')) {
-            return $this->sendCurlRequest($url, $method, $params);
-        }
-
-        return $this->sendStreamRequest($url, $method, $params);
-    }
-
-    /**
      * Combines given URLs into single one.
      * @param string $baseUrl base URL.
      * @param string|array $additionalUrl additional URL string or information array.
diff --git a/extensions/elasticsearch/README.md b/extensions/elasticsearch/README.md
index fe08f5d..ee569fe 100644
--- a/extensions/elasticsearch/README.md
+++ b/extensions/elasticsearch/README.md
@@ -144,11 +144,11 @@ $customer->save();
 
 $customer = Customer::get(1); // get a record by pk
 $customers = Customer::mget([1,2,3]); // get multiple records by pk
-$customer = Customer::find()->where(['name' => 'test'])->one(); // find by query
+$customer = Customer::find()->where(['name' => 'test'])->one(); // find by query, note that you need to configure mapping for this field in order to find records properly
 $customers = Customer::find()->active()->all(); // find all by query (using the `active` scope)
 
-// http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-field-query.html
-$result = Article::find()->query(["field" => ["title" => "yii"]])->all(); // articles whose title contains "yii"
+// http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-match-query.html
+$result = Article::find()->query(["match" => ["title" => "yii"]])->all(); // articles whose title contains "yii"
 
 // http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-flt-query.html
 $query = Article::find()->query([
diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md
index 8c11d13..836528b 100644
--- a/framework/CHANGELOG.md
+++ b/framework/CHANGELOG.md
@@ -139,6 +139,7 @@ Yii Framework 2 Change Log
 - Enh #4086: changedAttributes of afterSave Event now contain old values (dizews)
 - Enh #4114: Added `Security::generateRandomBytes()`, improved tests (samdark)
 - Enh #4209: Added `beforeCopy`, `afterCopy`, `forceCopy` properties to AssetManager (cebe)
+- Enh #4297: Added check for DOM extension to requirements (samdark)
 - Enh #4317: Added `absoluteAuthTimeout` to yii\web\User (ivokund, nkovacs)
 - Enh: Added support for using sub-queries when building a DB query with `IN` condition (qiangxue)
 - Enh: Supported adding a new response formatter without the need to reconfigure existing formatters (qiangxue)
diff --git a/framework/messages/pt-PT/yii.php b/framework/messages/pt/yii.php
similarity index 100%
rename from framework/messages/pt-PT/yii.php
rename to framework/messages/pt/yii.php
diff --git a/framework/requirements/requirements.php b/framework/requirements/requirements.php
index 05e18bf..0be98bf 100644
--- a/framework/requirements/requirements.php
+++ b/framework/requirements/requirements.php
@@ -62,4 +62,11 @@ return array(
         'by' => '<a href="http://www.php.net/manual/en/book.fileinfo.php">File Information</a>',
         'memo' => 'Required for files upload to detect correct file mime-types.'
     ),
+    array(
+        'name' => 'DOM extension',
+        'mandatory' => false,
+        'condition' => extension_loaded('dom'),
+        'by' => '<a href="http://php.net/manual/en/book.dom.php">Document Object Model</a>',
+        'memo' => 'Required for REST API to send XML responses via <code>yii\web\XmlResponseFormatter</code>.'
+    ),
 );
diff --git a/framework/validators/FileValidator.php b/framework/validators/FileValidator.php
index 77e49c4..90f9580 100644
--- a/framework/validators/FileValidator.php
+++ b/framework/validators/FileValidator.php
@@ -71,6 +71,8 @@ class FileValidator extends Validator
     public $message;
     /**
      * @var string the error message used when no file is uploaded.
+     * Note that this is the text of the validation error message. To make uploading files required,
+     * you have to set [[skipOnEmpty]] to `false`.
      */
     public $uploadRequired;
     /**