Cleanup
This commit is contained in:
parent
cb491341df
commit
9947a5f033
391 changed files with 0 additions and 15712 deletions
|
@ -1,668 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace PicoFeed\Client;
|
||||
|
||||
use LogicException;
|
||||
use PicoFeed\Logging\Logger;
|
||||
use PicoFeed\Config\Config;
|
||||
|
||||
/**
|
||||
* Client class.
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
abstract class Client
|
||||
{
|
||||
/**
|
||||
* Flag that say if the resource have been modified.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $is_modified = true;
|
||||
|
||||
/**
|
||||
* HTTP Content-Type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $content_type = '';
|
||||
|
||||
/**
|
||||
* HTTP encoding.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $encoding = '';
|
||||
|
||||
/**
|
||||
* HTTP request headers.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $request_headers = array();
|
||||
|
||||
/**
|
||||
* HTTP Etag header.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $etag = '';
|
||||
|
||||
/**
|
||||
* HTTP Last-Modified header.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $last_modified = '';
|
||||
|
||||
/**
|
||||
* Proxy hostname.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $proxy_hostname = '';
|
||||
|
||||
/**
|
||||
* Proxy port.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $proxy_port = 3128;
|
||||
|
||||
/**
|
||||
* Proxy username.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $proxy_username = '';
|
||||
|
||||
/**
|
||||
* Proxy password.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $proxy_password = '';
|
||||
|
||||
/**
|
||||
* Basic auth username.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $username = '';
|
||||
|
||||
/**
|
||||
* Basic auth password.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $password = '';
|
||||
|
||||
/**
|
||||
* Client connection timeout.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $timeout = 10;
|
||||
|
||||
/**
|
||||
* User-agent.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $user_agent = 'PicoFeed (https://github.com/fguillot/picoFeed)';
|
||||
|
||||
/**
|
||||
* Real URL used (can be changed after a HTTP redirect).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $url = '';
|
||||
|
||||
/**
|
||||
* Page/Feed content.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $content = '';
|
||||
|
||||
/**
|
||||
* Number maximum of HTTP redirections to avoid infinite loops.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $max_redirects = 5;
|
||||
|
||||
/**
|
||||
* Maximum size of the HTTP body response.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $max_body_size = 2097152; // 2MB
|
||||
|
||||
/**
|
||||
* HTTP response status code.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $status_code = 0;
|
||||
|
||||
/**
|
||||
* Enables direct passthrough to requesting client.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $passthrough = false;
|
||||
|
||||
/**
|
||||
* Do the HTTP request.
|
||||
*
|
||||
* @abstract
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public function doRequest();
|
||||
|
||||
/**
|
||||
* Get client instance: curl or stream driver.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (function_exists('curl_init')) {
|
||||
return new Curl();
|
||||
} elseif (ini_get('allow_url_fopen')) {
|
||||
return new Stream();
|
||||
}
|
||||
|
||||
throw new LogicException('You must have "allow_url_fopen=1" or curl extension installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add HTTP Header to the request.
|
||||
*
|
||||
* @param array $headers
|
||||
*/
|
||||
public function setHeaders($headers)
|
||||
{
|
||||
$this->request_headers = $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the HTTP request.
|
||||
*
|
||||
* @param string $url URL
|
||||
*
|
||||
* @return Client
|
||||
*/
|
||||
public function execute($url = '')
|
||||
{
|
||||
if ($url !== '') {
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
Logger::setMessage(get_called_class().' Fetch URL: '.$this->url);
|
||||
Logger::setMessage(get_called_class().' Etag provided: '.$this->etag);
|
||||
Logger::setMessage(get_called_class().' Last-Modified provided: '.$this->last_modified);
|
||||
|
||||
$response = $this->doRequest();
|
||||
|
||||
$this->status_code = $response['status'];
|
||||
$this->handleNotModifiedResponse($response);
|
||||
$this->handleNotFoundResponse($response);
|
||||
$this->handleNormalResponse($response);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle not modified response.
|
||||
*
|
||||
* @param array $response Client response
|
||||
*/
|
||||
public function handleNotModifiedResponse(array $response)
|
||||
{
|
||||
if ($response['status'] == 304) {
|
||||
$this->is_modified = false;
|
||||
} elseif ($response['status'] == 200) {
|
||||
$this->is_modified = $this->hasBeenModified($response, $this->etag, $this->last_modified);
|
||||
$this->etag = $this->getHeader($response, 'ETag');
|
||||
$this->last_modified = $this->getHeader($response, 'Last-Modified');
|
||||
}
|
||||
|
||||
if ($this->is_modified === false) {
|
||||
Logger::setMessage(get_called_class().' Resource not modified');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle not found response.
|
||||
*
|
||||
* @param array $response Client response
|
||||
*/
|
||||
public function handleNotFoundResponse(array $response)
|
||||
{
|
||||
if ($response['status'] == 404) {
|
||||
throw new InvalidUrlException('Resource not found');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle normal response.
|
||||
*
|
||||
* @param array $response Client response
|
||||
*/
|
||||
public function handleNormalResponse(array $response)
|
||||
{
|
||||
if ($response['status'] == 200) {
|
||||
$this->content = $response['body'];
|
||||
$this->content_type = $this->findContentType($response);
|
||||
$this->encoding = $this->findCharset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a request has been modified according to the parameters.
|
||||
*
|
||||
* @param array $response
|
||||
* @param string $etag
|
||||
* @param string $lastModified
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function hasBeenModified($response, $etag, $lastModified)
|
||||
{
|
||||
$headers = array(
|
||||
'Etag' => $etag,
|
||||
'Last-Modified' => $lastModified,
|
||||
);
|
||||
|
||||
// Compare the values for each header that is present
|
||||
$presentCacheHeaderCount = 0;
|
||||
foreach ($headers as $key => $value) {
|
||||
if (isset($response['headers'][$key])) {
|
||||
if ($response['headers'][$key] !== $value) {
|
||||
return true;
|
||||
}
|
||||
++$presentCacheHeaderCount;
|
||||
}
|
||||
}
|
||||
|
||||
// If at least one header is present and the values match, the response
|
||||
// was not modified
|
||||
if ($presentCacheHeaderCount > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find content type from response headers.
|
||||
*
|
||||
* @param array $response Client response
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function findContentType(array $response)
|
||||
{
|
||||
return strtolower($this->getHeader($response, 'Content-Type'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find charset from response headers.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function findCharset()
|
||||
{
|
||||
$result = explode('charset=', $this->content_type);
|
||||
|
||||
return isset($result[1]) ? $result[1] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get header value from a client response.
|
||||
*
|
||||
* @param array $response Client response
|
||||
* @param string $header Header name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHeader(array $response, $header)
|
||||
{
|
||||
return isset($response['headers'][$header]) ? $response['headers'][$header] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Last-Modified HTTP header.
|
||||
*
|
||||
* @param string $last_modified Header value
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setLastModified($last_modified)
|
||||
{
|
||||
$this->last_modified = $last_modified;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the Last-Modified HTTP header.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLastModified()
|
||||
{
|
||||
return $this->last_modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of the Etag HTTP header.
|
||||
*
|
||||
* @param string $etag Etag HTTP header value
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setEtag($etag)
|
||||
{
|
||||
$this->etag = $etag;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Etag HTTP header value.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEtag()
|
||||
{
|
||||
return $this->etag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the final url value.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUrl()
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the url.
|
||||
*
|
||||
* @return string
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setUrl($url)
|
||||
{
|
||||
$this->url = $url;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTTP response status code.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getStatusCode()
|
||||
{
|
||||
return $this->status_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the body of the HTTP response.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content type value from HTTP headers.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContentType()
|
||||
{
|
||||
return $this->content_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the encoding value from HTTP headers.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEncoding()
|
||||
{
|
||||
return $this->encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the remote resource has changed.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isModified()
|
||||
{
|
||||
return $this->is_modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* return true if passthrough mode is enabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isPassthroughEnabled()
|
||||
{
|
||||
return $this->passthrough;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set connection timeout.
|
||||
*
|
||||
* @param int $timeout Connection timeout
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setTimeout($timeout)
|
||||
{
|
||||
$this->timeout = $timeout ?: $this->timeout;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom user agent.
|
||||
*
|
||||
* @param string $user_agent User Agent
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setUserAgent($user_agent)
|
||||
{
|
||||
$this->user_agent = $user_agent ?: $this->user_agent;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum number of HTTP redirections.
|
||||
*
|
||||
* @param int $max Maximum
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setMaxRedirections($max)
|
||||
{
|
||||
$this->max_redirects = $max ?: $this->max_redirects;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum size of the HTTP body.
|
||||
*
|
||||
* @param int $max Maximum
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setMaxBodySize($max)
|
||||
{
|
||||
$this->max_body_size = $max ?: $this->max_body_size;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the proxy hostname.
|
||||
*
|
||||
* @param string $hostname Proxy hostname
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setProxyHostname($hostname)
|
||||
{
|
||||
$this->proxy_hostname = $hostname ?: $this->proxy_hostname;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the proxy port.
|
||||
*
|
||||
* @param int $port Proxy port
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setProxyPort($port)
|
||||
{
|
||||
$this->proxy_port = $port ?: $this->proxy_port;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the proxy username.
|
||||
*
|
||||
* @param string $username Proxy username
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setProxyUsername($username)
|
||||
{
|
||||
$this->proxy_username = $username ?: $this->proxy_username;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the proxy password.
|
||||
*
|
||||
* @param string $password Password
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setProxyPassword($password)
|
||||
{
|
||||
$this->proxy_password = $password ?: $this->proxy_password;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the username.
|
||||
*
|
||||
* @param string $username Basic Auth username
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setUsername($username)
|
||||
{
|
||||
$this->username = $username ?: $this->username;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the password.
|
||||
*
|
||||
* @param string $password Basic Auth Password
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setPassword($password)
|
||||
{
|
||||
$this->password = $password ?: $this->password;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the passthrough mode.
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function enablePassthroughMode()
|
||||
{
|
||||
$this->passthrough = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the passthrough mode.
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function disablePassthroughMode()
|
||||
{
|
||||
$this->passthrough = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set config object.
|
||||
*
|
||||
* @param \PicoFeed\Config\Config $config Config instance
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setConfig(Config $config)
|
||||
{
|
||||
if ($config !== null) {
|
||||
$this->setTimeout($config->getClientTimeout());
|
||||
$this->setUserAgent($config->getClientUserAgent());
|
||||
$this->setMaxRedirections($config->getMaxRedirections());
|
||||
$this->setMaxBodySize($config->getMaxBodySize());
|
||||
$this->setProxyHostname($config->getProxyHostname());
|
||||
$this->setProxyPort($config->getProxyPort());
|
||||
$this->setProxyUsername($config->getProxyUsername());
|
||||
$this->setProxyPassword($config->getProxyPassword());
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the HTTP status code is a redirection
|
||||
*
|
||||
* @access protected
|
||||
* @param integer $code
|
||||
* @return boolean
|
||||
*/
|
||||
public function isRedirection($code)
|
||||
{
|
||||
return $code == 301 || $code == 302 || $code == 303 || $code == 307;
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace PicoFeed\Client;
|
||||
|
||||
use PicoFeed\PicoFeedException;
|
||||
|
||||
/**
|
||||
* ClientException Exception.
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
abstract class ClientException extends PicoFeedException
|
||||
{
|
||||
}
|
|
@ -1,384 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace PicoFeed\Client;
|
||||
|
||||
use PicoFeed\Logging\Logger;
|
||||
|
||||
/**
|
||||
* cURL HTTP client.
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Curl extends Client
|
||||
{
|
||||
/**
|
||||
* HTTP response body.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $body = '';
|
||||
|
||||
/**
|
||||
* Body size.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $body_length = 0;
|
||||
|
||||
/**
|
||||
* HTTP response headers.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $response_headers = array();
|
||||
|
||||
/**
|
||||
* Counter on the number of header received.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $response_headers_count = 0;
|
||||
|
||||
/**
|
||||
* cURL callback to read the HTTP body.
|
||||
*
|
||||
* If the function return -1, curl stop to read the HTTP response
|
||||
*
|
||||
* @param resource $ch cURL handler
|
||||
* @param string $buffer Chunk of data
|
||||
*
|
||||
* @return int Length of the buffer
|
||||
*/
|
||||
public function readBody($ch, $buffer)
|
||||
{
|
||||
$length = strlen($buffer);
|
||||
$this->body_length += $length;
|
||||
|
||||
if ($this->body_length > $this->max_body_size) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
$this->body .= $buffer;
|
||||
|
||||
return $length;
|
||||
}
|
||||
|
||||
/**
|
||||
* cURL callback to read HTTP headers.
|
||||
*
|
||||
* @param resource $ch cURL handler
|
||||
* @param string $buffer Header line
|
||||
*
|
||||
* @return int Length of the buffer
|
||||
*/
|
||||
public function readHeaders($ch, $buffer)
|
||||
{
|
||||
$length = strlen($buffer);
|
||||
|
||||
if ($buffer === "\r\n" || $buffer === "\n") {
|
||||
++$this->response_headers_count;
|
||||
} else {
|
||||
if (!isset($this->response_headers[$this->response_headers_count])) {
|
||||
$this->response_headers[$this->response_headers_count] = '';
|
||||
}
|
||||
|
||||
$this->response_headers[$this->response_headers_count] .= $buffer;
|
||||
}
|
||||
|
||||
return $length;
|
||||
}
|
||||
|
||||
/**
|
||||
* cURL callback to passthrough the HTTP body to the client.
|
||||
*
|
||||
* If the function return -1, curl stop to read the HTTP response
|
||||
*
|
||||
* @param resource $ch cURL handler
|
||||
* @param string $buffer Chunk of data
|
||||
*
|
||||
* @return int Length of the buffer
|
||||
*/
|
||||
public function passthroughBody($ch, $buffer)
|
||||
{
|
||||
// do it only at the beginning of a transmission
|
||||
if ($this->body_length === 0) {
|
||||
list($status, $headers) = HttpHeaders::parse(explode("\n", $this->response_headers[$this->response_headers_count - 1]));
|
||||
|
||||
if ($this->isRedirection($status)) {
|
||||
return $this->handleRedirection($headers['Location']);
|
||||
}
|
||||
|
||||
header($status);
|
||||
|
||||
if (isset($headers['Content-Type'])) {
|
||||
header('Content-Type:' .$headers['Content-Type']);
|
||||
}
|
||||
}
|
||||
|
||||
$length = strlen($buffer);
|
||||
$this->body_length += $length;
|
||||
|
||||
echo $buffer;
|
||||
|
||||
return $length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare HTTP headers.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private function prepareHeaders()
|
||||
{
|
||||
$headers = array(
|
||||
'Connection: close',
|
||||
);
|
||||
|
||||
if ($this->etag) {
|
||||
$headers[] = 'If-None-Match: '.$this->etag;
|
||||
}
|
||||
|
||||
if ($this->last_modified) {
|
||||
$headers[] = 'If-Modified-Since: '.$this->last_modified;
|
||||
}
|
||||
|
||||
$headers = array_merge($headers, $this->request_headers);
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare curl proxy context.
|
||||
*
|
||||
* @param resource $ch
|
||||
*
|
||||
* @return resource $ch
|
||||
*/
|
||||
private function prepareProxyContext($ch)
|
||||
{
|
||||
if ($this->proxy_hostname) {
|
||||
Logger::setMessage(get_called_class().' Proxy: '.$this->proxy_hostname.':'.$this->proxy_port);
|
||||
|
||||
curl_setopt($ch, CURLOPT_PROXYPORT, $this->proxy_port);
|
||||
curl_setopt($ch, CURLOPT_PROXYTYPE, 'HTTP');
|
||||
curl_setopt($ch, CURLOPT_PROXY, $this->proxy_hostname);
|
||||
|
||||
if ($this->proxy_username) {
|
||||
Logger::setMessage(get_called_class().' Proxy credentials: Yes');
|
||||
curl_setopt($ch, CURLOPT_PROXYUSERPWD, $this->proxy_username.':'.$this->proxy_password);
|
||||
} else {
|
||||
Logger::setMessage(get_called_class().' Proxy credentials: No');
|
||||
}
|
||||
}
|
||||
|
||||
return $ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare curl auth context.
|
||||
*
|
||||
* @param resource $ch
|
||||
*
|
||||
* @return resource $ch
|
||||
*/
|
||||
private function prepareAuthContext($ch)
|
||||
{
|
||||
if ($this->username && $this->password) {
|
||||
curl_setopt($ch, CURLOPT_USERPWD, $this->username.':'.$this->password);
|
||||
}
|
||||
|
||||
return $ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set write/header functions.
|
||||
*
|
||||
* @param resource $ch
|
||||
*
|
||||
* @return resource $ch
|
||||
*/
|
||||
private function prepareDownloadMode($ch)
|
||||
{
|
||||
$write_function = 'readBody';
|
||||
$header_function = 'readHeaders';
|
||||
|
||||
if ($this->isPassthroughEnabled()) {
|
||||
$write_function = 'passthroughBody';
|
||||
}
|
||||
|
||||
curl_setopt($ch, CURLOPT_WRITEFUNCTION, array($this, $write_function));
|
||||
curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, $header_function));
|
||||
|
||||
return $ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare curl context.
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
private function prepareContext()
|
||||
{
|
||||
$ch = curl_init();
|
||||
|
||||
curl_setopt($ch, CURLOPT_URL, $this->url);
|
||||
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->timeout);
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, $this->user_agent);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $this->prepareHeaders());
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
|
||||
curl_setopt($ch, CURLOPT_ENCODING, '');
|
||||
curl_setopt($ch, CURLOPT_COOKIEJAR, 'php://memory');
|
||||
curl_setopt($ch, CURLOPT_COOKIEFILE, 'php://memory');
|
||||
|
||||
// Disable SSLv3 by enforcing TLSv1.x for curl >= 7.34.0 and < 7.39.0.
|
||||
// Versions prior to 7.34 and at least when compiled against openssl
|
||||
// interpret this parameter as "limit to TLSv1.0" which fails for sites
|
||||
// which enforce TLS 1.1+.
|
||||
// Starting with curl 7.39.0 SSLv3 is disabled by default.
|
||||
$version = curl_version();
|
||||
if ($version['version_number'] >= 467456 && $version['version_number'] < 468736) {
|
||||
curl_setopt($ch, CURLOPT_SSLVERSION, 1);
|
||||
}
|
||||
|
||||
$ch = $this->prepareDownloadMode($ch);
|
||||
$ch = $this->prepareProxyContext($ch);
|
||||
$ch = $this->prepareAuthContext($ch);
|
||||
|
||||
return $ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute curl context.
|
||||
*/
|
||||
private function executeContext()
|
||||
{
|
||||
$ch = $this->prepareContext();
|
||||
curl_exec($ch);
|
||||
|
||||
Logger::setMessage(get_called_class().' cURL total time: '.curl_getinfo($ch, CURLINFO_TOTAL_TIME));
|
||||
Logger::setMessage(get_called_class().' cURL dns lookup time: '.curl_getinfo($ch, CURLINFO_NAMELOOKUP_TIME));
|
||||
Logger::setMessage(get_called_class().' cURL connect time: '.curl_getinfo($ch, CURLINFO_CONNECT_TIME));
|
||||
Logger::setMessage(get_called_class().' cURL speed download: '.curl_getinfo($ch, CURLINFO_SPEED_DOWNLOAD));
|
||||
Logger::setMessage(get_called_class().' cURL effective url: '.curl_getinfo($ch, CURLINFO_EFFECTIVE_URL));
|
||||
|
||||
$curl_errno = curl_errno($ch);
|
||||
|
||||
if ($curl_errno) {
|
||||
Logger::setMessage(get_called_class().' cURL error: '.curl_error($ch));
|
||||
curl_close($ch);
|
||||
|
||||
$this->handleError($curl_errno);
|
||||
}
|
||||
|
||||
// Update the url if there where redirects
|
||||
$this->url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
|
||||
|
||||
curl_close($ch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the HTTP request.
|
||||
*
|
||||
* @return array HTTP response ['body' => ..., 'status' => ..., 'headers' => ...]
|
||||
*/
|
||||
public function doRequest()
|
||||
{
|
||||
$this->executeContext();
|
||||
|
||||
list($status, $headers) = HttpHeaders::parse(explode("\n", $this->response_headers[$this->response_headers_count - 1]));
|
||||
|
||||
if ($this->isRedirection($status)) {
|
||||
return $this->handleRedirection($headers['Location']);
|
||||
}
|
||||
|
||||
return array(
|
||||
'status' => $status,
|
||||
'body' => $this->body,
|
||||
'headers' => $headers,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle HTTP redirects
|
||||
*
|
||||
* @param string $location Redirected URL
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function handleRedirection($location)
|
||||
{
|
||||
$nb_redirects = 0;
|
||||
$result = array();
|
||||
$this->url = Url::resolve($location, $this->url);
|
||||
$this->body = '';
|
||||
$this->body_length = 0;
|
||||
$this->response_headers = array();
|
||||
$this->response_headers_count = 0;
|
||||
|
||||
while (true) {
|
||||
++$nb_redirects;
|
||||
|
||||
if ($nb_redirects >= $this->max_redirects) {
|
||||
throw new MaxRedirectException('Maximum number of redirections reached');
|
||||
}
|
||||
|
||||
$result = $this->doRequest();
|
||||
|
||||
if ($this->isRedirection($result['status'])) {
|
||||
$this->url = Url::resolve($result['headers']['Location'], $this->url);
|
||||
$this->body = '';
|
||||
$this->body_length = 0;
|
||||
$this->response_headers = array();
|
||||
$this->response_headers_count = 0;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle cURL errors (throw individual exceptions).
|
||||
*
|
||||
* We don't use constants because they are not necessary always available
|
||||
* (depends of the version of libcurl linked to php)
|
||||
*
|
||||
* @see http://curl.haxx.se/libcurl/c/libcurl-errors.html
|
||||
*
|
||||
* @param int $errno cURL error code
|
||||
*/
|
||||
private function handleError($errno)
|
||||
{
|
||||
switch ($errno) {
|
||||
case 78: // CURLE_REMOTE_FILE_NOT_FOUND
|
||||
throw new InvalidUrlException('Resource not found');
|
||||
case 6: // CURLE_COULDNT_RESOLVE_HOST
|
||||
throw new InvalidUrlException('Unable to resolve hostname');
|
||||
case 7: // CURLE_COULDNT_CONNECT
|
||||
throw new InvalidUrlException('Unable to connect to the remote host');
|
||||
case 23: // CURLE_WRITE_ERROR
|
||||
throw new MaxSizeException('Maximum response size exceeded');
|
||||
case 28: // CURLE_OPERATION_TIMEDOUT
|
||||
throw new TimeoutException('Operation timeout');
|
||||
case 35: // CURLE_SSL_CONNECT_ERROR
|
||||
case 51: // CURLE_PEER_FAILED_VERIFICATION
|
||||
case 58: // CURLE_SSL_CERTPROBLEM
|
||||
case 60: // CURLE_SSL_CACERT
|
||||
case 59: // CURLE_SSL_CIPHER
|
||||
case 64: // CURLE_USE_SSL_FAILED
|
||||
case 66: // CURLE_SSL_ENGINE_INITFAILED
|
||||
case 77: // CURLE_SSL_CACERT_BADFILE
|
||||
case 83: // CURLE_SSL_ISSUER_ERROR
|
||||
throw new InvalidCertificateException('Invalid SSL certificate');
|
||||
case 47: // CURLE_TOO_MANY_REDIRECTS
|
||||
throw new MaxRedirectException('Maximum number of redirections reached');
|
||||
case 63: // CURLE_FILESIZE_EXCEEDED
|
||||
throw new MaxSizeException('Maximum response size exceeded');
|
||||
default:
|
||||
throw new InvalidUrlException('Unable to fetch the URL');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace PicoFeed\Client;
|
||||
|
||||
use ArrayAccess;
|
||||
use PicoFeed\Logging\Logger;
|
||||
|
||||
/**
|
||||
* Class to handle HTTP headers case insensitivity.
|
||||
*
|
||||
* @author Bernhard Posselt
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class HttpHeaders implements ArrayAccess
|
||||
{
|
||||
private $headers = array();
|
||||
|
||||
public function __construct(array $headers)
|
||||
{
|
||||
foreach ($headers as $key => $value) {
|
||||
$this->headers[strtolower($key)] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->headers[strtolower($offset)];
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->headers[strtolower($offset)] = $value;
|
||||
}
|
||||
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return isset($this->headers[strtolower($offset)]);
|
||||
}
|
||||
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->headers[strtolower($offset)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse HTTP headers.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @param array $lines List of headers
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function parse(array $lines)
|
||||
{
|
||||
$status = 0;
|
||||
$headers = array();
|
||||
|
||||
foreach ($lines as $line) {
|
||||
if (strpos($line, 'HTTP/1') === 0) {
|
||||
$headers = array();
|
||||
$status = (int) substr($line, 9, 3);
|
||||
} elseif (strpos($line, ': ') !== false) {
|
||||
list($name, $value) = explode(': ', $line);
|
||||
if ($value) {
|
||||
$headers[trim($name)] = trim($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Logger::setMessage(get_called_class().' HTTP status code: '.$status);
|
||||
|
||||
foreach ($headers as $name => $value) {
|
||||
Logger::setMessage(get_called_class().' HTTP header: '.$name.' => '.$value);
|
||||
}
|
||||
|
||||
return array($status, new self($headers));
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace PicoFeed\Client;
|
||||
|
||||
/**
|
||||
* InvalidCertificateException Exception.
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class InvalidCertificateException extends ClientException
|
||||
{
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace PicoFeed\Client;
|
||||
|
||||
/**
|
||||
* InvalidUrlException Exception.
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class InvalidUrlException extends ClientException
|
||||
{
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace PicoFeed\Client;
|
||||
|
||||
/**
|
||||
* MaxRedirectException Exception.
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class MaxRedirectException extends ClientException
|
||||
{
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace PicoFeed\Client;
|
||||
|
||||
/**
|
||||
* MaxSizeException Exception.
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class MaxSizeException extends ClientException
|
||||
{
|
||||
}
|
|
@ -1,201 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace PicoFeed\Client;
|
||||
|
||||
use PicoFeed\Logging\Logger;
|
||||
|
||||
/**
|
||||
* Stream context HTTP client.
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Stream extends Client
|
||||
{
|
||||
/**
|
||||
* Prepare HTTP headers.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private function prepareHeaders()
|
||||
{
|
||||
$headers = array(
|
||||
'Connection: close',
|
||||
'User-Agent: '.$this->user_agent,
|
||||
);
|
||||
|
||||
// disable compression in passthrough mode. It could result in double
|
||||
// compressed content which isn't decodeable by browsers
|
||||
if (function_exists('gzdecode') && !$this->isPassthroughEnabled()) {
|
||||
$headers[] = 'Accept-Encoding: gzip';
|
||||
}
|
||||
|
||||
if ($this->etag) {
|
||||
$headers[] = 'If-None-Match: '.$this->etag;
|
||||
}
|
||||
|
||||
if ($this->last_modified) {
|
||||
$headers[] = 'If-Modified-Since: '.$this->last_modified;
|
||||
}
|
||||
|
||||
if ($this->proxy_username) {
|
||||
$headers[] = 'Proxy-Authorization: Basic '.base64_encode($this->proxy_username.':'.$this->proxy_password);
|
||||
}
|
||||
|
||||
if ($this->username && $this->password) {
|
||||
$headers[] = 'Authorization: Basic '.base64_encode($this->username.':'.$this->password);
|
||||
}
|
||||
|
||||
$headers = array_merge($headers, $this->request_headers);
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the final URL from location headers.
|
||||
*
|
||||
* @param array $headers List of HTTP response header
|
||||
*/
|
||||
private function setEffectiveUrl($headers)
|
||||
{
|
||||
foreach ($headers as $header) {
|
||||
if (stripos($header, 'Location') === 0) {
|
||||
list(, $value) = explode(': ', $header);
|
||||
|
||||
$this->url = Url::resolve($value, $this->url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare stream context.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function prepareContext()
|
||||
{
|
||||
$context = array(
|
||||
'http' => array(
|
||||
'method' => 'GET',
|
||||
'protocol_version' => 1.1,
|
||||
'timeout' => $this->timeout,
|
||||
'max_redirects' => $this->max_redirects,
|
||||
),
|
||||
);
|
||||
|
||||
if ($this->proxy_hostname) {
|
||||
Logger::setMessage(get_called_class().' Proxy: '.$this->proxy_hostname.':'.$this->proxy_port);
|
||||
|
||||
$context['http']['proxy'] = 'tcp://'.$this->proxy_hostname.':'.$this->proxy_port;
|
||||
$context['http']['request_fulluri'] = true;
|
||||
|
||||
if ($this->proxy_username) {
|
||||
Logger::setMessage(get_called_class().' Proxy credentials: Yes');
|
||||
} else {
|
||||
Logger::setMessage(get_called_class().' Proxy credentials: No');
|
||||
}
|
||||
}
|
||||
|
||||
$context['http']['header'] = implode("\r\n", $this->prepareHeaders());
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the HTTP request.
|
||||
*
|
||||
* @return array HTTP response ['body' => ..., 'status' => ..., 'headers' => ...]
|
||||
*/
|
||||
public function doRequest()
|
||||
{
|
||||
$body = '';
|
||||
|
||||
// Create context
|
||||
$context = stream_context_create($this->prepareContext());
|
||||
|
||||
// Make HTTP request
|
||||
$stream = @fopen($this->url, 'r', false, $context);
|
||||
if (!is_resource($stream)) {
|
||||
throw new InvalidUrlException('Unable to establish a connection');
|
||||
}
|
||||
|
||||
// Get HTTP headers response
|
||||
$metadata = stream_get_meta_data($stream);
|
||||
list($status, $headers) = HttpHeaders::parse($metadata['wrapper_data']);
|
||||
|
||||
if ($this->isPassthroughEnabled()) {
|
||||
header(':', true, $status);
|
||||
|
||||
if (isset($headers['Content-Type'])) {
|
||||
header('Content-Type: '.$headers['Content-Type']);
|
||||
}
|
||||
|
||||
fpassthru($stream);
|
||||
} else {
|
||||
// Get the entire body until the max size
|
||||
$body = stream_get_contents($stream, $this->max_body_size + 1);
|
||||
|
||||
// If the body size is too large abort everything
|
||||
if (strlen($body) > $this->max_body_size) {
|
||||
throw new MaxSizeException('Content size too large');
|
||||
}
|
||||
|
||||
if ($metadata['timed_out']) {
|
||||
throw new TimeoutException('Operation timeout');
|
||||
}
|
||||
}
|
||||
|
||||
fclose($stream);
|
||||
|
||||
$this->setEffectiveUrl($metadata['wrapper_data']);
|
||||
|
||||
return array(
|
||||
'status' => $status,
|
||||
'body' => $this->decodeBody($body, $headers),
|
||||
'headers' => $headers,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode body response according to the HTTP headers.
|
||||
*
|
||||
* @param string $body Raw body
|
||||
* @param HttpHeaders $headers HTTP headers
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function decodeBody($body, HttpHeaders $headers)
|
||||
{
|
||||
if (isset($headers['Transfer-Encoding']) && $headers['Transfer-Encoding'] === 'chunked') {
|
||||
$body = $this->decodeChunked($body);
|
||||
}
|
||||
|
||||
if (isset($headers['Content-Encoding']) && $headers['Content-Encoding'] === 'gzip') {
|
||||
$body = gzdecode($body);
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a chunked body.
|
||||
*
|
||||
* @param string $str Raw body
|
||||
*
|
||||
* @return string Decoded body
|
||||
*/
|
||||
public function decodeChunked($str)
|
||||
{
|
||||
for ($result = ''; !empty($str); $str = trim($str)) {
|
||||
|
||||
// Get the chunk length
|
||||
$pos = strpos($str, "\r\n");
|
||||
$len = hexdec(substr($str, 0, $pos));
|
||||
|
||||
// Append the chunk to the result
|
||||
$result .= substr($str, $pos + 2, $len);
|
||||
$str = substr($str, $pos + 2 + $len);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace PicoFeed\Client;
|
||||
|
||||
/**
|
||||
* TimeoutException Exception.
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TimeoutException extends ClientException
|
||||
{
|
||||
}
|
290
vendor/fguillot/picofeed/lib/PicoFeed/Client/Url.php
vendored
290
vendor/fguillot/picofeed/lib/PicoFeed/Client/Url.php
vendored
|
@ -1,290 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace PicoFeed\Client;
|
||||
|
||||
/**
|
||||
* URL class.
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Url
|
||||
{
|
||||
/**
|
||||
* URL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $url = '';
|
||||
|
||||
/**
|
||||
* URL components.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $components = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $url URL
|
||||
*/
|
||||
public function __construct($url)
|
||||
{
|
||||
$this->url = $url;
|
||||
$this->components = parse_url($url) ?: array();
|
||||
|
||||
// Issue with PHP < 5.4.7 and protocol relative url
|
||||
if (version_compare(PHP_VERSION, '5.4.7', '<') && $this->isProtocolRelative()) {
|
||||
$pos = strpos($this->components['path'], '/', 2);
|
||||
|
||||
if ($pos === false) {
|
||||
$pos = strlen($this->components['path']);
|
||||
}
|
||||
|
||||
$this->components['host'] = substr($this->components['path'], 2, $pos - 2);
|
||||
$this->components['path'] = substr($this->components['path'], $pos);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut method to get an absolute url from relative url.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @param mixed $item_url Unknown url (can be relative or not)
|
||||
* @param mixed $website_url Website url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function resolve($item_url, $website_url)
|
||||
{
|
||||
$link = is_string($item_url) ? new self($item_url) : $item_url;
|
||||
$website = is_string($website_url) ? new self($website_url) : $website_url;
|
||||
|
||||
if ($link->isRelativeUrl()) {
|
||||
if ($link->isRelativePath()) {
|
||||
return $link->getAbsoluteUrl($website->getBaseUrl($website->getBasePath()));
|
||||
}
|
||||
|
||||
return $link->getAbsoluteUrl($website->getBaseUrl());
|
||||
} elseif ($link->isProtocolRelative()) {
|
||||
$link->setScheme($website->getScheme());
|
||||
}
|
||||
|
||||
return $link->getAbsoluteUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut method to get a base url.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @param string $url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function base($url)
|
||||
{
|
||||
$link = new self($url);
|
||||
|
||||
return $link->getBaseUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the base URL.
|
||||
*
|
||||
* @param string $suffix Add a suffix to the url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBaseUrl($suffix = '')
|
||||
{
|
||||
return $this->hasHost() ? $this->getScheme('://').$this->getHost().$this->getPort(':').$suffix : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the absolute URL.
|
||||
*
|
||||
* @param string $base_url Use this url as base url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAbsoluteUrl($base_url = '')
|
||||
{
|
||||
if ($base_url) {
|
||||
$base = new self($base_url);
|
||||
$url = $base->getAbsoluteUrl().substr($this->getFullPath(), 1);
|
||||
} else {
|
||||
$url = $this->hasHost() ? $this->getBaseUrl().$this->getFullPath() : '';
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the url is relative.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isRelativeUrl()
|
||||
{
|
||||
return !$this->hasScheme() && !$this->isProtocolRelative();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the path is relative.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isRelativePath()
|
||||
{
|
||||
$path = $this->getPath();
|
||||
|
||||
return empty($path) || $path{0}
|
||||
!== '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the path of a URI.
|
||||
*
|
||||
* Imported from Guzzle library: https://github.com/guzzle/psr7/blob/master/src/Uri.php#L568-L582
|
||||
*
|
||||
* @param $path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function filterPath($path, $charUnreserved = 'a-zA-Z0-9_\-\.~', $charSubDelims = '!\$&\'\(\)\*\+,;=')
|
||||
{
|
||||
return preg_replace_callback(
|
||||
'/(?:[^'.$charUnreserved.$charSubDelims.':@\/%]+|%(?![A-Fa-f0-9]{2}))/',
|
||||
function (array $matches) { return rawurlencode($matches[0]); },
|
||||
$path
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
return $this->filterPath(empty($this->components['path']) ? '' : $this->components['path']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the base path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBasePath()
|
||||
{
|
||||
$current_path = $this->getPath();
|
||||
|
||||
$path = $this->isRelativePath() ? '/' : '';
|
||||
$path .= substr($current_path, -1) === '/' ? $current_path : dirname($current_path);
|
||||
|
||||
return preg_replace('/\\\\\/|\/\//', '/', $path.'/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full path (path + querystring + fragment).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFullPath()
|
||||
{
|
||||
$path = $this->isRelativePath() ? '/' : '';
|
||||
$path .= $this->getPath();
|
||||
$path .= empty($this->components['query']) ? '' : '?'.$this->components['query'];
|
||||
$path .= empty($this->components['fragment']) ? '' : '#'.$this->components['fragment'];
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hostname.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHost()
|
||||
{
|
||||
return empty($this->components['host']) ? '' : $this->components['host'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the url has a hostname.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasHost()
|
||||
{
|
||||
return !empty($this->components['host']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the scheme.
|
||||
*
|
||||
* @param string $suffix Suffix to add when there is a scheme
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getScheme($suffix = '')
|
||||
{
|
||||
return ($this->hasScheme() ? $this->components['scheme'] : 'http').$suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the scheme.
|
||||
*
|
||||
* @param string $scheme Set a scheme
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function setScheme($scheme)
|
||||
{
|
||||
$this->components['scheme'] = $scheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the url has a scheme.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasScheme()
|
||||
{
|
||||
return !empty($this->components['scheme']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the port.
|
||||
*
|
||||
* @param string $prefix Prefix to add when there is a port
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPort($prefix = '')
|
||||
{
|
||||
return $this->hasPort() ? $prefix.$this->components['port'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the url has a port.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPort()
|
||||
{
|
||||
return !empty($this->components['port']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the url is protocol relative (start with //).
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isProtocolRelative()
|
||||
{
|
||||
return strpos($this->url, '//') === 0;
|
||||
}
|
||||
}
|
Reference in a new issue