- Still WIP on Api's wrapper
This commit is contained in:
parent
7e69a09e7f
commit
5d9c0f0dbf
|
@ -10,6 +10,7 @@
|
|||
}
|
||||
],
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"mcnd/ulmus": "dev-master"
|
||||
},
|
||||
"repositories": [
|
||||
|
|
|
@ -47,7 +47,6 @@ class Rest implements AdapterInterface
|
|||
$this->$conf = $configuration[$conf];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function writableValue(mixed $value) : mixed
|
||||
|
|
|
@ -2,10 +2,18 @@
|
|||
|
||||
namespace Ulmus\Api;
|
||||
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use Ulmus\Api\Attribute\Obj\Api\ApiAction;
|
||||
use Ulmus\Api\Common\MethodEnum;
|
||||
use Ulmus\Api\Stream\Stream;
|
||||
use Ulmus\Api\Request\JsonRequest;
|
||||
use Ulmus\Api\Request\Request;
|
||||
use Ulmus\Api\Transport\CurlClient;
|
||||
use Ulmus\EntityCollection;
|
||||
use Ulmus\SearchRequest\SearchRequestInterface;
|
||||
|
||||
class ApiRepository extends \Ulmus\Repository
|
||||
{
|
||||
|
@ -29,29 +37,41 @@ class ApiRepository extends \Ulmus\Repository
|
|||
# Must manager URL here !
|
||||
$attribute = $this->getApiAttribute(Attribute\Obj\Api\Read::class);
|
||||
|
||||
$this->lastResponse = $response = $this->client->request($attribute->method, $this->buildRequestUrl($attribute->url));
|
||||
$request = $this->prepareRequest($this->buildRequestUrl($attribute->url), $attribute->method);
|
||||
|
||||
$dataset = $this->callApiResponseCallback($response, $attribute);
|
||||
$request = $this->callApiRequestCallback($request, $attribute);
|
||||
|
||||
return $this->instanciateEntity()->fromArray($dataset);
|
||||
$this->lastResponse = $response = $this->client->fromRequest($request);
|
||||
|
||||
$response = $this->callApiResponseCallback($response, $attribute);
|
||||
|
||||
return $this->instanciateEntity()->fromArray($response->render());
|
||||
}
|
||||
|
||||
public function loadAll() : EntityCollection
|
||||
{
|
||||
$attribute = $this->getApiAttribute(Attribute\Obj\Api\Collection::class);
|
||||
|
||||
$this->lastResponse = $response = $this->client->request($attribute->method, $this->buildRequestUrl($attribute->url));
|
||||
$request = $this->prepareRequest($this->buildRequestUrl($attribute->url), $attribute->method);
|
||||
|
||||
$collection = $this->callApiResponseCallback($response, $attribute);
|
||||
$request = $this->callApiRequestCallback($request, $attribute);
|
||||
|
||||
return $this->instanciateEntityCollection($collection);
|
||||
$this->lastResponse = $response = $this->client->fromRequest($request);
|
||||
|
||||
$response = $this->callApiResponseCallback($response, $attribute);
|
||||
|
||||
return $this->instanciateEntityCollection()->fromArray($response->render());
|
||||
}
|
||||
|
||||
public function save(object|array $entity, ?array $fieldsAndValue = null, bool $replace = false): bool
|
||||
{
|
||||
$attribute = $this->getApiAttribute(Attribute\Obj\Api\Create::class);
|
||||
|
||||
$this->lastResponse = $response = $this->client->request($attribute->method, $this->buildRequestUrl($attribute->url), $entity->entityLoadedDataset);
|
||||
$request = $this->prepareRequest($this->buildRequestUrl($attribute->url), $attribute->method);
|
||||
|
||||
$this->callApiRequestCallback($request, $attribute);
|
||||
|
||||
$this->lastResponse = $response = $this->client->fromRequest($request);
|
||||
|
||||
$ret = $this->callApiResponseCallback($response, $attribute);
|
||||
|
||||
|
@ -112,13 +132,40 @@ class ApiRepository extends \Ulmus\Repository
|
|||
return $route;
|
||||
}
|
||||
|
||||
protected function prepareRequest(string|UriInterface $uri, MethodEnum $method, null|string $body = null, array $headers = []) : RequestInterface
|
||||
{
|
||||
return new JsonRequest($uri, $method, $body === null ? Stream::fromTemp() : Stream::fromMemory($body), $headers);
|
||||
}
|
||||
|
||||
public function getApiAttribute(string $type) : object
|
||||
{
|
||||
return $this->entityClass::resolveEntity()->getAttributeImplementing($type);
|
||||
}
|
||||
|
||||
protected function callApiRequestCallback(RequestInterface $request, ApiAction $attribute) : mixed
|
||||
{
|
||||
return call_user_func_array($this->adapter->apiRequestCallback(), func_get_args());
|
||||
}
|
||||
|
||||
protected function callApiResponseCallback(ResponseInterface $response, ApiAction $attribute) : mixed
|
||||
{
|
||||
return call_user_func_array($this->adapter->apiResponseCallback(), func_get_args());
|
||||
}
|
||||
|
||||
public function count(): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
public function filterServerRequest(SearchRequestInterface $searchRequest, bool $count = true) : self
|
||||
{
|
||||
return $searchRequest->filter($this)
|
||||
->wheres($searchRequest->wheres(), \Ulmus\Query\Where::OPERATOR_EQUAL, \Ulmus\Query\Where::CONDITION_AND)
|
||||
->likes($searchRequest->likes(), \Ulmus\Query\Where::CONDITION_OR)
|
||||
->orders($searchRequest->orders())
|
||||
->groups($searchRequest->groups())
|
||||
->offset($searchRequest->offset())
|
||||
->limit($searchRequest->limit());
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\Api\Response;
|
||||
namespace Ulmus\Api\Common;
|
||||
|
||||
use Psr\Http\Message\MessageInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
@ -19,7 +19,7 @@ class Message implements MessageInterface
|
|||
return $this->protocolVersion;
|
||||
}
|
||||
|
||||
public function withProtocolVersion(string $version) : MessageInterface
|
||||
public function withProtocolVersion($version) : MessageInterface
|
||||
{
|
||||
return (clone $this)->setProtocolVersion($version);
|
||||
}
|
||||
|
@ -47,12 +47,12 @@ class Message implements MessageInterface
|
|||
return $output;
|
||||
}
|
||||
|
||||
public function hasHeader(string $name) : bool
|
||||
public function hasHeader($name) : bool
|
||||
{
|
||||
return isset($this->headers[HttpHeaderEnum::normalizeHeaderKey($name)]);
|
||||
}
|
||||
|
||||
public function getHeader(string $name) : array
|
||||
public function getHeader($name) : array
|
||||
{
|
||||
$name = HttpHeaderEnum::normalizeHeaderKey($name);
|
||||
|
||||
|
@ -63,7 +63,7 @@ class Message implements MessageInterface
|
|||
return $this->headers[$name];
|
||||
}
|
||||
|
||||
public function getHeaderLine(string $name) : string
|
||||
public function getHeaderLine($name) : string
|
||||
{
|
||||
$name = HttpHeaderEnum::normalizeHeaderKey($name);
|
||||
|
||||
|
@ -74,17 +74,17 @@ class Message implements MessageInterface
|
|||
return sprintf("%s: %s", $name, implode(', ', $this->getHeader($name)));
|
||||
}
|
||||
|
||||
public function withHeader(string $name, mixed $value): MessageInterface
|
||||
public function withHeader($name, mixed $value): MessageInterface
|
||||
{
|
||||
return (clone $this)->setHeaders(array_merge_recursive($this->headers, [ HttpHeaderEnum::normalizeHeaderKey($name) => $value ]));
|
||||
}
|
||||
|
||||
public function withAddedHeader(string $name, mixed $value) : MessageInterface
|
||||
public function withAddedHeader($name, mixed $value) : MessageInterface
|
||||
{
|
||||
return (clone $this)->setHeaders([ HttpHeaderEnum::normalizeHeaderKey($name) => $value ] + $this->headers);
|
||||
}
|
||||
|
||||
public function withoutHeader(string $name) : MessageInterface
|
||||
public function withoutHeader($name) : MessageInterface
|
||||
{
|
||||
return (clone $this)->unsetHeader($name);
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\Api\Request;
|
||||
namespace Ulmus\Api\Common;
|
||||
|
||||
interface StreamOutputInterface
|
||||
{
|
|
@ -0,0 +1,241 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\Api\Common;
|
||||
|
||||
use Psr\Http\Message\UriInterface;
|
||||
|
||||
class Uri implements UriInterface
|
||||
{
|
||||
public const IGNORE_HTTP_PORT = [ 80, 443 ];
|
||||
|
||||
protected string $scheme;
|
||||
|
||||
protected null|string $user;
|
||||
|
||||
protected null|string $password;
|
||||
|
||||
protected string $host;
|
||||
|
||||
protected null|int $port;
|
||||
|
||||
protected null|string $path;
|
||||
|
||||
protected null|array $query;
|
||||
|
||||
protected null|string $fragment;
|
||||
|
||||
public function __construct(string $uri = null)
|
||||
{
|
||||
$this->parse($uri);
|
||||
}
|
||||
|
||||
public static function from(string|UriInterface $uri = "") : UriInterface
|
||||
{
|
||||
if ($uri instanceof UriInterface) {
|
||||
return $uri;
|
||||
}
|
||||
|
||||
return new static($uri);
|
||||
}
|
||||
|
||||
protected function parse(string $uri): void
|
||||
{
|
||||
if (false === $parts = parse_url($uri)) {
|
||||
throw new \InvalidArgumentException(sprintf("Malformed URIs was provided (%s)", $uri));
|
||||
}
|
||||
|
||||
$this->setScheme($parts['scheme'] ?? "");
|
||||
$this->setUserInfo($parts['user'] ?? null, $parts['pass'] ?? null);
|
||||
$this->setHost($parts['host'] ?? "");
|
||||
$this->setPort($parts['port'] ?? null);
|
||||
$this->setPath($parts['path'] ?? null);
|
||||
$this->setQuery($parts['query'] ?? []);
|
||||
$this->setFragment($parts['fragment'] ?? null);
|
||||
}
|
||||
|
||||
public function getScheme()
|
||||
{
|
||||
return $this->scheme;
|
||||
}
|
||||
|
||||
public function renderScheme() : string
|
||||
{
|
||||
return $this->scheme ? "{$this->scheme}:" : "";
|
||||
}
|
||||
|
||||
protected function setScheme(string $scheme) : self
|
||||
{
|
||||
$this->scheme = $scheme;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAuthority() : string
|
||||
{
|
||||
return implode('', array_filter([
|
||||
$this->getHost(), $this->renderUserInfo(), $this->renderPort(), $this->renderFragment()
|
||||
]));
|
||||
}
|
||||
|
||||
public function renderAuthority() : string
|
||||
{
|
||||
$authority = $this->getAuthority();
|
||||
|
||||
return $authority ? "//{$authority}" : $authority;
|
||||
}
|
||||
|
||||
public function getUserInfo() : string
|
||||
{
|
||||
return $this->user . ( $this->password ? ":{$this->password}" : "" );
|
||||
}
|
||||
|
||||
public function renderUserInfo() : string
|
||||
{
|
||||
return ( $info = $this->getUserInfo() ) ? "{$info}@" : "";
|
||||
}
|
||||
|
||||
public function getHost()
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
protected function setHost(string $host) : static
|
||||
{
|
||||
$this->host = $host;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPort()
|
||||
{
|
||||
return $this->port;
|
||||
}
|
||||
|
||||
public function renderPort(array $ignore = self::IGNORE_HTTP_PORT) : string
|
||||
{
|
||||
$port = $this->getPort();
|
||||
|
||||
return $port && ! in_array($port, $ignore) ? ":{$port}" : "";
|
||||
}
|
||||
|
||||
public function getPath()
|
||||
{
|
||||
$path = implode('/', array_map('rawurlencode', explode('/', $this->path)));
|
||||
|
||||
return str_starts_with($this->path, '/') ? '/' . ltrim($this->path, '/.') : $this->path;
|
||||
}
|
||||
|
||||
public function renderPath() : string
|
||||
{
|
||||
return $this->getPath();
|
||||
}
|
||||
|
||||
public function getQuery()
|
||||
{
|
||||
return http_build_query($this->query, "", null, PHP_QUERY_RFC3986);
|
||||
}
|
||||
|
||||
public function renderQuery() : string
|
||||
{
|
||||
$query = $this->getQuery();
|
||||
|
||||
return $query ? "?{$query}" : "";
|
||||
}
|
||||
|
||||
public function getFragment()
|
||||
{
|
||||
return rawurlencode($this->fragment ? "#{$this->fragment}" : "");
|
||||
}
|
||||
|
||||
public function renderFragment() : string
|
||||
{
|
||||
return $this->getFragment();
|
||||
}
|
||||
|
||||
public function withScheme($scheme)
|
||||
{
|
||||
return (clone $this)->setScheme($scheme);
|
||||
}
|
||||
|
||||
public function withUserInfo($user, $password = null)
|
||||
{
|
||||
return (clone $this)->setUserInfo($user, $password);
|
||||
}
|
||||
|
||||
protected function setUserInfo(null|string $user, null|string $password) : static
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->password = $password;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withHost($host)
|
||||
{
|
||||
return (clone $this)->setHost($host);
|
||||
}
|
||||
|
||||
public function withPort($port)
|
||||
{
|
||||
return (clone $this)->setPort($port);
|
||||
}
|
||||
|
||||
protected function setPort(null|int $port) : static
|
||||
{
|
||||
$this->port = $port;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withPath($path)
|
||||
{
|
||||
return (clone $this)->setPath($path);
|
||||
}
|
||||
|
||||
protected function setPath(null|string $path) : static
|
||||
{
|
||||
$this->path = $path;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withQuery($query)
|
||||
{
|
||||
return $this->setQuery($query);
|
||||
}
|
||||
|
||||
protected function setQuery(null|array|string $query) : static
|
||||
{
|
||||
if (is_string($query)) {
|
||||
parse_str($query, $parsed);
|
||||
}
|
||||
|
||||
$this->query = $parsed ?? $query;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withFragment($fragment)
|
||||
{
|
||||
return (clone $this)->setFragment($fragment);
|
||||
}
|
||||
|
||||
protected function setFragment(null|string $fragment) : static
|
||||
{
|
||||
$this->fragment = $fragment;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->render();
|
||||
}
|
||||
|
||||
public function render() : string
|
||||
{
|
||||
return implode('', array_filter([
|
||||
$this->renderScheme(), $this->renderAuthority(), $this->renderPath(), $this->renderQuery(), $this->renderFragment()
|
||||
]));
|
||||
}
|
||||
}
|
|
@ -16,6 +16,8 @@ class ConnectionAdapter extends \Ulmus\ConnectionAdapter
|
|||
|
||||
protected mixed $apiResponseCallback;
|
||||
|
||||
protected mixed $apiRequestCallback;
|
||||
|
||||
public function resolveConfiguration(): void
|
||||
{
|
||||
$connection = $this->configuration['connections'][$this->name] ?? [];
|
||||
|
@ -66,4 +68,9 @@ class ConnectionAdapter extends \Ulmus\ConnectionAdapter
|
|||
{
|
||||
return $callback ? $this->apiResponseCallback = $callback : $this->apiResponseCallback ?? fn($resp) => $resp->render();
|
||||
}
|
||||
|
||||
public function apiRequestCallback(callable $callback = null) : callable|null
|
||||
{
|
||||
return $callback ? $this->apiRequestCallback = $callback : $this->apiRequestCallback ?? fn($resp) => $resp->render();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\Api\Request;
|
||||
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use Ulmus\Api\Common\HttpHeaderEnum;
|
||||
use Ulmus\Api\Common\MethodEnum;
|
||||
use Ulmus\Api\Stream\Stream;
|
||||
use Ulmus\Api\Common\StreamOutputInterface;
|
||||
use Ulmus\Api\Common\Uri;
|
||||
|
||||
class JsonRequest extends Request implements StreamOutputInterface
|
||||
{
|
||||
public const DEFAULT_JSON_ENCODING_FLAGS = JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_TAG | JSON_UNESCAPED_SLASHES;
|
||||
|
||||
public const DEFAULT_JSON_DECODING_FLAGS = JSON_THROW_ON_ERROR;
|
||||
|
||||
public int $jsonEncodingOptions;
|
||||
|
||||
public function __construct(string|UriInterface $uri, MethodEnum $method, null|StreamInterface $stream = null, array $headers = [], $jsonEncodingOptions = self::DEFAULT_JSON_ENCODING_FLAGS)
|
||||
{
|
||||
parent::__construct($uri, $method, $stream, $headers + [ 'Content-Type' => "application/json", 'Accept' => "application/json" ]);
|
||||
|
||||
$this->jsonEncodingOptions = $jsonEncodingOptions;
|
||||
}
|
||||
|
||||
public static function fromContent(mixed $content, int $encodingOptions = self::DEFAULT_JSON_ENCODING_FLAGS) : StreamInterface
|
||||
{
|
||||
if ( false === $json = json_encode($content, $encodingOptions) ) {
|
||||
throw new \InvalidArgumentException(sprintf('Json encoding failed with message `%s`.', json_last_error_msg()));
|
||||
}
|
||||
|
||||
return static::fromJsonEncoded($json);
|
||||
}
|
||||
|
||||
public static function fromJsonEncoded(string $json) : StreamInterface
|
||||
{
|
||||
$obj = Stream::fromTemp($json);
|
||||
$obj->rewind();
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public static function fromRequest(RequestInterface $request) : static
|
||||
{
|
||||
return new static($request->getUri(), MethodEnum::from($request->getMethod()), $request->getBody(), $request->getHeaders());
|
||||
}
|
||||
|
||||
public function render(): mixed
|
||||
{
|
||||
$content = $this->getBody()->getContents() ?: "[]";
|
||||
|
||||
return json_decode($content, true, 1024, static::DEFAULT_JSON_DECODING_FLAGS);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\Api\Request;
|
||||
|
||||
use League\CommonMark\Block\Element\StringContainerInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use Ulmus\Api\Common\{MethodEnum, Message, Stream, Uri};
|
||||
|
||||
class Request extends Message implements RequestInterface
|
||||
{
|
||||
protected int $code;
|
||||
|
||||
protected string $message;
|
||||
|
||||
protected string $target;
|
||||
|
||||
protected UriInterface $uri;
|
||||
|
||||
protected MethodEnum $method;
|
||||
|
||||
public function __construct(string|UriInterface $uri, MethodEnum $method, null|StreamInterface $stream = null, array $headers = [])
|
||||
{
|
||||
$this->method = $method;
|
||||
$this->uri = Uri::from($uri);
|
||||
$this->stream = $stream ?? Stream::fromMemory();
|
||||
$this->setHeaders($headers );
|
||||
}
|
||||
|
||||
public function getRequestTarget(): string
|
||||
{
|
||||
if ( isset($this->target) ) {
|
||||
return $this->target;
|
||||
}
|
||||
|
||||
$query = $this->uri->getQuery() ?: null;
|
||||
|
||||
$target = $this->uri->getPath() . ( $query ? "?{$query}" : "" );
|
||||
|
||||
return $target ?: '/';
|
||||
}
|
||||
|
||||
public function withRequestTarget($requestTarget)
|
||||
{
|
||||
return (clone $this)->setRequestTarget($requestTarget);
|
||||
}
|
||||
|
||||
public function setRequestTarget(string $target) : static
|
||||
{
|
||||
$this->target = $target;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMethod()
|
||||
{
|
||||
return $this->method->value;
|
||||
}
|
||||
|
||||
public function withMethod($method)
|
||||
{
|
||||
return (clone $this)->setMethod($method);
|
||||
}
|
||||
|
||||
protected function setMethod(string $method) : static
|
||||
{
|
||||
$this->method = MethodEnum::from($method);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUri()
|
||||
{
|
||||
return (string) $this->uri;
|
||||
}
|
||||
|
||||
public function withUri(UriInterface $uri, $preserveHost = false)
|
||||
{
|
||||
return (clone $this)->setUri($uri, $preserveHost);
|
||||
}
|
||||
|
||||
protected function setUri(UriInterface|string $uri, bool $preserveHost = false) : static
|
||||
{
|
||||
$this->uri = Uri::from($uri);
|
||||
|
||||
if ( ! $preserveHost || ! $this->hasHeader('Host') ) {
|
||||
$this->headers['Host'] = [ $this->renderHostHeader() ];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function renderHostHeader(UriInterface $fromUri = null) : string
|
||||
{
|
||||
$fromUri ??= $this->uri;
|
||||
|
||||
$port = $fromUri->getPort();
|
||||
|
||||
return implode('', [
|
||||
$fromUri->getHost(), $port && ! in_array($port, Uri::IGNORE_HTTP_PORT) ? ":{$port}" : ""
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -56,13 +56,13 @@ class RequestBuilder implements Ulmus\Query\QueryBuilderInterface
|
|||
|
||||
public function limit(int $value) : self
|
||||
{
|
||||
if ( null === $limit = $this->getFragment(Ulmus\Query\Limit::class) ) {
|
||||
/*if ( null === $limit = $this->getFragment(Ulmus\Query\Limit::class) ) {
|
||||
$limit = new Query\Limit();
|
||||
$this->push($limit);
|
||||
}
|
||||
|
||||
$limit->set($value);
|
||||
|
||||
*/
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -128,7 +128,7 @@ class RequestBuilder implements Ulmus\Query\QueryBuilderInterface
|
|||
return null;
|
||||
}
|
||||
|
||||
public function removeFragment(Ulmus\Query\Fragment $fragment) : void
|
||||
public function removeFragment(\Ulmus\Query\Fragment|array|\Stringable|string $fragment) : void
|
||||
{
|
||||
foreach($this->queryStack as $key => $item) {
|
||||
if ( $item === $fragment ) {
|
||||
|
|
|
@ -5,8 +5,8 @@ namespace Ulmus\Api\Response;
|
|||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Ulmus\Api\Common\HttpHeaderEnum;
|
||||
use Ulmus\Api\Request\Stream;
|
||||
use Ulmus\Api\Request\StreamOutputInterface;
|
||||
use Ulmus\Api\Stream\Stream;
|
||||
use Ulmus\Api\Common\StreamOutputInterface;
|
||||
|
||||
class JsonResponse extends Response implements StreamOutputInterface
|
||||
{
|
||||
|
@ -34,7 +34,7 @@ class JsonResponse extends Response implements StreamOutputInterface
|
|||
|
||||
public static function fromJsonEncoded(string $json) : StreamInterface
|
||||
{
|
||||
$obj = static::fromTemp($json);
|
||||
$obj = Stream::fromTemp($json);
|
||||
$obj->rewind();
|
||||
|
||||
return $obj;
|
||||
|
|
|
@ -4,7 +4,8 @@ namespace Ulmus\Api\Response;
|
|||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Ulmus\Api\Request\Stream;
|
||||
use Ulmus\Api\Common\{ Message };
|
||||
use Ulmus\Api\Stream\Stream;
|
||||
|
||||
class Response extends Message implements ResponseInterface
|
||||
{
|
||||
|
@ -15,7 +16,7 @@ class Response extends Message implements ResponseInterface
|
|||
public function __construct(null|StreamInterface $stream = null, int $statusCode = 200, array $headers = [])
|
||||
{
|
||||
$this->setStatusCode($statusCode);
|
||||
$this->stream = $stream ?? new Stream("php://memory", "wb+");
|
||||
$this->stream = $stream ?? Stream::fromMemory();
|
||||
$this->setHeaders($headers);
|
||||
}
|
||||
|
||||
|
@ -24,7 +25,7 @@ class Response extends Message implements ResponseInterface
|
|||
return $this->code;
|
||||
}
|
||||
|
||||
public function withStatus(int $code, null|string $reasonPhrase = null)/* : ResponseInterface*/
|
||||
public function withStatus($code, $reasonPhrase = null)/* : ResponseInterface*/
|
||||
{
|
||||
return (clone $this)->setStatusCode($code, $reasonPhrase);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\Api\Stream;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Ulmus\Api\Common\HttpHeaderEnum;
|
||||
use Ulmus\Api\Stream\Stream;
|
||||
use Ulmus\Api\Common\StreamOutputInterface;
|
||||
|
||||
class JsonStream extends Stream implements StreamOutputInterface
|
||||
{
|
||||
public const DEFAULT_JSON_ENCODING_FLAGS = JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_TAG | JSON_UNESCAPED_SLASHES;
|
||||
|
||||
public const DEFAULT_JSON_DECODING_FLAGS = JSON_THROW_ON_ERROR;
|
||||
|
||||
public int $depth = 1024;
|
||||
|
||||
public int $encodingOptions;
|
||||
|
||||
public int $decodingOptions;
|
||||
|
||||
public function __construct($encodingOptions = self::DEFAULT_JSON_ENCODING_FLAGS, $decodingOptions = self::DEFAULT_JSON_DECODING_FLAGS)
|
||||
{
|
||||
$this->encodingOptions = $encodingOptions;
|
||||
$this->decodingOptions = $decodingOptions;
|
||||
}
|
||||
|
||||
public static function fromContent(mixed $content, int $encodingOptions = self::DEFAULT_JSON_ENCODING_FLAGS) : StreamInterface
|
||||
{
|
||||
if ( false === $json = json_encode($content, $encodingOptions) ) {
|
||||
throw new \InvalidArgumentException(sprintf('Json encoding failed with message `%s`.', json_last_error_msg()));
|
||||
}
|
||||
|
||||
return static::fromJsonEncoded($json);
|
||||
}
|
||||
|
||||
public static function fromJsonEncoded(string $json) : StreamInterface
|
||||
{
|
||||
$obj = Stream::fromTemp($json);
|
||||
$obj->rewind();
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function render(): mixed
|
||||
{
|
||||
return json_decode($this->getContents(), true, $this->depth, $this->decodingOptions);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\Api\Request;
|
||||
namespace Ulmus\Api\Stream;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
|
@ -104,7 +104,7 @@ class Stream implements StreamInterface
|
|||
|
||||
public function isWritable() : bool
|
||||
{
|
||||
return $this->hasMode('wxca+');
|
||||
return $this->hasMode('acwx+');
|
||||
}
|
||||
|
||||
public function write($string) : false|int
|
||||
|
@ -137,7 +137,7 @@ class Stream implements StreamInterface
|
|||
return $this->hasMode('r+');
|
||||
}
|
||||
|
||||
public function read(int $length): string
|
||||
public function read($length): string
|
||||
{
|
||||
if (! $this->isReadable()) {
|
||||
throw new \UnexpectedValueException("An error occured while trying to read a stream which was not readable.");
|
||||
|
@ -178,6 +178,6 @@ class Stream implements StreamInterface
|
|||
$meta = stream_get_meta_data($this->stream);
|
||||
$mode = $meta['mode'];
|
||||
|
||||
return strpbrk($mode, 'acwx+') !== false;
|
||||
return strpbrk($mode, $search) !== false;
|
||||
}
|
||||
}
|
|
@ -29,12 +29,12 @@ class CurlAuthorization
|
|||
$this->basic($username, $password);
|
||||
break;
|
||||
|
||||
case AuthenticationEnum::NTLM:
|
||||
case AuthenticationEnum::Ntlm:
|
||||
$this->ntlm($username, $password);
|
||||
break;
|
||||
|
||||
case AuthenticationEnum::Negotiate:
|
||||
$this->ntlm($username, $password);
|
||||
$this->negotiate($username, $password);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,12 +3,9 @@
|
|||
namespace Ulmus\Api\Transport;
|
||||
|
||||
use Ulmus\Api\Common\{ ContentTypeEnum, MethodEnum, HttpHeaderEnum };
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Ulmus\Api\Response\JsonResponse;
|
||||
use Ulmus\Api\Response\Response;
|
||||
use Ulmus\Api\Request\Stream;
|
||||
use Ulmus\Attribute\Obj\Method;
|
||||
use Ulmus\Api\Stream\Stream;
|
||||
use Psr\Http\Message\{ RequestInterface, ResponseInterface };
|
||||
use Ulmus\Api\Response\{ Response, JsonResponse };
|
||||
|
||||
abstract class CurlTransport {
|
||||
|
||||
|
@ -52,16 +49,16 @@ abstract class CurlTransport {
|
|||
return $this->request(MethodEnum::Put, $url, $data, $headers, $curlOptions);
|
||||
}
|
||||
|
||||
public function fromRequest(ServerRequestInterface $request) : ResponseInterface
|
||||
public function fromRequest(RequestInterface $request) : ResponseInterface
|
||||
{
|
||||
return $this->request($request->getMethod(), $request->getUri(), $request->getBody()->getContents(), $request->getHeaders());
|
||||
return $this->request(MethodEnum::from($request->getMethod()), $request->getUri(), $request->getBody()->getContents(), $request->getHeaders());
|
||||
}
|
||||
|
||||
public function request(MethodEnum $method, string $url, mixed $data = null, array $headers = [], array $options = []) : ResponseInterface
|
||||
{
|
||||
$response = new Response();
|
||||
|
||||
$headers = HttpHeaderEnum::normalizeHeaderArray($headers + $this->headers);
|
||||
$headers = array_merge_recursive(HttpHeaderEnum::normalizeHeaderArray($headers), HttpHeaderEnum::normalizeHeaderArray($this->headers));
|
||||
|
||||
$options += $this->curlOptions;
|
||||
|
||||
|
@ -83,6 +80,7 @@ abstract class CurlTransport {
|
|||
CURLOPT_HEADERFUNCTION => function($curl, $headerLine) use (& $response) {
|
||||
if ( strpos($headerLine, ':') ) {
|
||||
list($key, $value) = array_map('trim', explode(':', $headerLine));
|
||||
|
||||
$response = $response->withHeader($key, $value);
|
||||
}
|
||||
elseif ( strtoupper(substr($headerLine, 0, 4)) === 'HTTP' ) {
|
||||
|
@ -145,7 +143,7 @@ abstract class CurlTransport {
|
|||
|
||||
switch($this->contentType) {
|
||||
case ContentTypeEnum::Json:
|
||||
$options[CURLOPT_POSTFIELDS] = json_encode($data, JsonResponse::DEFAULT_JSON_ENCODING_FLAGS);
|
||||
$options[CURLOPT_POSTFIELDS] = json_encode(is_string($data) && empty($data) ? new \stdClass() : $data, JsonResponse::DEFAULT_JSON_ENCODING_FLAGS);
|
||||
$headers['Accept'] ??= $this->contentType->value;
|
||||
break;
|
||||
|
||||
|
|
Loading…
Reference in New Issue