diff options
Diffstat (limited to 'inc/mailgun/php-http/message/src')
45 files changed, 3344 insertions, 0 deletions
diff --git a/inc/mailgun/php-http/message/src/Authentication.php b/inc/mailgun/php-http/message/src/Authentication.php new file mode 100644 index 0000000..b50366f --- /dev/null +++ b/inc/mailgun/php-http/message/src/Authentication.php @@ -0,0 +1,22 @@ +<?php + +namespace Http\Message; + +use Psr\Http\Message\RequestInterface; + +/** + * Authenticate a PSR-7 Request. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +interface Authentication +{ + /** + * Authenticates a request. + * + * @param RequestInterface $request + * + * @return RequestInterface + */ + public function authenticate(RequestInterface $request); +} diff --git a/inc/mailgun/php-http/message/src/Authentication/AutoBasicAuth.php b/inc/mailgun/php-http/message/src/Authentication/AutoBasicAuth.php new file mode 100644 index 0000000..7b6a429 --- /dev/null +++ b/inc/mailgun/php-http/message/src/Authentication/AutoBasicAuth.php @@ -0,0 +1,48 @@ +<?php + +namespace Http\Message\Authentication; + +use Http\Message\Authentication; +use Psr\Http\Message\RequestInterface; + +/** + * Authenticate a PSR-7 Request using Basic Auth based on credentials in the URI. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +final class AutoBasicAuth implements Authentication +{ + /** + * Whether user info should be removed from the URI. + * + * @var bool + */ + private $shouldRemoveUserInfo; + + /** + * @param bool|true $shouldRremoveUserInfo + */ + public function __construct($shouldRremoveUserInfo = true) + { + $this->shouldRemoveUserInfo = (bool) $shouldRremoveUserInfo; + } + + /** + * {@inheritdoc} + */ + public function authenticate(RequestInterface $request) + { + $uri = $request->getUri(); + $userInfo = $uri->getUserInfo(); + + if (!empty($userInfo)) { + if ($this->shouldRemoveUserInfo) { + $request = $request->withUri($uri->withUserInfo('')); + } + + $request = $request->withHeader('Authorization', sprintf('Basic %s', base64_encode($userInfo))); + } + + return $request; + } +} diff --git a/inc/mailgun/php-http/message/src/Authentication/BasicAuth.php b/inc/mailgun/php-http/message/src/Authentication/BasicAuth.php new file mode 100644 index 0000000..23618a5 --- /dev/null +++ b/inc/mailgun/php-http/message/src/Authentication/BasicAuth.php @@ -0,0 +1,44 @@ +<?php + +namespace Http\Message\Authentication; + +use Http\Message\Authentication; +use Psr\Http\Message\RequestInterface; + +/** + * Authenticate a PSR-7 Request using Basic Auth. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +final class BasicAuth implements Authentication +{ + /** + * @var string + */ + private $username; + + /** + * @var string + */ + private $password; + + /** + * @param string $username + * @param string $password + */ + public function __construct($username, $password) + { + $this->username = $username; + $this->password = $password; + } + + /** + * {@inheritdoc} + */ + public function authenticate(RequestInterface $request) + { + $header = sprintf('Basic %s', base64_encode(sprintf('%s:%s', $this->username, $this->password))); + + return $request->withHeader('Authorization', $header); + } +} diff --git a/inc/mailgun/php-http/message/src/Authentication/Bearer.php b/inc/mailgun/php-http/message/src/Authentication/Bearer.php new file mode 100644 index 0000000..a8fb21a --- /dev/null +++ b/inc/mailgun/php-http/message/src/Authentication/Bearer.php @@ -0,0 +1,37 @@ +<?php + +namespace Http\Message\Authentication; + +use Http\Message\Authentication; +use Psr\Http\Message\RequestInterface; + +/** + * Authenticate a PSR-7 Request using a token. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +final class Bearer implements Authentication +{ + /** + * @var string + */ + private $token; + + /** + * @param string $token + */ + public function __construct($token) + { + $this->token = $token; + } + + /** + * {@inheritdoc} + */ + public function authenticate(RequestInterface $request) + { + $header = sprintf('Bearer %s', $this->token); + + return $request->withHeader('Authorization', $header); + } +} diff --git a/inc/mailgun/php-http/message/src/Authentication/Chain.php b/inc/mailgun/php-http/message/src/Authentication/Chain.php new file mode 100644 index 0000000..71002bb --- /dev/null +++ b/inc/mailgun/php-http/message/src/Authentication/Chain.php @@ -0,0 +1,47 @@ +<?php + +namespace Http\Message\Authentication; + +use Http\Message\Authentication; +use Psr\Http\Message\RequestInterface; + +/** + * Authenticate a PSR-7 Request with a multiple authentication methods. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +final class Chain implements Authentication +{ + /** + * @var Authentication[] + */ + private $authenticationChain = []; + + /** + * @param Authentication[] $authenticationChain + */ + public function __construct(array $authenticationChain = []) + { + foreach ($authenticationChain as $authentication) { + if (!$authentication instanceof Authentication) { + throw new \InvalidArgumentException( + 'Members of the authentication chain must be of type Http\Message\Authentication' + ); + } + } + + $this->authenticationChain = $authenticationChain; + } + + /** + * {@inheritdoc} + */ + public function authenticate(RequestInterface $request) + { + foreach ($this->authenticationChain as $authentication) { + $request = $authentication->authenticate($request); + } + + return $request; + } +} diff --git a/inc/mailgun/php-http/message/src/Authentication/Matching.php b/inc/mailgun/php-http/message/src/Authentication/Matching.php new file mode 100644 index 0000000..4b89b50 --- /dev/null +++ b/inc/mailgun/php-http/message/src/Authentication/Matching.php @@ -0,0 +1,74 @@ +<?php + +namespace Http\Message\Authentication; + +use Http\Message\Authentication; +use Http\Message\RequestMatcher\CallbackRequestMatcher; +use Psr\Http\Message\RequestInterface; + +@trigger_error('The '.__NAMESPACE__.'\Matching class is deprecated since version 1.2 and will be removed in 2.0. Use Http\Message\Authentication\RequestConditional instead.', E_USER_DEPRECATED); + +/** + * Authenticate a PSR-7 Request if the request is matching. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + * + * @deprecated since since version 1.2, and will be removed in 2.0. Use {@link RequestConditional} instead. + */ +final class Matching implements Authentication +{ + /** + * @var Authentication + */ + private $authentication; + + /** + * @var CallbackRequestMatcher + */ + private $matcher; + + /** + * @param Authentication $authentication + * @param callable|null $matcher + */ + public function __construct(Authentication $authentication, callable $matcher = null) + { + if (is_null($matcher)) { + $matcher = function () { + return true; + }; + } + + $this->authentication = $authentication; + $this->matcher = new CallbackRequestMatcher($matcher); + } + + /** + * {@inheritdoc} + */ + public function authenticate(RequestInterface $request) + { + if ($this->matcher->matches($request)) { + return $this->authentication->authenticate($request); + } + + return $request; + } + + /** + * Creates a matching authentication for an URL. + * + * @param Authentication $authentication + * @param string $url + * + * @return self + */ + public static function createUrlMatcher(Authentication $authentication, $url) + { + $matcher = function (RequestInterface $request) use ($url) { + return preg_match($url, $request->getRequestTarget()); + }; + + return new static($authentication, $matcher); + } +} diff --git a/inc/mailgun/php-http/message/src/Authentication/QueryParam.php b/inc/mailgun/php-http/message/src/Authentication/QueryParam.php new file mode 100644 index 0000000..14b58ff --- /dev/null +++ b/inc/mailgun/php-http/message/src/Authentication/QueryParam.php @@ -0,0 +1,50 @@ +<?php + +namespace Http\Message\Authentication; + +use Http\Message\Authentication; +use Psr\Http\Message\RequestInterface; + +/** + * Authenticate a PSR-7 Request by adding parameters to its query. + * + * Note: Although in some cases it can be useful, we do not recommend using query parameters for authentication. + * Credentials in the URL is generally unsafe as they are not encrypted, anyone can see them. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +final class QueryParam implements Authentication +{ + /** + * @var array + */ + private $params = []; + + /** + * @param array $params + */ + public function __construct(array $params) + { + $this->params = $params; + } + + /** + * {@inheritdoc} + */ + public function authenticate(RequestInterface $request) + { + $uri = $request->getUri(); + $query = $uri->getQuery(); + $params = []; + + parse_str($query, $params); + + $params = array_merge($params, $this->params); + + $query = http_build_query($params); + + $uri = $uri->withQuery($query); + + return $request->withUri($uri); + } +} diff --git a/inc/mailgun/php-http/message/src/Authentication/RequestConditional.php b/inc/mailgun/php-http/message/src/Authentication/RequestConditional.php new file mode 100644 index 0000000..5477440 --- /dev/null +++ b/inc/mailgun/php-http/message/src/Authentication/RequestConditional.php @@ -0,0 +1,47 @@ +<?php + +namespace Http\Message\Authentication; + +use Http\Message\Authentication; +use Http\Message\RequestMatcher; +use Psr\Http\Message\RequestInterface; + +/** + * Authenticate a PSR-7 Request if the request is matching the given request matcher. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +final class RequestConditional implements Authentication +{ + /** + * @var RequestMatcher + */ + private $requestMatcher; + + /** + * @var Authentication + */ + private $authentication; + + /** + * @param RequestMatcher $requestMatcher + * @param Authentication $authentication + */ + public function __construct(RequestMatcher $requestMatcher, Authentication $authentication) + { + $this->requestMatcher = $requestMatcher; + $this->authentication = $authentication; + } + + /** + * {@inheritdoc} + */ + public function authenticate(RequestInterface $request) + { + if ($this->requestMatcher->matches($request)) { + return $this->authentication->authenticate($request); + } + + return $request; + } +} diff --git a/inc/mailgun/php-http/message/src/Authentication/Wsse.php b/inc/mailgun/php-http/message/src/Authentication/Wsse.php new file mode 100644 index 0000000..fbbde33 --- /dev/null +++ b/inc/mailgun/php-http/message/src/Authentication/Wsse.php @@ -0,0 +1,58 @@ +<?php + +namespace Http\Message\Authentication; + +use Http\Message\Authentication; +use Psr\Http\Message\RequestInterface; + +/** + * Authenticate a PSR-7 Request using WSSE. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +final class Wsse implements Authentication +{ + /** + * @var string + */ + private $username; + + /** + * @var string + */ + private $password; + + /** + * @param string $username + * @param string $password + */ + public function __construct($username, $password) + { + $this->username = $username; + $this->password = $password; + } + + /** + * {@inheritdoc} + */ + public function authenticate(RequestInterface $request) + { + // TODO: generate better nonce? + $nonce = substr(md5(uniqid(uniqid().'_', true)), 0, 16); + $created = date('c'); + $digest = base64_encode(sha1(base64_decode($nonce).$created.$this->password, true)); + + $wsse = sprintf( + 'UsernameToken Username="%s", PasswordDigest="%s", Nonce="%s", Created="%s"', + $this->username, + $digest, + $nonce, + $created + ); + + return $request + ->withHeader('Authorization', 'WSSE profile="UsernameToken"') + ->withHeader('X-WSSE', $wsse) + ; + } +} diff --git a/inc/mailgun/php-http/message/src/Builder/ResponseBuilder.php b/inc/mailgun/php-http/message/src/Builder/ResponseBuilder.php new file mode 100644 index 0000000..e6933a0 --- /dev/null +++ b/inc/mailgun/php-http/message/src/Builder/ResponseBuilder.php @@ -0,0 +1,148 @@ +<?php + +namespace Http\Message\Builder; + +use Psr\Http\Message\ResponseInterface; + +/** + * Fills response object with values. + */ +class ResponseBuilder +{ + /** + * The response to be built. + * + * @var ResponseInterface + */ + protected $response; + + /** + * Create builder for the given response. + * + * @param ResponseInterface $response + */ + public function __construct(ResponseInterface $response) + { + $this->response = $response; + } + + /** + * Return response. + * + * @return ResponseInterface + */ + public function getResponse() + { + return $this->response; + } + + /** + * Add headers represented by an array of header lines. + * + * @param string[] $headers Response headers as array of header lines. + * + * @return $this + * + * @throws \UnexpectedValueException For invalid header values. + * @throws \InvalidArgumentException For invalid status code arguments. + */ + public function setHeadersFromArray(array $headers) + { + $status = array_shift($headers); + $this->setStatus($status); + + foreach ($headers as $headerLine) { + $headerLine = trim($headerLine); + if ('' === $headerLine) { + continue; + } + + $this->addHeader($headerLine); + } + + return $this; + } + + /** + * Add headers represented by a single string. + * + * @param string $headers Response headers as single string. + * + * @return $this + * + * @throws \InvalidArgumentException if $headers is not a string on object with __toString() + * @throws \UnexpectedValueException For invalid header values. + */ + public function setHeadersFromString($headers) + { + if (!(is_string($headers) + || (is_object($headers) && method_exists($headers, '__toString'))) + ) { + throw new \InvalidArgumentException( + sprintf( + '%s expects parameter 1 to be a string, %s given', + __METHOD__, + is_object($headers) ? get_class($headers) : gettype($headers) + ) + ); + } + + $this->setHeadersFromArray(explode("\r\n", $headers)); + + return $this; + } + + /** + * Set response status from a status string. + * + * @param string $statusLine Response status as a string. + * + * @return $this + * + * @throws \InvalidArgumentException For invalid status line. + */ + public function setStatus($statusLine) + { + $parts = explode(' ', $statusLine, 3); + if (count($parts) < 2 || strpos(strtolower($parts[0]), 'http/') !== 0) { + throw new \InvalidArgumentException( + sprintf('"%s" is not a valid HTTP status line', $statusLine) + ); + } + + $reasonPhrase = count($parts) > 2 ? $parts[2] : ''; + $this->response = $this->response + ->withStatus((int) $parts[1], $reasonPhrase) + ->withProtocolVersion(substr($parts[0], 5)); + + return $this; + } + + /** + * Add header represented by a string. + * + * @param string $headerLine Response header as a string. + * + * @return $this + * + * @throws \InvalidArgumentException For invalid header names or values. + */ + public function addHeader($headerLine) + { + $parts = explode(':', $headerLine, 2); + if (count($parts) !== 2) { + throw new \InvalidArgumentException( + sprintf('"%s" is not a valid HTTP header line', $headerLine) + ); + } + $name = trim($parts[0]); + $value = trim($parts[1]); + if ($this->response->hasHeader($name)) { + $this->response = $this->response->withAddedHeader($name, $value); + } else { + $this->response = $this->response->withHeader($name, $value); + } + + return $this; + } +} diff --git a/inc/mailgun/php-http/message/src/Cookie.php b/inc/mailgun/php-http/message/src/Cookie.php new file mode 100644 index 0000000..5f61b90 --- /dev/null +++ b/inc/mailgun/php-http/message/src/Cookie.php @@ -0,0 +1,526 @@ +<?php + +namespace Http\Message; + +/** + * Cookie Value Object. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + * + * @see http://tools.ietf.org/search/rfc6265 + */ +final class Cookie +{ + /** + * @var string + */ + private $name; + + /** + * @var string|null + */ + private $value; + + /** + * @var int|null + */ + private $maxAge; + + /** + * @var string|null + */ + private $domain; + + /** + * @var string + */ + private $path; + + /** + * @var bool + */ + private $secure; + + /** + * @var bool + */ + private $httpOnly; + + /** + * Expires attribute is HTTP 1.0 only and should be avoided. + * + * @var \DateTime|null + */ + private $expires; + + /** + * @param string $name + * @param string|null $value + * @param int $maxAge + * @param string|null $domain + * @param string|null $path + * @param bool $secure + * @param bool $httpOnly + * @param \DateTime|null $expires Expires attribute is HTTP 1.0 only and should be avoided. + * + * @throws \InvalidArgumentException If name, value or max age is not valid. + */ + public function __construct( + $name, + $value = null, + $maxAge = null, + $domain = null, + $path = null, + $secure = false, + $httpOnly = false, + \DateTime $expires = null + ) { + $this->validateName($name); + $this->validateValue($value); + $this->validateMaxAge($maxAge); + + $this->name = $name; + $this->value = $value; + $this->maxAge = $maxAge; + $this->expires = $expires; + $this->domain = $this->normalizeDomain($domain); + $this->path = $this->normalizePath($path); + $this->secure = (bool) $secure; + $this->httpOnly = (bool) $httpOnly; + } + + /** + * Creates a new cookie without any attribute validation. + * + * @param string $name + * @param string|null $value + * @param int $maxAge + * @param string|null $domain + * @param string|null $path + * @param bool $secure + * @param bool $httpOnly + * @param \DateTime|null $expires Expires attribute is HTTP 1.0 only and should be avoided. + */ + public static function createWithoutValidation( + $name, + $value = null, + $maxAge = null, + $domain = null, + $path = null, + $secure = false, + $httpOnly = false, + \DateTime $expires = null + ) { + $cookie = new self('name', null, null, $domain, $path, $secure, $httpOnly, $expires); + $cookie->name = $name; + $cookie->value = $value; + $cookie->maxAge = $maxAge; + + return $cookie; + } + + /** + * Returns the name. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Returns the value. + * + * @return string|null + */ + public function getValue() + { + return $this->value; + } + + /** + * Checks if there is a value. + * + * @return bool + */ + public function hasValue() + { + return isset($this->value); + } + + /** + * Sets the value. + * + * @param string|null $value + * + * @return Cookie + */ + public function withValue($value) + { + $this->validateValue($value); + + $new = clone $this; + $new->value = $value; + + return $new; + } + + /** + * Returns the max age. + * + * @return int|null + */ + public function getMaxAge() + { + return $this->maxAge; + } + + /** + * Checks if there is a max age. + * + * @return bool + */ + public function hasMaxAge() + { + return isset($this->maxAge); + } + + /** + * Sets the max age. + * + * @param int|null $maxAge + * + * @return Cookie + */ + public function withMaxAge($maxAge) + { + $this->validateMaxAge($maxAge); + + $new = clone $this; + $new->maxAge = $maxAge; + + return $new; + } + + /** + * Returns the expiration time. + * + * @return \DateTime|null + */ + public function getExpires() + { + return $this->expires; + } + + /** + * Checks if there is an expiration time. + * + * @return bool + */ + public function hasExpires() + { + return isset($this->expires); + } + + /** + * Sets the expires. + * + * @param \DateTime|null $expires + * + * @return Cookie + */ + public function withExpires(\DateTime $expires = null) + { + $new = clone $this; + $new->expires = $expires; + + return $new; + } + + /** + * Checks if the cookie is expired. + * + * @return bool + */ + public function isExpired() + { + return isset($this->expires) and $this->expires < new \DateTime(); + } + + /** + * Returns the domain. + * + * @return string|null + */ + public function getDomain() + { + return $this->domain; + } + + /** + * Checks if there is a domain. + * + * @return bool + */ + public function hasDomain() + { + return isset($this->domain); + } + + /** + * Sets the domain. + * + * @param string|null $domain + * + * @return Cookie + */ + public function withDomain($domain) + { + $new = clone $this; + $new->domain = $this->normalizeDomain($domain); + + return $new; + } + + /** + * Checks whether this cookie is meant for this domain. + * + * @see http://tools.ietf.org/html/rfc6265#section-5.1.3 + * + * @param string $domain + * + * @return bool + */ + public function matchDomain($domain) + { + // Domain is not set or exact match + if (!$this->hasDomain() || strcasecmp($domain, $this->domain) === 0) { + return true; + } + + // Domain is not an IP address + if (filter_var($domain, FILTER_VALIDATE_IP)) { + return false; + } + + return (bool) preg_match(sprintf('/\b%s$/i', preg_quote($this->domain)), $domain); + } + + /** + * Returns the path. + * + * @return string + */ + public function getPath() + { + return $this->path; + } + + /** + * Sets the path. + * + * @param string|null $path + * + * @return Cookie + */ + public function withPath($path) + { + $new = clone $this; + $new->path = $this->normalizePath($path); + + return $new; + } + + /** + * Checks whether this cookie is meant for this path. + * + * @see http://tools.ietf.org/html/rfc6265#section-5.1.4 + * + * @param string $path + * + * @return bool + */ + public function matchPath($path) + { + return $this->path === $path || (strpos($path, rtrim($this->path, '/').'/') === 0); + } + + /** + * Checks whether this cookie may only be sent over HTTPS. + * + * @return bool + */ + public function isSecure() + { + return $this->secure; + } + + /** + * Sets whether this cookie should only be sent over HTTPS. + * + * @param bool $secure + * + * @return Cookie + */ + public function withSecure($secure) + { + $new = clone $this; + $new->secure = (bool) $secure; + + return $new; + } + + /** + * Check whether this cookie may not be accessed through Javascript. + * + * @return bool + */ + public function isHttpOnly() + { + return $this->httpOnly; + } + + /** + * Sets whether this cookie may not be accessed through Javascript. + * + * @param bool $httpOnly + * + * @return Cookie + */ + public function withHttpOnly($httpOnly) + { + $new = clone $this; + $new->httpOnly = (bool) $httpOnly; + + return $new; + } + + /** + * Checks if this cookie represents the same cookie as $cookie. + * + * This does not compare the values, only name, domain and path. + * + * @param Cookie $cookie + * + * @return bool + */ + public function match(Cookie $cookie) + { + return $this->name === $cookie->name && $this->domain === $cookie->domain and $this->path === $cookie->path; + } + + /** + * Validates cookie attributes. + * + * @return bool + */ + public function isValid() + { + try { + $this->validateName($this->name); + $this->validateValue($this->value); + $this->validateMaxAge($this->maxAge); + } catch (\InvalidArgumentException $e) { + return false; + } + + return true; + } + + /** + * Validates the name attribute. + * + * @see http://tools.ietf.org/search/rfc2616#section-2.2 + * + * @param string $name + * + * @throws \InvalidArgumentException If the name is empty or contains invalid characters. + */ + private function validateName($name) + { + if (strlen($name) < 1) { + throw new \InvalidArgumentException('The name cannot be empty'); + } + + // Name attribute is a token as per spec in RFC 2616 + if (preg_match('/[\x00-\x20\x22\x28-\x29\x2C\x2F\x3A-\x40\x5B-\x5D\x7B\x7D\x7F]/', $name)) { + throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $name)); + } + } + + /** + * Validates a value. + * + * @see http://tools.ietf.org/html/rfc6265#section-4.1.1 + * + * @param string|null $value + * + * @throws \InvalidArgumentException If the value contains invalid characters. + */ + private function validateValue($value) + { + if (isset($value)) { + if (preg_match('/[^\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]/', $value)) { + throw new \InvalidArgumentException(sprintf('The cookie value "%s" contains invalid characters.', $value)); + } + } + } + + /** + * Validates a Max-Age attribute. + * + * @param int|null $maxAge + * + * @throws \InvalidArgumentException If the Max-Age is not an empty or integer value. + */ + private function validateMaxAge($maxAge) + { + if (isset($maxAge)) { + if (!is_int($maxAge)) { + throw new \InvalidArgumentException('Max-Age must be integer'); + } + } + } + + /** + * Remove the leading '.' and lowercase the domain as per spec in RFC 6265. + * + * @see http://tools.ietf.org/html/rfc6265#section-4.1.2.3 + * @see http://tools.ietf.org/html/rfc6265#section-5.1.3 + * @see http://tools.ietf.org/html/rfc6265#section-5.2.3 + * + * @param string|null $domain + * + * @return string + */ + private function normalizeDomain($domain) + { + if (isset($domain)) { + $domain = ltrim(strtolower($domain), '.'); + } + + return $domain; + } + + /** + * Processes path as per spec in RFC 6265. + * + * @see http://tools.ietf.org/html/rfc6265#section-5.1.4 + * @see http://tools.ietf.org/html/rfc6265#section-5.2.4 + * + * @param string|null $path + * + * @return string + */ + private function normalizePath($path) + { + $path = rtrim($path, '/'); + + if (empty($path) or substr($path, 0, 1) !== '/') { + $path = '/'; + } + + return $path; + } +} diff --git a/inc/mailgun/php-http/message/src/CookieJar.php b/inc/mailgun/php-http/message/src/CookieJar.php new file mode 100644 index 0000000..ab267d3 --- /dev/null +++ b/inc/mailgun/php-http/message/src/CookieJar.php @@ -0,0 +1,220 @@ +<?php + +namespace Http\Message; + +/** + * Cookie Jar holds a set of Cookies. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +final class CookieJar implements \Countable, \IteratorAggregate +{ + /** + * @var \SplObjectStorage + */ + protected $cookies; + + public function __construct() + { + $this->cookies = new \SplObjectStorage(); + } + + /** + * Checks if there is a cookie. + * + * @param Cookie $cookie + * + * @return bool + */ + public function hasCookie(Cookie $cookie) + { + return $this->cookies->contains($cookie); + } + + /** + * Adds a cookie. + * + * @param Cookie $cookie + */ + public function addCookie(Cookie $cookie) + { + if (!$this->hasCookie($cookie)) { + $cookies = $this->getMatchingCookies($cookie); + + foreach ($cookies as $matchingCookie) { + if ($cookie->getValue() !== $matchingCookie->getValue() || $cookie->getMaxAge() > $matchingCookie->getMaxAge()) { + $this->removeCookie($matchingCookie); + + continue; + } + } + + if ($cookie->hasValue()) { + $this->cookies->attach($cookie); + } + } + } + + /** + * Removes a cookie. + * + * @param Cookie $cookie + */ + public function removeCookie(Cookie $cookie) + { + $this->cookies->detach($cookie); + } + + /** + * Returns the cookies. + * + * @return Cookie[] + */ + public function getCookies() + { + $match = function ($matchCookie) { + return true; + }; + + return $this->findMatchingCookies($match); + } + + /** + * Returns all matching cookies. + * + * @param Cookie $cookie + * + * @return Cookie[] + */ + public function getMatchingCookies(Cookie $cookie) + { + $match = function ($matchCookie) use ($cookie) { + return $matchCookie->match($cookie); + }; + + return $this->findMatchingCookies($match); + } + + /** + * Finds matching cookies based on a callable. + * + * @param callable $match + * + * @return Cookie[] + */ + protected function findMatchingCookies(callable $match) + { + $cookies = []; + + foreach ($this->cookies as $cookie) { + if ($match($cookie)) { + $cookies[] = $cookie; + } + } + + return $cookies; + } + + /** + * Checks if there are cookies. + * + * @return bool + */ + public function hasCookies() + { + return $this->cookies->count() > 0; + } + + /** + * Sets the cookies and removes any previous one. + * + * @param Cookie[] $cookies + */ + public function setCookies(array $cookies) + { + $this->clear(); + $this->addCookies($cookies); + } + + /** + * Adds some cookies. + * + * @param Cookie[] $cookies + */ + public function addCookies(array $cookies) + { + foreach ($cookies as $cookie) { + $this->addCookie($cookie); + } + } + + /** + * Removes some cookies. + * + * @param Cookie[] $cookies + */ + public function removeCookies(array $cookies) + { + foreach ($cookies as $cookie) { + $this->removeCookie($cookie); + } + } + + /** + * Removes cookies which match the given parameters. + * + * Null means that parameter should not be matched + * + * @param string|null $name + * @param string|null $domain + * @param string|null $path + */ + public function removeMatchingCookies($name = null, $domain = null, $path = null) + { + $match = function ($cookie) use ($name, $domain, $path) { + $match = true; + + if (isset($name)) { + $match = $match && ($cookie->getName() === $name); + } + + if (isset($domain)) { + $match = $match && $cookie->matchDomain($domain); + } + + if (isset($path)) { + $match = $match && $cookie->matchPath($path); + } + + return $match; + }; + + $cookies = $this->findMatchingCookies($match); + + $this->removeCookies($cookies); + } + + /** + * Removes all cookies. + */ + public function clear() + { + $this->cookies = new \SplObjectStorage(); + } + + /** + * {@inheritdoc} + */ + public function count() + { + return $this->cookies->count(); + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + return clone $this->cookies; + } +} diff --git a/inc/mailgun/php-http/message/src/Decorator/MessageDecorator.php b/inc/mailgun/php-http/message/src/Decorator/MessageDecorator.php new file mode 100644 index 0000000..0ffc7ca --- /dev/null +++ b/inc/mailgun/php-http/message/src/Decorator/MessageDecorator.php @@ -0,0 +1,133 @@ +<?php + +namespace Http\Message\Decorator; + +use Psr\Http\Message\MessageInterface; +use Psr\Http\Message\StreamInterface; + +/** + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +trait MessageDecorator +{ + /** + * @var MessageInterface + */ + private $message; + + /** + * Returns the decorated message. + * + * Since the underlying Message is immutable as well + * exposing it is not an issue, because it's state cannot be altered + * + * @return MessageInterface + */ + public function getMessage() + { + return $this->message; + } + + /** + * {@inheritdoc} + */ + public function getProtocolVersion() + { + return $this->message->getProtocolVersion(); + } + + /** + * {@inheritdoc} + */ + public function withProtocolVersion($version) + { + $new = clone $this; + $new->message = $this->message->withProtocolVersion($version); + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getHeaders() + { + return $this->message->getHeaders(); + } + + /** + * {@inheritdoc} + */ + public function hasHeader($header) + { + return $this->message->hasHeader($header); + } + + /** + * {@inheritdoc} + */ + public function getHeader($header) + { + return $this->message->getHeader($header); + } + + /** + * {@inheritdoc} + */ + public function getHeaderLine($header) + { + return $this->message->getHeaderLine($header); + } + + /** + * {@inheritdoc} + */ + public function withHeader($header, $value) + { + $new = clone $this; + $new->message = $this->message->withHeader($header, $value); + + return $new; + } + + /** + * {@inheritdoc} + */ + public function withAddedHeader($header, $value) + { + $new = clone $this; + $new->message = $this->message->withAddedHeader($header, $value); + + return $new; + } + + /** + * {@inheritdoc} + */ + public function withoutHeader($header) + { + $new = clone $this; + $new->message = $this->message->withoutHeader($header); + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getBody() + { + return $this->message->getBody(); + } + + /** + * {@inheritdoc} + */ + public function withBody(StreamInterface $body) + { + $new = clone $this; + $new->message = $this->message->withBody($body); + + return $new; + } +} diff --git a/inc/mailgun/php-http/message/src/Decorator/RequestDecorator.php b/inc/mailgun/php-http/message/src/Decorator/RequestDecorator.php new file mode 100644 index 0000000..7c50e58 --- /dev/null +++ b/inc/mailgun/php-http/message/src/Decorator/RequestDecorator.php @@ -0,0 +1,88 @@ +<?php + +namespace Http\Message\Decorator; + +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\UriInterface; + +/** + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +trait RequestDecorator +{ + use MessageDecorator { + getMessage as getRequest; + } + + /** + * Exchanges the underlying request with another. + * + * @param RequestInterface $request + * + * @return self + */ + public function withRequest(RequestInterface $request) + { + $new = clone $this; + $new->message = $request; + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getRequestTarget() + { + return $this->message->getRequestTarget(); + } + + /** + * {@inheritdoc} + */ + public function withRequestTarget($requestTarget) + { + $new = clone $this; + $new->message = $this->message->withRequestTarget($requestTarget); + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getMethod() + { + return $this->message->getMethod(); + } + + /** + * {@inheritdoc} + */ + public function withMethod($method) + { + $new = clone $this; + $new->message = $this->message->withMethod($method); + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getUri() + { + return $this->message->getUri(); + } + + /** + * {@inheritdoc} + */ + public function withUri(UriInterface $uri, $preserveHost = false) + { + $new = clone $this; + $new->message = $this->message->withUri($uri, $preserveHost); + + return $new; + } +} diff --git a/inc/mailgun/php-http/message/src/Decorator/ResponseDecorator.php b/inc/mailgun/php-http/message/src/Decorator/ResponseDecorator.php new file mode 100644 index 0000000..82d9ae0 --- /dev/null +++ b/inc/mailgun/php-http/message/src/Decorator/ResponseDecorator.php @@ -0,0 +1,57 @@ +<?php + +namespace Http\Message\Decorator; + +use Psr\Http\Message\ResponseInterface; + +/** + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +trait ResponseDecorator +{ + use MessageDecorator { + getMessage as getResponse; + } + + /** + * Exchanges the underlying response with another. + * + * @param ResponseInterface $response + * + * @return self + */ + public function withResponse(ResponseInterface $response) + { + $new = clone $this; + $new->message = $response; + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getStatusCode() + { + return $this->message->getStatusCode(); + } + + /** + * {@inheritdoc} + */ + public function withStatus($code, $reasonPhrase = '') + { + $new = clone $this; + $new->message = $this->message->withStatus($code, $reasonPhrase); + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getReasonPhrase() + { + return $this->message->getReasonPhrase(); + } +} diff --git a/inc/mailgun/php-http/message/src/Decorator/StreamDecorator.php b/inc/mailgun/php-http/message/src/Decorator/StreamDecorator.php new file mode 100644 index 0000000..f405c7a --- /dev/null +++ b/inc/mailgun/php-http/message/src/Decorator/StreamDecorator.php @@ -0,0 +1,138 @@ +<?php + +namespace Http\Message\Decorator; + +use Psr\Http\Message\StreamInterface; + +/** + * Decorates a stream. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +trait StreamDecorator +{ + /** + * @var StreamInterface + */ + protected $stream; + + /** + * {@inheritdoc} + */ + public function __toString() + { + return $this->stream->__toString(); + } + + /** + * {@inheritdoc} + */ + public function close() + { + $this->stream->close(); + } + + /** + * {@inheritdoc} + */ + public function detach() + { + return $this->stream->detach(); + } + + /** + * {@inheritdoc} + */ + public function getSize() + { + return $this->stream->getSize(); + } + + /** + * {@inheritdoc} + */ + public function tell() + { + return $this->stream->tell(); + } + + /** + * {@inheritdoc} + */ + public function eof() + { + return $this->stream->eof(); + } + + /** + * {@inheritdoc} + */ + public function isSeekable() + { + return $this->stream->isSeekable(); + } + + /** + * {@inheritdoc} + */ + public function seek($offset, $whence = SEEK_SET) + { + $this->stream->seek($offset, $whence); + } + + /** + * {@inheritdoc} + */ + public function rewind() + { + $this->stream->rewind(); + } + + /** + * {@inheritdoc} + */ + public function isWritable() + { + return $this->stream->isWritable(); + } + + /** + * {@inheritdoc} + */ + public function write($string) + { + return $this->stream->write($string); + } + + /** + * {@inheritdoc} + */ + public function isReadable() + { + return $this->stream->isReadable(); + } + + /** + * {@inheritdoc} + */ + public function read($length) + { + return $this->stream->read($length); + } + + /** + * {@inheritdoc} + */ + public function getContents() + { + return $this->stream->getContents(); + } + + /** + * {@inheritdoc} + */ + public function getMetadata($key = null) + { + return $this->stream->getMetadata($key); + } +} diff --git a/inc/mailgun/php-http/message/src/Encoding/ChunkStream.php b/inc/mailgun/php-http/message/src/Encoding/ChunkStream.php new file mode 100644 index 0000000..74c2fbd --- /dev/null +++ b/inc/mailgun/php-http/message/src/Encoding/ChunkStream.php @@ -0,0 +1,39 @@ +<?php + +namespace Http\Message\Encoding; + +/** + * Transform a regular stream into a chunked one. + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +class ChunkStream extends FilteredStream +{ + /** + * {@inheritdoc} + */ + protected function readFilter() + { + return 'chunk'; + } + + /** + * {@inheritdoc} + */ + protected function writeFilter() + { + return 'dechunk'; + } + + /** + * {@inheritdoc} + */ + protected function fill() + { + parent::fill(); + + if ($this->stream->eof()) { + $this->buffer .= "0\r\n\r\n"; + } + } +} diff --git a/inc/mailgun/php-http/message/src/Encoding/CompressStream.php b/inc/mailgun/php-http/message/src/Encoding/CompressStream.php new file mode 100644 index 0000000..d1013dc --- /dev/null +++ b/inc/mailgun/php-http/message/src/Encoding/CompressStream.php @@ -0,0 +1,42 @@ +<?php + +namespace Http\Message\Encoding; + +use Psr\Http\Message\StreamInterface; + +/** + * Stream compress (RFC 1950). + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +class CompressStream extends FilteredStream +{ + /** + * @param StreamInterface $stream + * @param int $level + */ + public function __construct(StreamInterface $stream, $level = -1) + { + if (!extension_loaded('zlib')) { + throw new \RuntimeException('The zlib extension must be enabled to use this stream'); + } + + parent::__construct($stream, ['window' => 15, 'level' => $level], ['window' => 15]); + } + + /** + * {@inheritdoc} + */ + protected function readFilter() + { + return 'zlib.deflate'; + } + + /** + * {@inheritdoc} + */ + protected function writeFilter() + { + return 'zlib.inflate'; + } +} diff --git a/inc/mailgun/php-http/message/src/Encoding/DechunkStream.php b/inc/mailgun/php-http/message/src/Encoding/DechunkStream.php new file mode 100644 index 0000000..4cade83 --- /dev/null +++ b/inc/mailgun/php-http/message/src/Encoding/DechunkStream.php @@ -0,0 +1,29 @@ +<?php + +namespace Http\Message\Encoding; + +/** + * Decorate a stream which is chunked. + * + * Allow to decode a chunked stream + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +class DechunkStream extends FilteredStream +{ + /** + * {@inheritdoc} + */ + protected function readFilter() + { + return 'dechunk'; + } + + /** + * {@inheritdoc} + */ + protected function writeFilter() + { + return 'chunk'; + } +} diff --git a/inc/mailgun/php-http/message/src/Encoding/DecompressStream.php b/inc/mailgun/php-http/message/src/Encoding/DecompressStream.php new file mode 100644 index 0000000..4e3a723 --- /dev/null +++ b/inc/mailgun/php-http/message/src/Encoding/DecompressStream.php @@ -0,0 +1,42 @@ +<?php + +namespace Http\Message\Encoding; + +use Psr\Http\Message\StreamInterface; + +/** + * Stream decompress (RFC 1950). + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +class DecompressStream extends FilteredStream +{ + /** + * @param StreamInterface $stream + * @param int $level + */ + public function __construct(StreamInterface $stream, $level = -1) + { + if (!extension_loaded('zlib')) { + throw new \RuntimeException('The zlib extension must be enabled to use this stream'); + } + + parent::__construct($stream, ['window' => 15], ['window' => 15, 'level' => $level]); + } + + /** + * {@inheritdoc} + */ + protected function readFilter() + { + return 'zlib.inflate'; + } + + /** + * {@inheritdoc} + */ + protected function writeFilter() + { + return 'zlib.deflate'; + } +} diff --git a/inc/mailgun/php-http/message/src/Encoding/DeflateStream.php b/inc/mailgun/php-http/message/src/Encoding/DeflateStream.php new file mode 100644 index 0000000..1d7344b --- /dev/null +++ b/inc/mailgun/php-http/message/src/Encoding/DeflateStream.php @@ -0,0 +1,38 @@ +<?php + +namespace Http\Message\Encoding; + +use Psr\Http\Message\StreamInterface; + +/** + * Stream deflate (RFC 1951). + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +class DeflateStream extends FilteredStream +{ + /** + * @param StreamInterface $stream + * @param int $level + */ + public function __construct(StreamInterface $stream, $level = -1) + { + parent::__construct($stream, ['window' => -15, 'level' => $level], ['window' => -15]); + } + + /** + * {@inheritdoc} + */ + protected function readFilter() + { + return 'zlib.deflate'; + } + + /** + * {@inheritdoc} + */ + protected function writeFilter() + { + return 'zlib.inflate'; + } +} diff --git a/inc/mailgun/php-http/message/src/Encoding/Filter/Chunk.php b/inc/mailgun/php-http/message/src/Encoding/Filter/Chunk.php new file mode 100644 index 0000000..0f8f53b --- /dev/null +++ b/inc/mailgun/php-http/message/src/Encoding/Filter/Chunk.php @@ -0,0 +1,30 @@ +<?php + +namespace Http\Message\Encoding\Filter; + +/** + * Userland implementation of the chunk stream filter. + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +class Chunk extends \php_user_filter +{ + /** + * {@inheritdoc} + */ + public function filter($in, $out, &$consumed, $closing) + { + while ($bucket = stream_bucket_make_writeable($in)) { + $lenbucket = stream_bucket_new($this->stream, dechex($bucket->datalen)."\r\n"); + stream_bucket_append($out, $lenbucket); + + $consumed += $bucket->datalen; + stream_bucket_append($out, $bucket); + + $lenbucket = stream_bucket_new($this->stream, "\r\n"); + stream_bucket_append($out, $lenbucket); + } + + return PSFS_PASS_ON; + } +} diff --git a/inc/mailgun/php-http/message/src/Encoding/FilteredStream.php b/inc/mailgun/php-http/message/src/Encoding/FilteredStream.php new file mode 100644 index 0000000..a32554b --- /dev/null +++ b/inc/mailgun/php-http/message/src/Encoding/FilteredStream.php @@ -0,0 +1,198 @@ +<?php + +namespace Http\Message\Encoding; + +use Clue\StreamFilter as Filter; +use Http\Message\Decorator\StreamDecorator; +use Psr\Http\Message\StreamInterface; + +/** + * A filtered stream has a filter for filtering output and a filter for filtering input made to a underlying stream. + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +abstract class FilteredStream implements StreamInterface +{ + const BUFFER_SIZE = 8192; + + use StreamDecorator; + + /** + * @var callable + */ + protected $readFilterCallback; + + /** + * @var resource + * + * @deprecated since version 1.5, will be removed in 2.0 + */ + protected $readFilter; + + /** + * @var callable + * + * @deprecated since version 1.5, will be removed in 2.0 + */ + protected $writeFilterCallback; + + /** + * @var resource + * + * @deprecated since version 1.5, will be removed in 2.0 + */ + protected $writeFilter; + + /** + * Internal buffer. + * + * @var string + */ + protected $buffer = ''; + + /** + * @param StreamInterface $stream + * @param mixed|null $readFilterOptions + * @param mixed|null $writeFilterOptions deprecated since 1.5, will be removed in 2.0 + */ + public function __construct(StreamInterface $stream, $readFilterOptions = null, $writeFilterOptions = null) + { + $this->readFilterCallback = Filter\fun($this->readFilter(), $readFilterOptions); + $this->writeFilterCallback = Filter\fun($this->writeFilter(), $writeFilterOptions); + + if (null !== $writeFilterOptions) { + @trigger_error('The $writeFilterOptions argument is deprecated since version 1.5 and will be removed in 2.0.', E_USER_DEPRECATED); + } + + $this->stream = $stream; + } + + /** + * {@inheritdoc} + */ + public function read($length) + { + if (strlen($this->buffer) >= $length) { + $read = substr($this->buffer, 0, $length); + $this->buffer = substr($this->buffer, $length); + + return $read; + } + + if ($this->stream->eof()) { + $buffer = $this->buffer; + $this->buffer = ''; + + return $buffer; + } + + $read = $this->buffer; + $this->buffer = ''; + $this->fill(); + + return $read.$this->read($length - strlen($read)); + } + + /** + * {@inheritdoc} + */ + public function eof() + { + return $this->stream->eof() && $this->buffer === ''; + } + + /** + * Buffer is filled by reading underlying stream. + * + * Callback is reading once more even if the stream is ended. + * This allow to get last data in the PHP buffer otherwise this + * bug is present : https://bugs.php.net/bug.php?id=48725 + */ + protected function fill() + { + $readFilterCallback = $this->readFilterCallback; + $this->buffer .= $readFilterCallback($this->stream->read(self::BUFFER_SIZE)); + + if ($this->stream->eof()) { + $this->buffer .= $readFilterCallback(); + } + } + + /** + * {@inheritdoc} + */ + public function getContents() + { + $buffer = ''; + + while (!$this->eof()) { + $buf = $this->read(self::BUFFER_SIZE); + // Using a loose equality here to match on '' and false. + if ($buf == null) { + break; + } + + $buffer .= $buf; + } + + return $buffer; + } + + /** + * {@inheritdoc} + */ + public function getSize() + { + return; + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return $this->getContents(); + } + + /** + * Returns the read filter name. + * + * @return string + * + * @deprecated since version 1.5, will be removed in 2.0 + */ + public function getReadFilter() + { + @trigger_error('The '.__CLASS__.'::'.__METHOD__.' method is deprecated since version 1.5 and will be removed in 2.0.', E_USER_DEPRECATED); + + return $this->readFilter(); + } + + /** + * Returns the write filter name. + * + * @return string + */ + abstract protected function readFilter(); + + /** + * Returns the write filter name. + * + * @return string + * + * @deprecated since version 1.5, will be removed in 2.0 + */ + public function getWriteFilter() + { + @trigger_error('The '.__CLASS__.'::'.__METHOD__.' method is deprecated since version 1.5 and will be removed in 2.0.', E_USER_DEPRECATED); + + return $this->writeFilter(); + } + + /** + * Returns the write filter name. + * + * @return string + */ + abstract protected function writeFilter(); +} diff --git a/inc/mailgun/php-http/message/src/Encoding/GzipDecodeStream.php b/inc/mailgun/php-http/message/src/Encoding/GzipDecodeStream.php new file mode 100644 index 0000000..4f958ed --- /dev/null +++ b/inc/mailgun/php-http/message/src/Encoding/GzipDecodeStream.php @@ -0,0 +1,42 @@ +<?php + +namespace Http\Message\Encoding; + +use Psr\Http\Message\StreamInterface; + +/** + * Stream for decoding from gzip format (RFC 1952). + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +class GzipDecodeStream extends FilteredStream +{ + /** + * @param StreamInterface $stream + * @param int $level + */ + public function __construct(StreamInterface $stream, $level = -1) + { + if (!extension_loaded('zlib')) { + throw new \RuntimeException('The zlib extension must be enabled to use this stream'); + } + + parent::__construct($stream, ['window' => 31], ['window' => 31, 'level' => $level]); + } + + /** + * {@inheritdoc} + */ + protected function readFilter() + { + return 'zlib.inflate'; + } + + /** + * {@inheritdoc} + */ + protected function writeFilter() + { + return 'zlib.deflate'; + } +} diff --git a/inc/mailgun/php-http/message/src/Encoding/GzipEncodeStream.php b/inc/mailgun/php-http/message/src/Encoding/GzipEncodeStream.php new file mode 100644 index 0000000..1066eec --- /dev/null +++ b/inc/mailgun/php-http/message/src/Encoding/GzipEncodeStream.php @@ -0,0 +1,42 @@ +<?php + +namespace Http\Message\Encoding; + +use Psr\Http\Message\StreamInterface; + +/** + * Stream for encoding to gzip format (RFC 1952). + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +class GzipEncodeStream extends FilteredStream +{ + /** + * @param StreamInterface $stream + * @param int $level + */ + public function __construct(StreamInterface $stream, $level = -1) + { + if (!extension_loaded('zlib')) { + throw new \RuntimeException('The zlib extension must be enabled to use this stream'); + } + + parent::__construct($stream, ['window' => 31, 'level' => $level], ['window' => 31]); + } + + /** + * {@inheritdoc} + */ + protected function readFilter() + { + return 'zlib.deflate'; + } + + /** + * {@inheritdoc} + */ + protected function writeFilter() + { + return 'zlib.inflate'; + } +} diff --git a/inc/mailgun/php-http/message/src/Encoding/InflateStream.php b/inc/mailgun/php-http/message/src/Encoding/InflateStream.php new file mode 100644 index 0000000..7070230 --- /dev/null +++ b/inc/mailgun/php-http/message/src/Encoding/InflateStream.php @@ -0,0 +1,42 @@ +<?php + +namespace Http\Message\Encoding; + +use Psr\Http\Message\StreamInterface; + +/** + * Stream inflate (RFC 1951). + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +class InflateStream extends FilteredStream +{ + /** + * @param StreamInterface $stream + * @param int $level + */ + public function __construct(StreamInterface $stream, $level = -1) + { + if (!extension_loaded('zlib')) { + throw new \RuntimeException('The zlib extension must be enabled to use this stream'); + } + + parent::__construct($stream, ['window' => -15], ['window' => -15, 'level' => $level]); + } + + /** + * {@inheritdoc} + */ + protected function readFilter() + { + return 'zlib.inflate'; + } + + /** + * {@inheritdoc} + */ + protected function writeFilter() + { + return 'zlib.deflate'; + } +} diff --git a/inc/mailgun/php-http/message/src/Formatter.php b/inc/mailgun/php-http/message/src/Formatter.php new file mode 100644 index 0000000..d04d2c3 --- /dev/null +++ b/inc/mailgun/php-http/message/src/Formatter.php @@ -0,0 +1,32 @@ +<?php + +namespace Http\Message; + +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * Formats a request and/or a response as a string. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +interface Formatter +{ + /** + * Formats a request. + * + * @param RequestInterface $request + * + * @return string + */ + public function formatRequest(RequestInterface $request); + + /** + * Formats a response. + * + * @param ResponseInterface $response + * + * @return string + */ + public function formatResponse(ResponseInterface $response); +} diff --git a/inc/mailgun/php-http/message/src/Formatter/CurlCommandFormatter.php b/inc/mailgun/php-http/message/src/Formatter/CurlCommandFormatter.php new file mode 100644 index 0000000..5364ccc --- /dev/null +++ b/inc/mailgun/php-http/message/src/Formatter/CurlCommandFormatter.php @@ -0,0 +1,80 @@ +<?php + +namespace Http\Message\Formatter; + +use Http\Message\Formatter; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * A formatter that prints a cURL command for HTTP requests. + * + * @author Tobias Nyholm <tobias.nyholm@gmail.com> + */ +class CurlCommandFormatter implements Formatter +{ + /** + * {@inheritdoc} + */ + public function formatRequest(RequestInterface $request) + { + $command = sprintf('curl %s', escapeshellarg((string) $request->getUri()->withFragment(''))); + if ($request->getProtocolVersion() === '1.0') { + $command .= ' --http1.0'; + } elseif ($request->getProtocolVersion() === '2.0') { + $command .= ' --http2'; + } + + $method = strtoupper($request->getMethod()); + if ('HEAD' === $method) { + $command .= ' --head'; + } elseif ('GET' !== $method) { + $command .= ' --request '.$method; + } + + $command .= $this->getHeadersAsCommandOptions($request); + + $body = $request->getBody(); + if ($body->getSize() > 0) { + if (!$body->isSeekable()) { + return 'Cant format Request as cUrl command if body stream is not seekable.'; + } + $command .= sprintf(' --data %s', escapeshellarg($body->__toString())); + $body->rewind(); + } + + return $command; + } + + /** + * {@inheritdoc} + */ + public function formatResponse(ResponseInterface $response) + { + return ''; + } + + /** + * @param RequestInterface $request + * + * @return string + */ + private function getHeadersAsCommandOptions(RequestInterface $request) + { + $command = ''; + foreach ($request->getHeaders() as $name => $values) { + if ('host' === strtolower($name) && $values[0] === $request->getUri()->getHost()) { + continue; + } + + if ('user-agent' === strtolower($name)) { + $command .= sprintf('-A %s', escapeshellarg($values[0])); + continue; + } + + $command .= sprintf(' -H %s', escapeshellarg($name.': '.$request->getHeaderLine($name))); + } + + return $command; + } +} diff --git a/inc/mailgun/php-http/message/src/Formatter/FullHttpMessageFormatter.php b/inc/mailgun/php-http/message/src/Formatter/FullHttpMessageFormatter.php new file mode 100644 index 0000000..3fa1029 --- /dev/null +++ b/inc/mailgun/php-http/message/src/Formatter/FullHttpMessageFormatter.php @@ -0,0 +1,91 @@ +<?php + +namespace Http\Message\Formatter; + +use Http\Message\Formatter; +use Psr\Http\Message\MessageInterface; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * A formatter that prints the complete HTTP message. + * + * @author Tobias Nyholm <tobias.nyholm@gmail.com> + */ +class FullHttpMessageFormatter implements Formatter +{ + /** + * The maximum length of the body. + * + * @var int + */ + private $maxBodyLength; + + /** + * @param int $maxBodyLength + */ + public function __construct($maxBodyLength = 1000) + { + $this->maxBodyLength = $maxBodyLength; + } + + /** + * {@inheritdoc} + */ + public function formatRequest(RequestInterface $request) + { + $message = sprintf( + "%s %s HTTP/%s\n", + $request->getMethod(), + $request->getRequestTarget(), + $request->getProtocolVersion() + ); + + foreach ($request->getHeaders() as $name => $values) { + $message .= $name.': '.implode(', ', $values)."\n"; + } + + return $this->addBody($request, $message); + } + + /** + * {@inheritdoc} + */ + public function formatResponse(ResponseInterface $response) + { + $message = sprintf( + "HTTP/%s %s %s\n", + $response->getProtocolVersion(), + $response->getStatusCode(), + $response->getReasonPhrase() + ); + + foreach ($response->getHeaders() as $name => $values) { + $message .= $name.': '.implode(', ', $values)."\n"; + } + + return $this->addBody($response, $message); + } + + /** + * Add the message body if the stream is seekable. + * + * @param MessageInterface $request + * @param string $message + * + * @return string + */ + private function addBody(MessageInterface $request, $message) + { + $stream = $request->getBody(); + if (!$stream->isSeekable() || $this->maxBodyLength === 0) { + // Do not read the stream + $message .= "\n"; + } else { + $message .= "\n".mb_substr($stream->__toString(), 0, $this->maxBodyLength); + $stream->rewind(); + } + + return $message; + } +} diff --git a/inc/mailgun/php-http/message/src/Formatter/SimpleFormatter.php b/inc/mailgun/php-http/message/src/Formatter/SimpleFormatter.php new file mode 100644 index 0000000..b1fcabd --- /dev/null +++ b/inc/mailgun/php-http/message/src/Formatter/SimpleFormatter.php @@ -0,0 +1,42 @@ +<?php + +namespace Http\Message\Formatter; + +use Http\Message\Formatter; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * Normalize a request or a response into a string or an array. + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +class SimpleFormatter implements Formatter +{ + /** + * {@inheritdoc} + */ + public function formatRequest(RequestInterface $request) + { + return sprintf( + '%s %s %s', + $request->getMethod(), + $request->getUri()->__toString(), + $request->getProtocolVersion() + ); + } + + /** + * {@inheritdoc} + */ + public function formatResponse(ResponseInterface $response) + { + return sprintf( + '%s %s %s', + $response->getStatusCode(), + $response->getReasonPhrase(), + $response->getProtocolVersion() + ); + } +} diff --git a/inc/mailgun/php-http/message/src/MessageFactory/DiactorosMessageFactory.php b/inc/mailgun/php-http/message/src/MessageFactory/DiactorosMessageFactory.php new file mode 100644 index 0000000..53f08ae --- /dev/null +++ b/inc/mailgun/php-http/message/src/MessageFactory/DiactorosMessageFactory.php @@ -0,0 +1,61 @@ +<?php + +namespace Http\Message\MessageFactory; + +use Http\Message\StreamFactory\DiactorosStreamFactory; +use Http\Message\MessageFactory; +use Zend\Diactoros\Request; +use Zend\Diactoros\Response; + +/** + * Creates Diactoros messages. + * + * @author GeLo <geloen.eric@gmail.com> + */ +final class DiactorosMessageFactory implements MessageFactory +{ + /** + * @var DiactorosStreamFactory + */ + private $streamFactory; + + public function __construct() + { + $this->streamFactory = new DiactorosStreamFactory(); + } + + /** + * {@inheritdoc} + */ + public function createRequest( + $method, + $uri, + array $headers = [], + $body = null, + $protocolVersion = '1.1' + ) { + return (new Request( + $uri, + $method, + $this->streamFactory->createStream($body), + $headers + ))->withProtocolVersion($protocolVersion); + } + + /** + * {@inheritdoc} + */ + public function createResponse( + $statusCode = 200, + $reasonPhrase = null, + array $headers = [], + $body = null, + $protocolVersion = '1.1' + ) { + return (new Response( + $this->streamFactory->createStream($body), + $statusCode, + $headers + ))->withProtocolVersion($protocolVersion); + } +} diff --git a/inc/mailgun/php-http/message/src/MessageFactory/GuzzleMessageFactory.php b/inc/mailgun/php-http/message/src/MessageFactory/GuzzleMessageFactory.php new file mode 100644 index 0000000..59eb655 --- /dev/null +++ b/inc/mailgun/php-http/message/src/MessageFactory/GuzzleMessageFactory.php @@ -0,0 +1,53 @@ +<?php + +namespace Http\Message\MessageFactory; + +use GuzzleHttp\Psr7\Request; +use GuzzleHttp\Psr7\Response; +use Http\Message\MessageFactory; + +/** + * Creates Guzzle messages. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +final class GuzzleMessageFactory implements MessageFactory +{ + /** + * {@inheritdoc} + */ + public function createRequest( + $method, + $uri, + array $headers = [], + $body = null, + $protocolVersion = '1.1' + ) { + return new Request( + $method, + $uri, + $headers, + $body, + $protocolVersion + ); + } + + /** + * {@inheritdoc} + */ + public function createResponse( + $statusCode = 200, + $reasonPhrase = null, + array $headers = [], + $body = null, + $protocolVersion = '1.1' + ) { + return new Response( + $statusCode, + $headers, + $body, + $protocolVersion, + $reasonPhrase + ); + } +} diff --git a/inc/mailgun/php-http/message/src/MessageFactory/SlimMessageFactory.php b/inc/mailgun/php-http/message/src/MessageFactory/SlimMessageFactory.php new file mode 100644 index 0000000..cdad2ec --- /dev/null +++ b/inc/mailgun/php-http/message/src/MessageFactory/SlimMessageFactory.php @@ -0,0 +1,72 @@ +<?php + +namespace Http\Message\MessageFactory; + +use Http\Message\StreamFactory\SlimStreamFactory; +use Http\Message\UriFactory\SlimUriFactory; +use Http\Message\MessageFactory; +use Slim\Http\Request; +use Slim\Http\Response; +use Slim\Http\Headers; + +/** + * Creates Slim 3 messages. + * + * @author Mika Tuupola <tuupola@appelsiini.net> + */ +final class SlimMessageFactory implements MessageFactory +{ + /** + * @var SlimStreamFactory + */ + private $streamFactory; + + /** + * @var SlimUriFactory + */ + private $uriFactory; + + public function __construct() + { + $this->streamFactory = new SlimStreamFactory(); + $this->uriFactory = new SlimUriFactory(); + } + + /** + * {@inheritdoc} + */ + public function createRequest( + $method, + $uri, + array $headers = [], + $body = null, + $protocolVersion = '1.1' + ) { + return (new Request( + $method, + $this->uriFactory->createUri($uri), + new Headers($headers), + [], + [], + $this->streamFactory->createStream($body), + [] + ))->withProtocolVersion($protocolVersion); + } + + /** + * {@inheritdoc} + */ + public function createResponse( + $statusCode = 200, + $reasonPhrase = null, + array $headers = [], + $body = null, + $protocolVersion = '1.1' + ) { + return (new Response( + $statusCode, + new Headers($headers), + $this->streamFactory->createStream($body) + ))->withProtocolVersion($protocolVersion); + } +} diff --git a/inc/mailgun/php-http/message/src/RequestMatcher.php b/inc/mailgun/php-http/message/src/RequestMatcher.php new file mode 100644 index 0000000..94fe532 --- /dev/null +++ b/inc/mailgun/php-http/message/src/RequestMatcher.php @@ -0,0 +1,26 @@ +<?php + +namespace Http\Message; + +use Psr\Http\Message\RequestInterface; + +/** + * Match a request. + * + * PSR-7 equivalent of Symfony's RequestMatcher + * + * @see https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpFoundation/RequestMatcherInterface.php + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +interface RequestMatcher +{ + /** + * Decides whether the rule(s) implemented by the strategy matches the supplied request. + * + * @param RequestInterface $request The PSR7 request to check for a match + * + * @return bool true if the request matches, false otherwise + */ + public function matches(RequestInterface $request); +} diff --git a/inc/mailgun/php-http/message/src/RequestMatcher/CallbackRequestMatcher.php b/inc/mailgun/php-http/message/src/RequestMatcher/CallbackRequestMatcher.php new file mode 100644 index 0000000..4d45e32 --- /dev/null +++ b/inc/mailgun/php-http/message/src/RequestMatcher/CallbackRequestMatcher.php @@ -0,0 +1,35 @@ +<?php + +namespace Http\Message\RequestMatcher; + +use Http\Message\RequestMatcher; +use Psr\Http\Message\RequestInterface; + +/** + * Match a request with a callback. + * + * @author Márk Sági-Kazár <mark.sagikazar@gmail.com> + */ +final class CallbackRequestMatcher implements RequestMatcher +{ + /** + * @var callable + */ + private $callback; + + /** + * @param callable $callback + */ + public function __construct(callable $callback) + { + $this->callback = $callback; + } + + /** + * {@inheritdoc} + */ + public function matches(RequestInterface $request) + { + return (bool) call_user_func($this->callback, $request); + } +} diff --git a/inc/mailgun/php-http/message/src/RequestMatcher/RegexRequestMatcher.php b/inc/mailgun/php-http/message/src/RequestMatcher/RegexRequestMatcher.php new file mode 100644 index 0000000..91f3729 --- /dev/null +++ b/inc/mailgun/php-http/message/src/RequestMatcher/RegexRequestMatcher.php @@ -0,0 +1,41 @@ +<?php + +namespace Http\Message\RequestMatcher; + +use Http\Message\RequestMatcher; +use Psr\Http\Message\RequestInterface; + +@trigger_error('The '.__NAMESPACE__.'\RegexRequestMatcher class is deprecated since version 1.2 and will be removed in 2.0. Use Http\Message\RequestMatcher\RequestMatcher instead.', E_USER_DEPRECATED); + +/** + * Match a request with a regex on the uri. + * + * @author Joel Wurtz <joel.wurtz@gmail.com> + * + * @deprecated since version 1.2 and will be removed in 2.0. Use {@link RequestMatcher} instead. + */ +final class RegexRequestMatcher implements RequestMatcher +{ + /** + * Matching regex. + * + * @var string + */ + private $regex; + + /** + * @param string $regex + */ + public function __construct($regex) + { + $this->regex = $regex; + } + + /** + * {@inheritdoc} + */ + public function matches(RequestInterface $request) + { + return (bool) preg_match($this->regex, (string) $request->getUri()); + } +} diff --git a/inc/mailgun/php-http/message/src/RequestMatcher/RequestMatcher.php b/inc/mailgun/php-http/message/src/RequestMatcher/RequestMatcher.php new file mode 100644 index 0000000..e2aa021 --- /dev/null +++ b/inc/mailgun/php-http/message/src/RequestMatcher/RequestMatcher.php @@ -0,0 +1,78 @@ +<?php + +namespace Http\Message\RequestMatcher; + +use Http\Message\RequestMatcher as RequestMatcherInterface; +use Psr\Http\Message\RequestInterface; + +/** + * A port of the Symfony RequestMatcher for PSR-7. + * + * @author Fabien Potencier <fabien@symfony.com> + * @author Joel Wurtz <joel.wurtz@gmail.com> + */ +final class RequestMatcher implements RequestMatcherInterface +{ + /** + * @var string + */ + private $path; + + /** + * @var string + */ + private $host; + + /** + * @var array + */ + private $methods = []; + + /** + * @var string[] + */ + private $schemes = []; + + /** + * The regular expressions used for path or host must be specified without delimiter. + * You do not need to escape the forward slash / to match it. + * + * @param string|null $path Regular expression for the path + * @param string|null $host Regular expression for the hostname + * @param string|string[]|null $methods Method or list of methods to match + * @param string|string[]|null $schemes Scheme or list of schemes to match (e.g. http or https) + */ + public function __construct($path = null, $host = null, $methods = [], $schemes = []) + { + $this->path = $path; + $this->host = $host; + $this->methods = array_map('strtoupper', (array) $methods); + $this->schemes = array_map('strtolower', (array) $schemes); + } + + /** + * {@inheritdoc} + * + * @api + */ + public function matches(RequestInterface $request) + { + if ($this->schemes && !in_array($request->getUri()->getScheme(), $this->schemes)) { + return false; + } + + if ($this->methods && !in_array($request->getMethod(), $this->methods)) { + return false; + } + + if (null !== $this->path && !preg_match('{'.$this->path.'}', rawurldecode($request->getUri()->getPath()))) { + return false; + } + + if (null !== $this->host && !preg_match('{'.$this->host.'}i', $request->getUri()->getHost())) { + return false; + } + + return true; + } +} diff --git a/inc/mailgun/php-http/message/src/Stream/BufferedStream.php b/inc/mailgun/php-http/message/src/Stream/BufferedStream.php new file mode 100644 index 0000000..1eac974 --- /dev/null +++ b/inc/mailgun/php-http/message/src/Stream/BufferedStream.php @@ -0,0 +1,270 @@ +<?php + +namespace Http\Message\Stream; + +use Psr\Http\Message\StreamInterface; + +/** + * Decorator to make any stream seekable. + * + * Internally it buffers an existing StreamInterface into a php://temp resource (or memory). By default it will use + * 2 megabytes of memory before writing to a temporary disk file. + * + * Due to this, very large stream can suffer performance issue (i/o slowdown). + */ +class BufferedStream implements StreamInterface +{ + /** @var resource The buffered resource used to seek previous data */ + private $resource; + + /** @var int size of the stream if available */ + private $size; + + /** @var StreamInterface The underlying stream decorated by this class */ + private $stream; + + /** @var int How many bytes were written */ + private $written = 0; + + /** + * @param StreamInterface $stream Decorated stream + * @param bool $useFileBuffer Whether to use a file buffer (write to a file, if data exceed a certain size) + * by default, set this to false to only use memory + * @param int $memoryBuffer In conjunction with using file buffer, limit (in bytes) from which it begins to buffer + * the data in a file + */ + public function __construct(StreamInterface $stream, $useFileBuffer = true, $memoryBuffer = 2097152) + { + $this->stream = $stream; + $this->size = $stream->getSize(); + + if ($useFileBuffer) { + $this->resource = fopen('php://temp/maxmemory:'.$memoryBuffer, 'rw+'); + } else { + $this->resource = fopen('php://memory', 'rw+'); + } + + if (false === $this->resource) { + throw new \RuntimeException('Cannot create a resource over temp or memory implementation'); + } + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + try { + $this->rewind(); + + return $this->getContents(); + } catch (\Throwable $throwable) { + return ''; + } catch (\Exception $exception) { // Layer to be BC with PHP 5, remove this when we only support PHP 7+ + return ''; + } + } + + /** + * {@inheritdoc} + */ + public function close() + { + if (null === $this->resource) { + throw new \RuntimeException('Cannot close on a detached stream'); + } + + $this->stream->close(); + fclose($this->resource); + } + + /** + * {@inheritdoc} + */ + public function detach() + { + if (null === $this->resource) { + return; + } + + // Force reading the remaining data of the stream + $this->getContents(); + + $resource = $this->resource; + $this->stream->close(); + $this->stream = null; + $this->resource = null; + + return $resource; + } + + /** + * {@inheritdoc} + */ + public function getSize() + { + if (null === $this->resource) { + return; + } + + if (null === $this->size && $this->stream->eof()) { + return $this->written; + } + + return $this->size; + } + + /** + * {@inheritdoc} + */ + public function tell() + { + if (null === $this->resource) { + throw new \RuntimeException('Cannot tell on a detached stream'); + } + + return ftell($this->resource); + } + + /** + * {@inheritdoc} + */ + public function eof() + { + if (null === $this->resource) { + throw new \RuntimeException('Cannot call eof on a detached stream'); + } + + // We are at the end only when both our resource and underlying stream are at eof + return $this->stream->eof() && (ftell($this->resource) === $this->written); + } + + /** + * {@inheritdoc} + */ + public function isSeekable() + { + return null !== $this->resource; + } + + /** + * {@inheritdoc} + */ + public function seek($offset, $whence = SEEK_SET) + { + if (null === $this->resource) { + throw new \RuntimeException('Cannot seek on a detached stream'); + } + + fseek($this->resource, $offset, $whence); + } + + /** + * {@inheritdoc} + */ + public function rewind() + { + if (null === $this->resource) { + throw new \RuntimeException('Cannot rewind on a detached stream'); + } + + rewind($this->resource); + } + + /** + * {@inheritdoc} + */ + public function isWritable() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function write($string) + { + throw new \RuntimeException('Cannot write on this stream'); + } + + /** + * {@inheritdoc} + */ + public function isReadable() + { + return null !== $this->resource; + } + + /** + * {@inheritdoc} + */ + public function read($length) + { + if (null === $this->resource) { + throw new \RuntimeException('Cannot read on a detached stream'); + } + + $read = ''; + + // First read from the resource + if (ftell($this->resource) !== $this->written) { + $read = fread($this->resource, $length); + } + + $bytesRead = strlen($read); + + if ($bytesRead < $length) { + $streamRead = $this->stream->read($length - $bytesRead); + + // Write on the underlying stream what we read + $this->written += fwrite($this->resource, $streamRead); + $read .= $streamRead; + } + + return $read; + } + + /** + * {@inheritdoc} + */ + public function getContents() + { + if (null === $this->resource) { + throw new \RuntimeException('Cannot read on a detached stream'); + } + + $read = ''; + + while (!$this->eof()) { + $read .= $this->read(8192); + } + + return $read; + } + + /** + * {@inheritdoc} + */ + public function getMetadata($key = null) + { + if (null === $this->resource) { + if (null === $key) { + return []; + } + + return; + } + + $metadata = stream_get_meta_data($this->resource); + + if (null === $key) { + return $metadata; + } + + if (!array_key_exists($key, $metadata)) { + return; + } + + return $metadata[$key]; + } +} diff --git a/inc/mailgun/php-http/message/src/StreamFactory/DiactorosStreamFactory.php b/inc/mailgun/php-http/message/src/StreamFactory/DiactorosStreamFactory.php new file mode 100644 index 0000000..a75ec98 --- /dev/null +++ b/inc/mailgun/php-http/message/src/StreamFactory/DiactorosStreamFactory.php @@ -0,0 +1,36 @@ +<?php + +namespace Http\Message\StreamFactory; + +use Http\Message\StreamFactory; +use Psr\Http\Message\StreamInterface; +use Zend\Diactoros\Stream; + +/** + * Creates Diactoros streams. + * + * @author Михаил Красильников <m.krasilnikov@yandex.ru> + */ +final class DiactorosStreamFactory implements StreamFactory +{ + /** + * {@inheritdoc} + */ + public function createStream($body = null) + { + if ($body instanceof StreamInterface) { + return $body; + } + + if (is_resource($body)) { + return new Stream($body); + } + + $stream = new Stream('php://memory', 'rw'); + if (null !== $body && '' !== $body) { + $stream->write((string) $body); + } + + return $stream; + } +} diff --git a/inc/mailgun/php-http/message/src/StreamFactory/GuzzleStreamFactory.php b/inc/mailgun/php-http/message/src/StreamFactory/GuzzleStreamFactory.php new file mode 100644 index 0000000..10f4d3f --- /dev/null +++ b/inc/mailgun/php-http/message/src/StreamFactory/GuzzleStreamFactory.php @@ -0,0 +1,21 @@ +<?php + +namespace Http\Message\StreamFactory; + +use Http\Message\StreamFactory; + +/** + * Creates Guzzle streams. + * + * @author Михаил Красильников <m.krasilnikov@yandex.ru> + */ +final class GuzzleStreamFactory implements StreamFactory +{ + /** + * {@inheritdoc} + */ + public function createStream($body = null) + { + return \GuzzleHttp\Psr7\stream_for($body); + } +} diff --git a/inc/mailgun/php-http/message/src/StreamFactory/SlimStreamFactory.php b/inc/mailgun/php-http/message/src/StreamFactory/SlimStreamFactory.php new file mode 100644 index 0000000..efcadc4 --- /dev/null +++ b/inc/mailgun/php-http/message/src/StreamFactory/SlimStreamFactory.php @@ -0,0 +1,37 @@ +<?php + +namespace Http\Message\StreamFactory; + +use Http\Message\StreamFactory; +use Psr\Http\Message\StreamInterface; +use Slim\Http\Stream; + +/** + * Creates Slim 3 streams. + * + * @author Mika Tuupola <tuupola@appelsiini.net> + */ +final class SlimStreamFactory implements StreamFactory +{ + /** + * {@inheritdoc} + */ + public function createStream($body = null) + { + if ($body instanceof StreamInterface) { + return $body; + } + + if (is_resource($body)) { + return new Stream($body); + } + + $resource = fopen('php://memory', 'r+'); + $stream = new Stream($resource); + if (null !== $body && '' !== $body) { + $stream->write((string) $body); + } + + return $stream; + } +} diff --git a/inc/mailgun/php-http/message/src/UriFactory/DiactorosUriFactory.php b/inc/mailgun/php-http/message/src/UriFactory/DiactorosUriFactory.php new file mode 100644 index 0000000..268c361 --- /dev/null +++ b/inc/mailgun/php-http/message/src/UriFactory/DiactorosUriFactory.php @@ -0,0 +1,29 @@ +<?php + +namespace Http\Message\UriFactory; + +use Http\Message\UriFactory; +use Psr\Http\Message\UriInterface; +use Zend\Diactoros\Uri; + +/** + * Creates Diactoros URI. + * + * @author David de Boer <david@ddeboer.nl> + */ +final class DiactorosUriFactory implements UriFactory +{ + /** + * {@inheritdoc} + */ + public function createUri($uri) + { + if ($uri instanceof UriInterface) { + return $uri; + } elseif (is_string($uri)) { + return new Uri($uri); + } + + throw new \InvalidArgumentException('URI must be a string or UriInterface'); + } +} diff --git a/inc/mailgun/php-http/message/src/UriFactory/GuzzleUriFactory.php b/inc/mailgun/php-http/message/src/UriFactory/GuzzleUriFactory.php new file mode 100644 index 0000000..4c1c286 --- /dev/null +++ b/inc/mailgun/php-http/message/src/UriFactory/GuzzleUriFactory.php @@ -0,0 +1,22 @@ +<?php + +namespace Http\Message\UriFactory; + +use GuzzleHttp\Psr7; +use Http\Message\UriFactory; + +/** + * Creates Guzzle URI. + * + * @author David de Boer <david@ddeboer.nl> + */ +final class GuzzleUriFactory implements UriFactory +{ + /** + * {@inheritdoc} + */ + public function createUri($uri) + { + return Psr7\uri_for($uri); + } +} diff --git a/inc/mailgun/php-http/message/src/UriFactory/SlimUriFactory.php b/inc/mailgun/php-http/message/src/UriFactory/SlimUriFactory.php new file mode 100644 index 0000000..c013d54 --- /dev/null +++ b/inc/mailgun/php-http/message/src/UriFactory/SlimUriFactory.php @@ -0,0 +1,31 @@ +<?php + +namespace Http\Message\UriFactory; + +use Http\Message\UriFactory; +use Psr\Http\Message\UriInterface; +use Slim\Http\Uri; + +/** + * Creates Slim 3 URI. + * + * @author Mika Tuupola <tuupola@appelsiini.net> + */ +final class SlimUriFactory implements UriFactory +{ + /** + * {@inheritdoc} + */ + public function createUri($uri) + { + if ($uri instanceof UriInterface) { + return $uri; + } + + if (is_string($uri)) { + return Uri::createFromString($uri); + } + + throw new \InvalidArgumentException('URI must be a string or UriInterface'); + } +} diff --git a/inc/mailgun/php-http/message/src/filters.php b/inc/mailgun/php-http/message/src/filters.php new file mode 100644 index 0000000..15ed73d --- /dev/null +++ b/inc/mailgun/php-http/message/src/filters.php @@ -0,0 +1,6 @@ +<?php + +// Register chunk filter if not found +if (!array_key_exists('chunk', stream_get_filters())) { + stream_filter_register('chunk', 'Http\Message\Encoding\Filter\Chunk'); +} |
