Browse Source

Mark the transport parameter explicitly nullable

PHP 8.4 deprecates implicitly nullable parameters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main
Aaron Parecki 2 days ago
parent
commit
fc5ebaa10c
1 changed files with 98 additions and 29 deletions
  1. +98
    -29
      src/p3k/HTTP.php

+ 98
- 29
src/p3k/HTTP.php View File

@ -8,8 +8,9 @@ class HTTP {
private $_transport;
private $_user_agent;
private $_guard = null;
public function __construct($user_agent=null, HTTP\Transport $transport=null) {
public function __construct($user_agent=null, ?HTTP\Transport $transport=null) {
if($user_agent) {
$this->_user_agent = $user_agent;
}
@ -36,48 +37,116 @@ class HTTP {
$this->_transport = $transport;
}
/**
* Safe mode: only fetch http and https URLs whose host resolves to public
* addresses, follow redirects one at a time so every hop is checked, and
* drop credentials when a redirect leaves the original origin.
*
* @param bool $enabled
* @param array $allow Hostnames, IP addresses or CIDR ranges that
* may be reached although they are private,
* e.g. a development server on the LAN.
* @param callable|null $resolver fn(string $host): string[]; the system
* resolver when null. For tests.
*/
public function set_safe_mode($enabled, array $allow=[], $resolver=null) {
$this->_guard = $enabled ? new HTTP\Guard($allow, $resolver) : null;
}
public function safe_mode() {
return $this->_guard !== null;
}
public function get($url, $headers=[]) {
$this->_transport->set_timeout($this->_timeout);
$this->_transport->set_max_redirects($this->_max_redirects);
if($this->_user_agent) {
$headers[] = 'User-Agent: ' . $this->_user_agent;
}
$response = $this->_transport->get($url, $headers);
$response = $this->_build_response($response);
return $response;
return $this->_request('get', $url, false, $headers);
}
public function post($url, $body, $headers=[]) {
$this->_transport->set_timeout($this->_timeout);
$this->_transport->set_max_redirects($this->_max_redirects);
if($this->_user_agent) {
$headers[] = 'User-Agent: ' . $this->_user_agent;
}
$response = $this->_transport->post($url, $body, $headers);
$response = $this->_build_response($response);
return $response;
return $this->_request('post', $url, $body, $headers);
}
public function head($url, $headers=[]) {
$this->_transport->set_timeout($this->_timeout);
$this->_transport->set_max_redirects($this->_max_redirects);
if($this->_user_agent) {
$headers[] = 'User-Agent: ' . $this->_user_agent;
}
$response = $this->_transport->head($url, $headers);
$response = $this->_build_response($response);
return $response;
return $this->_request('head', $url, false, $headers);
}
public function put($url, $body, $headers=[]) {
return $this->_request('put', $url, $body, $headers);
}
private function _request($method, $url, $body, $headers) {
$this->_transport->set_timeout($this->_timeout);
$this->_transport->set_max_redirects($this->_max_redirects);
if($this->_user_agent) {
$headers[] = 'User-Agent: ' . $this->_user_agent;
}
$response = $this->_transport->put($url, $body, $headers);
$response = $this->_build_response($response);
return $response;
if($this->_guard === null) {
$this->_transport->set_max_redirects($this->_max_redirects);
return $this->_build_response($this->_send($method, $url, $body, $headers));
}
$this->_transport->set_max_redirects(0);
$redirects = $this->_max_redirects;
while(true) {
$check = $this->_guard->check($url);
if(isset($check['error'])) {
return $this->_build_response([
'code' => 0,
'header' => '',
'body' => false,
'error' => $check['error'],
'error_description' => $check['error_description'],
'url' => $url,
'debug' => '',
]);
}
$pinnable = $this->_transport instanceof HTTP\Pinnable;
if($pinnable) {
$this->_transport->pin_addresses(array_map(function($address) use($check) {
return $check['host'] . ':' . $check['port'] . ':' . (strpos($address, ':') !== false ? '[' . $address . ']' : $address);
}, $check['addresses']));
}
try {
$response = $this->_build_response($this->_send($method, $url, $body, $headers));
} finally {
if($pinnable)
$this->_transport->pin_addresses(null);
}
$location = in_array((int)$response['code'], [301, 302, 303, 307, 308], true) ? ($response['headers']['Location'] ?? null) : null;
if(is_array($location))
$location = end($location);
if(!is_string($location) || $location === '')
return $response;
if($redirects-- <= 0) {
$response['error'] = 'too_many_redirects';
$response['error_description'] = 'Stopped after ' . $this->_max_redirects . ' redirects';
return $response;
}
$next = \Mf2\resolveUrl($url, $location);
if(self::_origin($next) !== self::_origin($url)) {
$headers = array_values(array_filter($headers, function($header) {
return !preg_match('/^\s*(authorization|proxy-authorization|cookie)\s*:/i', $header);
}));
}
$url = $next;
}
}
private function _send($method, $url, $body, $headers) {
if($method === 'post' || $method === 'put')
return $this->_transport->{$method}($url, $body, $headers);
return $this->_transport->{$method}($url, $headers);
}
private static function _origin($url) {
$parts = parse_url($url);
$scheme = strtolower($parts['scheme'] ?? '');
$port = $parts['port'] ?? ($scheme === 'https' ? 443 : 80);
return $scheme . '://' . strtolower($parts['host'] ?? '') . ':' . $port;
}
private function _build_response($response) {

Loading…
Cancel
Save