From 661a4c74de93a1591ea83cdae26da6547cb2dfa7 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Sat, 27 Nov 2021 00:37:27 +0100 Subject: [PATCH 1/2] CS: normalize control structures Previously this codebase mostly placed the `else` (or other follow-on keyword in a multi-part control structure) on a new line, except this was not enforced leading to inconsistency across the codebase. By removing the exclusion from the PHPCS ruleset, we now enforce the follow-on keyword in a multi-part control structure to be on the same line as the closing brace of the preceding control structure. This is in line with both WPCS as well as PSR-12. --- .phpcs.xml.dist | 6 ---- src/Cookie.php | 24 ++++++---------- src/IdnaEncoder.php | 49 +++++++++++--------------------- src/Ipv6.php | 28 ++++++++---------- src/Proxy/Http.php | 9 ++---- src/Requests.php | 21 +++++--------- src/Response.php | 3 +- src/Session.php | 3 +- src/Transport/Curl.php | 36 ++++++++--------------- src/Transport/Fsockopen.php | 30 +++++++------------ tests/Transport/BaseTestCase.php | 9 ++---- tests/bootstrap.php | 3 +- 12 files changed, 75 insertions(+), 146 deletions(-) diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist index f032603ff..b345144aa 100644 --- a/.phpcs.xml.dist +++ b/.phpcs.xml.dist @@ -95,12 +95,6 @@ - - - diff --git a/src/Cookie.php b/src/Cookie.php index 1a33ec075..b9347bd13 100644 --- a/src/Cookie.php +++ b/src/Cookie.php @@ -331,8 +331,7 @@ protected function normalize_attribute($name, $value) { $delta_seconds = (int) $value; if ($delta_seconds <= 0) { $expiry_time = 0; - } - else { + } else { $expiry_time = $this->reference_time + $delta_seconds; } @@ -383,8 +382,7 @@ public function format_for_set_cookie() { // Ignore non-associative attributes if (is_numeric($key)) { $parts[] = $value; - } - else { + } else { $parts[] = sprintf('%s=%s', $key, $value); } } @@ -423,8 +421,7 @@ public static function parse($cookie_header, $name = '', $reference_time = null) if (!empty($name)) { $value = $cookie_header; - } - elseif (strpos($kvparts, '=') === false) { + } elseif (strpos($kvparts, '=') === false) { // Some sites might only have a value without the equals separator. // Deviate from RFC 6265 and pretend it was actually a blank name // (`=foo`) @@ -432,8 +429,7 @@ public static function parse($cookie_header, $name = '', $reference_time = null) // https://bugzilla.mozilla.org/show_bug.cgi?id=169091 $name = ''; $value = $kvparts; - } - else { + } else { list($name, $value) = explode('=', $kvparts, 2); } $name = trim($name); @@ -447,8 +443,7 @@ public static function parse($cookie_header, $name = '', $reference_time = null) if (strpos($part, '=') === false) { $part_key = $part; $part_value = true; - } - else { + } else { list($part_key, $part_value) = explode('=', $part, 2); $part_value = trim($part_value); } @@ -483,8 +478,7 @@ public static function parse_from_headers(Headers $headers, Iri $origin = null, if (empty($parsed->attributes['domain']) && !empty($origin)) { $parsed->attributes['domain'] = $origin->host; $parsed->flags['host-only'] = true; - } - else { + } else { $parsed->flags['host-only'] = false; } @@ -498,14 +492,12 @@ public static function parse_from_headers(Headers $headers, Iri $origin = null, // the uri-path is not a %x2F ("/") character, output // %x2F ("/") and skip the remaining steps. $path = '/'; - } - elseif (substr_count($path, '/') === 1) { + } elseif (substr_count($path, '/') === 1) { // If the uri-path contains no more than one %x2F ("/") // character, output %x2F ("/") and skip the remaining // step. $path = '/'; - } - else { + } else { // Output the characters of the uri-path from the first // character up to, but not including, the right-most // %x2F ("/"). diff --git a/src/IdnaEncoder.php b/src/IdnaEncoder.php index a0df5004a..709bd82fe 100644 --- a/src/IdnaEncoder.php +++ b/src/IdnaEncoder.php @@ -174,32 +174,23 @@ protected static function utf8_to_codepoints($input) { for ($position = 0; $position < $strlen; $position++) { $value = ord($input[$position]); - // One byte sequence: - if ((~$value & 0x80) === 0x80) { + if ((~$value & 0x80) === 0x80) { // One byte sequence: $character = $value; $length = 1; $remaining = 0; - } - // Two byte sequence: - elseif (($value & 0xE0) === 0xC0) { + } elseif (($value & 0xE0) === 0xC0) { // Two byte sequence: $character = ($value & 0x1F) << 6; $length = 2; $remaining = 1; - } - // Three byte sequence: - elseif (($value & 0xF0) === 0xE0) { + } elseif (($value & 0xF0) === 0xE0) { // Three byte sequence: $character = ($value & 0x0F) << 12; $length = 3; $remaining = 2; - } - // Four byte sequence: - elseif (($value & 0xF8) === 0xF0) { + } elseif (($value & 0xF8) === 0xF0) { // Four byte sequence: $character = ($value & 0x07) << 18; $length = 4; $remaining = 3; - } - // Invalid byte: - else { + } else { // Invalid byte: throw new Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $value); } @@ -277,15 +268,14 @@ public static function punycode_encode($input) { // TODO: this should also check if it's valid for a URL $output .= chr($char); $h++; - } - // Check if the character is non-ASCII, but below initial n - // This never occurs for Punycode, so ignore in coverage - // @codeCoverageIgnoreStart - elseif ($char < $n) { + + // Check if the character is non-ASCII, but below initial n + // This never occurs for Punycode, so ignore in coverage + // @codeCoverageIgnoreStart + } elseif ($char < $n) { throw new Exception('Invalid character', 'idna.character_outside_domain', $char); - } - // @codeCoverageIgnoreEnd - else { + // @codeCoverageIgnoreEnd + } else { $extended[$char] = true; } } @@ -313,9 +303,7 @@ public static function punycode_encode($input) { // if c < n then increment delta, fail on overflow if ($c < $n) { $delta++; - } - // if c == n then begin - elseif ($c === $n) { + } elseif ($c === $n) { // if c == n then begin // let q = delta $q = $delta; // for k = base to infinity in steps of base do begin @@ -324,11 +312,9 @@ public static function punycode_encode($input) { // tmax if k >= bias + tmax, or k - bias otherwise if ($k <= ($bias + self::BOOTSTRAP_TMIN)) { $t = self::BOOTSTRAP_TMIN; - } - elseif ($k >= ($bias + self::BOOTSTRAP_TMAX)) { + } elseif ($k >= ($bias + self::BOOTSTRAP_TMAX)) { $t = self::BOOTSTRAP_TMAX; - } - else { + } else { $t = $k - $bias; } // if q < t then break @@ -395,9 +381,8 @@ protected static function adapt($delta, $numpoints, $firsttime) { // if firsttime then let delta = delta div damp if ($firsttime) { $delta = floor($delta / self::BOOTSTRAP_DAMP); - } - // else let delta = delta div 2 - else { + } else { + // else let delta = delta div 2 $delta = floor($delta / 2); } // let delta = delta + (delta div numpoints) diff --git a/src/Ipv6.php b/src/Ipv6.php index a01b640e6..5a3970b51 100644 --- a/src/Ipv6.php +++ b/src/Ipv6.php @@ -58,25 +58,24 @@ public static function uncompress($ip) { if (strpos($ip2, '.') !== false) { $c2++; } - // :: + if ($c1 === -1 && $c2 === -1) { + // :: $ip = '0:0:0:0:0:0:0:0'; - } - // ::xxx - elseif ($c1 === -1) { + } elseif ($c1 === -1) { + // ::xxx $fill = str_repeat('0:', 7 - $c2); $ip = str_replace('::', $fill, $ip); - } - // xxx:: - elseif ($c2 === -1) { + } elseif ($c2 === -1) { + // xxx:: $fill = str_repeat(':0', 7 - $c1); $ip = str_replace('::', $fill, $ip); - } - // xxx::xxx - else { + } else { + // xxx::xxx $fill = ':' . str_repeat('0:', 6 - $c2 - $c1); $ip = str_replace('::', $fill, $ip); } + return $ip; } @@ -120,8 +119,7 @@ public static function compress($ip) { if ($ip_parts[1] !== '') { return implode(':', $ip_parts); - } - else { + } else { return $ip_parts[0]; } } @@ -144,8 +142,7 @@ private static function split_v6_v4($ip) { $ipv6_part = substr($ip, 0, $pos); $ipv4_part = substr($ip, $pos + 1); return [$ipv6_part, $ipv4_part]; - } - else { + } else { return [$ip, '']; } } @@ -197,8 +194,7 @@ public static function check_ipv6($ip) { } } return true; - } - else { + } else { return false; } } diff --git a/src/Proxy/Http.php b/src/Proxy/Http.php index 77d7cb5da..e1e2520cc 100644 --- a/src/Proxy/Http.php +++ b/src/Proxy/Http.php @@ -67,16 +67,13 @@ final class Http implements Proxy { public function __construct($args = null) { if (is_string($args)) { $this->proxy = $args; - } - elseif (is_array($args)) { + } elseif (is_array($args)) { if (count($args) === 1) { list($this->proxy) = $args; - } - elseif (count($args) === 3) { + } elseif (count($args) === 3) { list($this->proxy, $this->user, $this->pass) = $args; $this->use_authentication = true; - } - else { + } else { throw ArgumentCount::create( 'an array with exactly one element or exactly three elements', count($args), diff --git a/src/Requests.php b/src/Requests.php index b61189217..a39f175a5 100644 --- a/src/Requests.php +++ b/src/Requests.php @@ -459,8 +459,7 @@ public static function request($url, $headers = [], $data = [], $type = self::GE if (is_string($options['transport'])) { $transport = new $transport(); } - } - else { + } else { $need_ssl = (stripos($url, 'https://') === 0); $capabilities = [Capability::SSL => $need_ssl]; $transport = self::get_transport($capabilities); @@ -547,8 +546,7 @@ public static function request_multiple($requests, $options = []) { if (!isset($request['options'])) { $request['options'] = $options; $request['options']['type'] = $request['type']; - } - else { + } else { if (empty($request['options']['type'])) { $request['options']['type'] = $request['type']; } @@ -573,8 +571,7 @@ public static function request_multiple($requests, $options = []) { if (is_string($options['transport'])) { $transport = new $transport(); } - } - else { + } else { $transport = self::get_transport(); } $responses = $transport->request_multiple($requests, $options); @@ -670,8 +667,7 @@ protected static function set_defaults(&$url, &$headers, &$data, &$type, &$optio if (is_array($options['cookies'])) { $options['cookies'] = new Jar($options['cookies']); - } - elseif (empty($options['cookies'])) { + } elseif (empty($options['cookies'])) { $options['cookies'] = new Jar(); } if ($options['cookies'] !== false) { @@ -690,8 +686,7 @@ protected static function set_defaults(&$url, &$headers, &$data, &$type, &$optio if (!isset($options['data_format'])) { if (in_array($type, [self::HEAD, self::GET, self::DELETE], true)) { $options['data_format'] = 'query'; - } - else { + } else { $options['data_format'] = 'body'; } } @@ -795,8 +790,7 @@ protected static function parse_response($headers, $url, $req_headers, $req_data $redirected = self::request($location, $req_headers, $req_data, $options['type'], $options); $redirected->history[] = $return; return $redirected; - } - elseif ($options['redirected'] >= $options['redirects']) { + } elseif ($options['redirected'] >= $options['redirects']) { throw new Exception('Too many redirects', 'toomanyredirects', $return); } } @@ -824,8 +818,7 @@ public static function parse_multiple(&$response, $request) { $data = $request['data']; $options = $request['options']; $response = self::parse_response($response, $url, $headers, $data, $options); - } - catch (Exception $e) { + } catch (Exception $e) { $response = $e; } } diff --git a/src/Response.php b/src/Response.php index 82ab481ba..8964521a8 100644 --- a/src/Response.php +++ b/src/Response.php @@ -124,8 +124,7 @@ public function throw_for_status($allow_redirects = true) { if ($allow_redirects !== true) { throw new Exception('Redirection not allowed', 'response.no_redirects', $this); } - } - elseif (!$this->success) { + } elseif (!$this->success) { $exception = Http::get_class($this->status_code); throw new $exception(null, $this); } diff --git a/src/Session.php b/src/Session.php index abecc05f7..18123a4e2 100644 --- a/src/Session.php +++ b/src/Session.php @@ -287,8 +287,7 @@ protected function merge_request($request, $merge_options = true) { if (is_array($this->data)) { $request['data'] = $this->data; } - } - elseif (is_array($request['data']) && is_array($this->data)) { + } elseif (is_array($request['data']) && is_array($this->data)) { $request['data'] = array_merge($this->data, $request['data']); } diff --git a/src/Transport/Curl.php b/src/Transport/Curl.php index 289d9b15b..de8110c58 100644 --- a/src/Transport/Curl.php +++ b/src/Transport/Curl.php @@ -190,8 +190,7 @@ public function request($url, $headers = [], $data = [], $options = []) { if ($options['verify'] === false) { curl_setopt($this->handle, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($this->handle, CURLOPT_SSL_VERIFYPEER, 0); - } - elseif (is_string($options['verify'])) { + } elseif (is_string($options['verify'])) { curl_setopt($this->handle, CURLOPT_CAINFO, $options['verify']); } } @@ -272,8 +271,7 @@ public function request_multiple($requests, $options) { do { $status = curl_multi_exec($multihandle, $active); - } - while ($status === CURLM_CALL_MULTI_PERFORM); + } while ($status === CURLM_CALL_MULTI_PERFORM); $to_process = []; @@ -299,8 +297,7 @@ public function request_multiple($requests, $options) { ); $responses[$key] = $exception; $options['hooks']->dispatch('transport.internal.parse_error', [&$responses[$key], $requests[$key]]); - } - else { + } else { $responses[$key] = $subrequests[$key]->process_response($subrequests[$key]->response_data, $options); $options['hooks']->dispatch('transport.internal.parse_response', [&$responses[$key], $requests[$key]]); @@ -314,8 +311,7 @@ public function request_multiple($requests, $options) { } $completed++; } - } - while ($active || $completed < $subrequestcount); + } while ($active || $completed < $subrequestcount); $request['options']['hooks']->dispatch('curl.after_multi_exec', [&$multihandle]); @@ -390,8 +386,7 @@ private function setup_handle($url, $headers, $data, $options) { if ($data_format === 'query') { $url = self::format_get($url, $data); $data = ''; - } - elseif (!is_string($data)) { + } elseif (!is_string($data)) { $data = http_build_query($data, '', '&'); } } @@ -429,16 +424,14 @@ private function setup_handle($url, $headers, $data, $options) { if (is_int($timeout) || $this->version < self::CURL_7_16_2) { curl_setopt($this->handle, CURLOPT_TIMEOUT, ceil($timeout)); - } - else { + } else { // phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_timeout_msFound curl_setopt($this->handle, CURLOPT_TIMEOUT_MS, round($timeout * 1000)); } if (is_int($options['connect_timeout']) || $this->version < self::CURL_7_16_2) { curl_setopt($this->handle, CURLOPT_CONNECTTIMEOUT, ceil($options['connect_timeout'])); - } - else { + } else { // phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_connecttimeout_msFound curl_setopt($this->handle, CURLOPT_CONNECTTIMEOUT_MS, round($options['connect_timeout'] * 1000)); } @@ -449,8 +442,7 @@ private function setup_handle($url, $headers, $data, $options) { } if ($options['protocol_version'] === 1.1) { curl_setopt($this->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - } - else { + } else { curl_setopt($this->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); } @@ -478,8 +470,7 @@ public function process_response($response, $options) { if ($options['filename'] !== false && $this->stream_handle) { fclose($this->stream_handle); $this->headers = trim($this->headers); - } - else { + } else { $this->headers .= $response; } @@ -549,8 +540,7 @@ public function stream_body($handle, $data) { if ($this->stream_handle) { fwrite($this->stream_handle, $data); - } - else { + } else { $this->response_data .= $data; } @@ -571,8 +561,7 @@ private static function format_get($url, $data) { $url_parts = parse_url($url); if (empty($url_parts['query'])) { $url_parts['query'] = ''; - } - else { + } else { $query = $url_parts['query']; } @@ -581,8 +570,7 @@ private static function format_get($url, $data) { if (empty($url_parts['query'])) { $url .= '?' . $query; - } - else { + } else { $url = str_replace($url_parts['query'], $query, $url); } } diff --git a/src/Transport/Fsockopen.php b/src/Transport/Fsockopen.php index 4779346ad..28181b339 100644 --- a/src/Transport/Fsockopen.php +++ b/src/Transport/Fsockopen.php @@ -128,8 +128,7 @@ public function request($url, $headers = [], $data = [], $options = []) { $context_options['verify_peer'] = false; $context_options['verify_peer_name'] = false; $verifyname = false; - } - elseif (is_string($options['verify'])) { + } elseif (is_string($options['verify'])) { $context_options['cafile'] = $options['verify']; } } @@ -140,8 +139,7 @@ public function request($url, $headers = [], $data = [], $options = []) { } stream_context_set_option($context, ['ssl' => $context_options]); - } - else { + } else { $remote_socket = 'tcp://' . $host; } @@ -179,8 +177,7 @@ public function request($url, $headers = [], $data = [], $options = []) { if ($data_format === 'query') { $path = self::format_get($url_parts, $data); $data = ''; - } - else { + } else { $path = self::format_get($url_parts, []); } @@ -192,8 +189,7 @@ public function request($url, $headers = [], $data = [], $options = []) { if ($options['type'] !== Requests::TRACE) { if (is_array($data)) { $request_body = http_build_query($data, '', '&'); - } - else { + } else { $request_body = $data; } @@ -262,8 +258,7 @@ public function request($url, $headers = [], $data = [], $options = []) { $timeout_sec = (int) floor($options['timeout']); if ($timeout_sec === $options['timeout']) { $timeout_msec = 0; - } - else { + } else { $timeout_msec = self::SECOND_IN_MICROSECONDS * $options['timeout'] % self::SECOND_IN_MICROSECONDS; } stream_set_timeout($socket, $timeout_sec, $timeout_msec); @@ -318,8 +313,7 @@ public function request($url, $headers = [], $data = [], $options = []) { $size += strlen($block); if ($download) { fwrite($download, $block); - } - else { + } else { $body .= $block; } } @@ -328,8 +322,7 @@ public function request($url, $headers = [], $data = [], $options = []) { if ($download) { fclose($download); - } - else { + } else { $this->headers .= "\r\n\r\n" . $body; } fclose($socket); @@ -370,8 +363,7 @@ public function request_multiple($requests, $options) { $responses[$id] = $handler->request($request['url'], $request['headers'], $request['data'], $request['options']); $request['options']['hooks']->dispatch('transport.internal.parse_response', [&$responses[$id], $request]); - } - catch (Exception $e) { + } catch (Exception $e) { $responses[$id] = $e; } @@ -422,12 +414,10 @@ private static function format_get($url_parts, $data) { if (isset($url_parts['path'])) { if (isset($url_parts['query'])) { $get = $url_parts['path'] . '?' . $url_parts['query']; - } - else { + } else { $get = $url_parts['path']; } - } - else { + } else { $get = '/'; } return $get; diff --git a/tests/Transport/BaseTestCase.php b/tests/Transport/BaseTestCase.php index 2ae1c55cd..d95852ca4 100644 --- a/tests/Transport/BaseTestCase.php +++ b/tests/Transport/BaseTestCase.php @@ -676,8 +676,7 @@ public function testStatusCodeThrow($code, $success) { if ($code >= 400) { $this->expectException('\WpOrg\Requests\Exception\Http\Status' . $code); $this->expectExceptionCode($code); - } - elseif ($code >= 300 && $code < 400) { + } elseif ($code >= 300 && $code < 400) { $this->expectException(Exception::class); } } @@ -1081,13 +1080,11 @@ public function testAlternatePort() { try { $request = Requests::get('http://portquiz.net:8080/', [], $this->getOptions()); - } - catch (Exception $e) { + } catch (Exception $e) { // Retry the request as it often times-out. try { $request = Requests::get('http://portquiz.net:8080/', [], $this->getOptions()); - } - catch (Exception $e) { + } catch (Exception $e) { // If it still times out, mark the test as skipped. $this->markTestSkipped( $e->getMessage() diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 4e77810fc..371a61aa4 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -6,8 +6,7 @@ function define_from_env($name, $fallback = false) { $env = getenv($name); if ($env) { define($name, $env); - } - else { + } else { define($name, $fallback); } } From b496a5b252fed7e9ff8630024f8043a9c3b093be Mon Sep 17 00:00:00 2001 From: jrfnl Date: Sat, 27 Nov 2021 00:58:51 +0100 Subject: [PATCH 2/2] CS: improve code readability ... by enforcing a blank line after control structures. --- .phpcs.xml.dist | 2 -- build/ghpages/UpdateMarkdown.php | 3 +-- src/Cookie.php | 3 +++ src/IdnaEncoder.php | 9 +++++++++ src/Ipv6.php | 2 ++ src/Requests.php | 23 +++++++++++++++++++++++ src/Session.php | 1 + src/Transport/Curl.php | 11 +++++++++++ src/Transport/Fsockopen.php | 9 +++++++++ tests/CookiesTest.php | 5 +++++ tests/Fixtures/TestTransportMock.php | 1 + tests/Fixtures/TransportMock.php | 1 + tests/RequestsTest.php | 1 + tests/SslTest.php | 1 + 14 files changed, 68 insertions(+), 4 deletions(-) diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist index b345144aa..e2792cf36 100644 --- a/.phpcs.xml.dist +++ b/.phpcs.xml.dist @@ -135,8 +135,6 @@ - - diff --git a/build/ghpages/UpdateMarkdown.php b/build/ghpages/UpdateMarkdown.php index 4065d6345..5b91ef5c8 100644 --- a/build/ghpages/UpdateMarkdown.php +++ b/build/ghpages/UpdateMarkdown.php @@ -339,8 +339,7 @@ private function put_contents(string $target, string $contents, string $type = ' if (@mkdir($target_dir, 0777, true) === false) { throw new RuntimeException(sprintf('Failed to create the %s directory.', $target_dir)); } - } - // phpcs:enable WordPress + } // phpcs:enable WordPress // Make sure the file always ends on a new line. $contents = rtrim($contents) . "\n"; diff --git a/src/Cookie.php b/src/Cookie.php index b9347bd13..ccbbc73db 100644 --- a/src/Cookie.php +++ b/src/Cookie.php @@ -389,6 +389,7 @@ public function format_for_set_cookie() { $header_value .= '; ' . implode('; ', $parts); } + return $header_value; } @@ -432,6 +433,7 @@ public static function parse($cookie_header, $name = '', $reference_time = null) } else { list($name, $value) = explode('=', $kvparts, 2); } + $name = trim($name); $value = trim($value); @@ -503,6 +505,7 @@ public static function parse_from_headers(Headers $headers, Iri $origin = null, // %x2F ("/"). $path = substr($path, 0, strrpos($path, '/')); } + $parsed->attributes['path'] = $path; } diff --git a/src/IdnaEncoder.php b/src/IdnaEncoder.php index 709bd82fe..094fff3d5 100644 --- a/src/IdnaEncoder.php +++ b/src/IdnaEncoder.php @@ -67,6 +67,7 @@ public static function encode($hostname) { foreach ($parts as &$part) { $part = self::to_ascii($part); } + return implode('.', $parts); } @@ -198,6 +199,7 @@ protected static function utf8_to_codepoints($input) { if ($position + $length > $strlen) { throw new Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character); } + for ($position++; $remaining > 0; $position++) { $value = ord($input[$position]); @@ -209,6 +211,7 @@ protected static function utf8_to_codepoints($input) { --$remaining; $character |= ($value & 0x3F) << ($remaining * 6); } + $position--; } @@ -279,6 +282,7 @@ public static function punycode_encode($input) { $extended[$char] = true; } } + $extended = array_keys($extended); sort($extended); $b = $h; @@ -286,6 +290,7 @@ public static function punycode_encode($input) { if (strlen($output) > 0) { $output .= '-'; } + // {if the input contains a non-basic code point < n then fail} // while h < length(input) do begin $codepointcount = count($codepoints); @@ -317,10 +322,12 @@ public static function punycode_encode($input) { } else { $t = $k - $bias; } + // if q < t then break if ($q < $t) { break; } + // output the code point for digit t + ((q - t) mod (base - t)) $digit = $t + (($q - $t) % (self::BOOTSTRAP_BASE - $t)); $output .= self::digit_to_char($digit); @@ -361,6 +368,7 @@ protected static function digit_to_char($digit) { if ($digit < 0 || $digit > 35) { throw new Exception(sprintf('Invalid digit %d', $digit), 'idna.invalid_digit', $digit); } + // @codeCoverageIgnoreEnd $digits = 'abcdefghijklmnopqrstuvwxyz0123456789'; return substr($digits, $digit, 1); @@ -385,6 +393,7 @@ protected static function adapt($delta, $numpoints, $firsttime) { // else let delta = delta div 2 $delta = floor($delta / 2); } + // let delta = delta + (delta div numpoints) $delta += floor($delta / $numpoints); // let k = 0 diff --git a/src/Ipv6.php b/src/Ipv6.php index 5a3970b51..137bcafa0 100644 --- a/src/Ipv6.php +++ b/src/Ipv6.php @@ -185,6 +185,7 @@ public static function check_ipv6($ip) { return false; } } + if (count($ipv4) === 4) { foreach ($ipv4 as $ipv4_part) { $value = (int) $ipv4_part; @@ -193,6 +194,7 @@ public static function check_ipv6($ip) { } } } + return true; } else { return false; diff --git a/src/Requests.php b/src/Requests.php index a39f175a5..cf4d972f5 100644 --- a/src/Requests.php +++ b/src/Requests.php @@ -447,6 +447,7 @@ public static function request($url, $headers = [], $data = [], $type = self::GE if (empty($options['type'])) { $options['type'] = $type; } + $options = array_merge(self::get_default_options(), $options); self::set_defaults($url, $headers, $data, $type, $options); @@ -464,6 +465,7 @@ public static function request($url, $headers = [], $data = [], $type = self::GE $capabilities = [Capability::SSL => $need_ssl]; $transport = self::get_transport($capabilities); } + $response = $transport->request($url, $headers, $data, $options); $options['hooks']->dispatch('requests.before_parse', [&$response, $url, $headers, $data, $type, $options]); @@ -537,12 +539,15 @@ public static function request_multiple($requests, $options = []) { if (!isset($request['headers'])) { $request['headers'] = []; } + if (!isset($request['data'])) { $request['data'] = []; } + if (!isset($request['type'])) { $request['type'] = self::GET; } + if (!isset($request['options'])) { $request['options'] = $options; $request['options']['type'] = $request['type']; @@ -550,6 +555,7 @@ public static function request_multiple($requests, $options = []) { if (empty($request['options']['type'])) { $request['options']['type'] = $request['type']; } + $request['options'] = array_merge($options, $request['options']); } @@ -563,6 +569,7 @@ public static function request_multiple($requests, $options = []) { } } } + unset($request); if (!empty($options['transport'])) { @@ -574,6 +581,7 @@ public static function request_multiple($requests, $options = []) { } else { $transport = self::get_transport(); } + $responses = $transport->request_multiple($requests, $options); foreach ($responses as $id => &$response) { @@ -603,6 +611,7 @@ protected static function get_default_options($multirequest = false) { if ($multirequest !== false) { $defaults['complete'] = null; } + return $defaults; } @@ -654,6 +663,7 @@ protected static function set_defaults(&$url, &$headers, &$data, &$type, &$optio if (is_array($options['auth'])) { $options['auth'] = new Basic($options['auth']); } + if ($options['auth'] !== false) { $options['auth']->register($options['hooks']); } @@ -661,6 +671,7 @@ protected static function set_defaults(&$url, &$headers, &$data, &$type, &$optio if (is_string($options['proxy']) || is_array($options['proxy'])) { $options['proxy'] = new Http($options['proxy']); } + if ($options['proxy'] !== false) { $options['proxy']->register($options['hooks']); } @@ -670,6 +681,7 @@ protected static function set_defaults(&$url, &$headers, &$data, &$type, &$optio } elseif (empty($options['cookies'])) { $options['cookies'] = new Jar(); } + if ($options['cookies'] !== false) { $options['cookies']->register($options['hooks']); } @@ -730,6 +742,7 @@ protected static function parse_response($headers, $url, $req_headers, $req_data $return->body = $body; } } + // Pretend CRLF = LF for compatibility (RFC 2616, section 19.3) $headers = str_replace("\r\n", "\n", $headers); // Unfold headers (replace [CRLF] 1*( SP | HT ) with SP) as per RFC 2616 (section 2.2) @@ -739,6 +752,7 @@ protected static function parse_response($headers, $url, $req_headers, $req_data if (empty($matches)) { throw new Exception('Response could not be parsed', 'noversion', $headers); } + $return->protocol_version = (float) $matches[1]; $return->status_code = (int) $matches[2]; if ($return->status_code >= 200 && $return->status_code < 300) { @@ -751,10 +765,12 @@ protected static function parse_response($headers, $url, $req_headers, $req_data preg_replace('#(\s+)#i', ' ', $value); $return->headers[$key] = $value; } + if (isset($return->headers['transfer-encoding'])) { $return->body = self::decode_chunked($return->body); unset($return->headers['transfer-encoding']); } + if (isset($return->headers['content-encoding'])) { $return->body = self::decompress($return->body); } @@ -771,6 +787,7 @@ protected static function parse_response($headers, $url, $req_headers, $req_data if ($return->status_code === 303) { $options['type'] = self::GET; } + $options['redirected']++; $location = $return->headers['location']; if (strpos($location, 'http://') !== 0 && strpos($location, 'https://') !== 0) { @@ -882,6 +899,7 @@ public static function flatten($dictionary) { foreach ($dictionary as $key => $value) { $return[] = sprintf('%s: %s', $key, $value); } + return $return; } @@ -982,16 +1000,20 @@ public static function compatible_gzinflate($gz_data) { list($xlen) = unpack('v', substr($gz_data, $i, 2)); $i += 2 + $xlen; } + if ($flg & 8) { $i = strpos($gz_data, "\0", $i) + 1; } + if ($flg & 16) { $i = strpos($gz_data, "\0", $i) + 1; } + if ($flg & 2) { $i += 2; } } + $decompressed = self::compatible_gzinflate(substr($gz_data, $i)); if ($decompressed !== false) { return $decompressed; @@ -1051,6 +1073,7 @@ public static function compatible_gzinflate($gz_data) { if ($decompressed !== false) { return $decompressed; } + return false; } diff --git a/src/Session.php b/src/Session.php index 18123a4e2..000d2526d 100644 --- a/src/Session.php +++ b/src/Session.php @@ -281,6 +281,7 @@ protected function merge_request($request, $merge_options = true) { if (empty($request['headers'])) { $request['headers'] = []; } + $request['headers'] = array_merge($this->headers, $request['headers']); if (empty($request['data'])) { diff --git a/src/Transport/Curl.php b/src/Transport/Curl.php index de8110c58..8b0a13080 100644 --- a/src/Transport/Curl.php +++ b/src/Transport/Curl.php @@ -109,10 +109,12 @@ public function __construct() { if ($this->version >= self::CURL_7_10_5) { curl_setopt($this->handle, CURLOPT_ENCODING, ''); } + if (defined('CURLOPT_PROTOCOLS')) { // phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_protocolsFound curl_setopt($this->handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); } + if (defined('CURLOPT_REDIR_PROTOCOLS')) { // phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_redir_protocolsFound curl_setopt($this->handle, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); @@ -309,6 +311,7 @@ public function request_multiple($requests, $options) { if (!is_string($responses[$key])) { $options['hooks']->dispatch('multiple.request.complete', [&$responses[$key], $key]); } + $completed++; } } while ($active || $completed < $subrequestcount); @@ -342,6 +345,7 @@ public function &get_subrequest_handle($url, $headers, $data, $options) { if ($options['max_bytes'] !== false) { $this->response_byte_limit = $options['max_bytes']; } + $this->hooks = $options['hooks']; return $this->handle; @@ -435,11 +439,13 @@ private function setup_handle($url, $headers, $data, $options) { // phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_connecttimeout_msFound curl_setopt($this->handle, CURLOPT_CONNECTTIMEOUT_MS, round($options['connect_timeout'] * 1000)); } + curl_setopt($this->handle, CURLOPT_URL, $url); curl_setopt($this->handle, CURLOPT_USERAGENT, $options['useragent']); if (!empty($headers)) { curl_setopt($this->handle, CURLOPT_HTTPHEADER, $headers); } + if ($options['protocol_version'] === 1.1) { curl_setopt($this->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); } else { @@ -467,6 +473,7 @@ public function process_response($response, $options) { $options['hooks']->dispatch('curl.after_request', [&$fake_headers]); return false; } + if ($options['filename'] !== false && $this->stream_handle) { fclose($this->stream_handle); $this->headers = trim($this->headers); @@ -482,6 +489,7 @@ public function process_response($response, $options) { ); throw new Exception($error, 'curlerror', $this->handle); } + $this->info = curl_getinfo($this->handle); $options['hooks']->dispatch('curl.after_request', [&$this->headers, &$this->info]); @@ -503,11 +511,13 @@ public function stream_headers($handle, $headers) { $this->headers = ''; $this->done_headers = false; } + $this->headers .= $headers; if ($headers === "\r\n") { $this->done_headers = true; } + return strlen($headers); } @@ -574,6 +584,7 @@ private static function format_get($url, $data) { $url = str_replace($url_parts['query'], $query, $url); } } + return $url; } diff --git a/src/Transport/Fsockopen.php b/src/Transport/Fsockopen.php index 28181b339..c3bd4a63d 100644 --- a/src/Transport/Fsockopen.php +++ b/src/Transport/Fsockopen.php @@ -96,6 +96,7 @@ public function request($url, $headers = [], $data = [], $options = []) { if (empty($url_parts)) { throw new Exception('Invalid URL.', 'invalidurl', $url); } + $host = $url_parts['host']; $context = stream_context_create(); $verifyname = false; @@ -148,6 +149,7 @@ public function request($url, $headers = [], $data = [], $options = []) { if (!isset($url_parts['port'])) { $url_parts['port'] = Port::HTTP; } + $remote_socket .= ':' . $url_parts['port']; // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler @@ -213,6 +215,7 @@ public function request($url, $headers = [], $data = [], $options = []) { if (($scheme_lower === 'http' && $url_parts['port'] !== Port::HTTP) || ($scheme_lower === 'https' && $url_parts['port'] !== Port::HTTPS)) { $out .= ':' . $url_parts['port']; } + $out .= "\r\n"; } @@ -261,6 +264,7 @@ public function request($url, $headers = [], $data = [], $options = []) { } else { $timeout_msec = self::SECOND_IN_MICROSECONDS * $options['timeout'] % self::SECOND_IN_MICROSECONDS; } + stream_set_timeout($socket, $timeout_sec, $timeout_msec); $response = ''; @@ -303,6 +307,7 @@ public function request($url, $headers = [], $data = [], $options = []) { if ($size === $this->max_bytes) { continue; } + if (($size + $data_length) > $this->max_bytes) { // Limit the length $limited_length = ($this->max_bytes - $size); @@ -318,6 +323,7 @@ public function request($url, $headers = [], $data = [], $options = []) { } } } + $this->headers = $headers; if ($download) { @@ -325,6 +331,7 @@ public function request($url, $headers = [], $data = [], $options = []) { } else { $this->headers .= "\r\n\r\n" . $body; } + fclose($socket); $options['hooks']->dispatch('fsockopen.after_request', [&$this->headers, &$this->info]); @@ -411,6 +418,7 @@ private static function format_get($url_parts, $data) { $url_parts['query'] .= '&' . http_build_query($data, '', '&'); $url_parts['query'] = trim($url_parts['query'], '&'); } + if (isset($url_parts['path'])) { if (isset($url_parts['query'])) { $get = $url_parts['path'] . '?' . $url_parts['query']; @@ -420,6 +428,7 @@ private static function format_get($url_parts, $data) { } else { $get = '/'; } + return $get; } diff --git a/tests/CookiesTest.php b/tests/CookiesTest.php index 3a789a331..6c147815e 100644 --- a/tests/CookiesTest.php +++ b/tests/CookiesTest.php @@ -397,17 +397,21 @@ private function check_parsed_cookie($cookie, $expected, $expected_attributes, $ if (isset($expected['name'])) { $this->assertSame($expected['name'], $cookie->name); } + if (isset($expected['value'])) { $this->assertSame($expected['value'], $cookie->value); } + if (isset($expected['expired'])) { $this->assertSame($expected['expired'], $cookie->is_expired()); } + if (isset($expected_attributes)) { foreach ($expected_attributes as $attr_key => $attr_val) { $this->assertSame($attr_val, $cookie->attributes[$attr_key], "$attr_key should match supplied"); } } + if (isset($expected_flags)) { foreach ($expected_flags as $flag_key => $flag_val) { $this->assertSame($flag_val, $cookie->flags[$flag_key], "$flag_key should match supplied"); @@ -586,6 +590,7 @@ public function testParsingHeaderWithOrigin($header, $origin, $expected, $expect $this->assertCount(0, $parsed); return; } + $this->assertCount(1, $parsed); $cookie = reset($parsed); diff --git a/tests/Fixtures/TestTransportMock.php b/tests/Fixtures/TestTransportMock.php index e62f8ced8..b910c03e8 100644 --- a/tests/Fixtures/TestTransportMock.php +++ b/tests/Fixtures/TestTransportMock.php @@ -16,6 +16,7 @@ public static function test($capabilities = []) { if (isset($capabilities['time-travel']) && $capabilities['time-travel']) { return false; } + return true; } } diff --git a/tests/Fixtures/TransportMock.php b/tests/Fixtures/TransportMock.php index 51655247c..8bcd38fbb 100644 --- a/tests/Fixtures/TransportMock.php +++ b/tests/Fixtures/TransportMock.php @@ -66,6 +66,7 @@ public function request($url, $headers = [], $data = [], $options = []) { if ($this->chunked) { $response .= "Transfer-Encoding: chunked\r\n"; } + $response .= $this->raw_headers; $response .= "Connection: close\r\n\r\n"; $response .= $this->body; diff --git a/tests/RequestsTest.php b/tests/RequestsTest.php index d8a374998..469a9b5b0 100644 --- a/tests/RequestsTest.php +++ b/tests/RequestsTest.php @@ -327,6 +327,7 @@ public function testHasCapabilitiesSucceedsForDetectingSsl() { if (!extension_loaded('curl') && !extension_loaded('openssl')) { $this->markTestSkipped('Testing for SSL requires either the curl or the openssl extension'); } + $this->assertTrue(Requests::has_capabilities([Capability::SSL => true])); } diff --git a/tests/SslTest.php b/tests/SslTest.php index 4e5423371..a5b459c3b 100644 --- a/tests/SslTest.php +++ b/tests/SslTest.php @@ -259,6 +259,7 @@ private function fakeCertificate($dnsname, $with_san = true) { if ($with_san === true) { $with_san = 'DNS: ' . $dnsname; } + $certificate['extensions'] = [ 'subjectAltName' => $with_san, ];