Skip to content

Commit

Permalink
#51 Mise en conformité du composant Http.
Browse files Browse the repository at this point in the history
  • Loading branch information
noelma committed May 25, 2019
1 parent a31adba commit 67e43bf
Show file tree
Hide file tree
Showing 15 changed files with 309 additions and 204 deletions.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -12,6 +12,7 @@ Soosyze Framework est un micro-framework MVC object offrant un socle solide de d
* [![PSR-2](https://img.shields.io/badge/PSR-2-yellow.svg)](https://www.php-fig.org/psr/psr-2 "Coding Style Guide") L'écriture du code est standardisée,
* [![PSR-4](https://img.shields.io/badge/PSR-4-yellow.svg)](https://www.php-fig.org/psr/psr-4 "Autoloading Standard") Autoloader, interchangeable avec l'autoloader de Composer,
* [![PSR-7](https://img.shields.io/badge/PSR-7-yellow.svg)](https://www.php-fig.org/psr/psr-7 "HTTP Message Interface") Composant Http (Resquest, Response, Message, Stream...),
* À passé les tests https://github.com/php-http/psr7-integration-tests à 100%, rendant l'implémentation intéroperable avec d'autres bibliothèques psr-7
* [![PSR-11](https://img.shields.io/badge/PSR-11-yellow.svg)](https://www.php-fig.org/psr/psr-11 "Container Interface") Container d'injection de dépendance ou CID,
* [![PSR-17](https://img.shields.io/badge/PSR-17-yellow.svg)](https://www.php-fig.org/psr/psr-17 "HTTP Factories") Fabriques Http implémentées sans les interfaces qui contraignent les implémentations à PHP7,
* Découpe des fonctionnalitées en modules,
Expand Down
88 changes: 75 additions & 13 deletions src/Components/Http/Message.php
Expand Up @@ -46,6 +46,13 @@ class Message implements MessageInterface
*/
protected $headers = [];

/**
* Les noms des entêtes.
*
* @var string[]
*/
protected $name = [];

/**
* Protocoles pris en charges.
*
Expand Down Expand Up @@ -97,7 +104,7 @@ public function getHeaders()
*/
public function hasHeader($name)
{
return isset($this->headers[ strtolower($name) ]);
return isset($this->name[ strtolower($name) ]);
}

/**
Expand All @@ -111,7 +118,7 @@ public function hasHeader($name)
public function getHeader($name)
{
return $this->hasHeader($name)
? $this->headers[ strtolower($name) ]
? $this->headers[ $this->name[ strtolower($name) ] ]
: [];
}

Expand All @@ -126,7 +133,7 @@ public function getHeader($name)
public function getHeaderLine($name)
{
return $this->hasHeader($name)
? implode(',', $this->headers[ strtolower($name) ])
? implode(',', $this->getHeader($name))
: '';
}

Expand All @@ -140,10 +147,10 @@ public function getHeaderLine($name)
*/
public function withHeader($name, $value)
{
$clone = clone $this;
$clone->headers[ strtolower($name) ] = is_array($value)
? $value
: [ $value ];
$clone = clone $this;
$values = $clone->validateAndTrimHeader($name, $value);
$clone->headers[ $name ] = $values;
$clone->name[ strtolower($name) ] = $name;

return $clone;
}
Expand All @@ -158,13 +165,15 @@ public function withHeader($name, $value)
*/
public function withAddedHeader($name, $value)
{
$clone = clone $this;
if (!is_array($value)) {
$value = [ $value ];
$clone = clone $this;
$values = $this->validateAndTrimHeader($name, $value);

if (!$this->hasHeader($name)) {
$clone->name[ strtolower($name) ] = $name;
}
/* Pour ne pas écraser les valeurs avec le array merge utilise une boucle simple. */
foreach ($value as $head) {
$clone->headers[ strtolower($name) ][] = $head;
foreach ($values as $head) {
$clone->headers[ $clone->name[ strtolower($name) ] ][] = $head;
}

return $clone;
Expand All @@ -181,7 +190,7 @@ public function withoutHeader($name)
{
$clone = clone $this;
if ($clone->hasHeader($name)) {
unset($clone->headers[ strtolower($name) ]);
unset($clone->headers[ $this->name[ strtolower($name) ] ], $clone->name[ strtolower($name) ]);
}

return $clone;
Expand Down Expand Up @@ -243,4 +252,57 @@ protected function withHeaders(array $headers)
: [ $value ];
}
}

/**
* Assurez-vous que l'en-tête est conforme à la norme RFC 7230.
*
* Les noms d'en-tête doivent être une chaîne non vide composée de caractères de jeton.
*
* Les valeurs d'en-tête doivent être des chaînes composées de caractères visibles, tous optionnels.
* Les espaces blancs de début et de fin sont supprimés. Cette méthode va toujours dépouiller ces
* espaces optionnels. Notez que la méthode ne permet pas de replier les espaces au sein de
* Les valeurs étant obsolètes dans presque toutes les instances par la RFC.
*
* header-field = field-name ":" OWS field-value OWS
* field-name = 1*( "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / "^"
* / "_" / "`" / "|" / "~" / %x30-39 / ( %x41-5A / %x61-7A ) )
* OWS = *( SP / HTAB )
* field-value = *( ( %x21-7E / %x80-FF ) [ 1*( SP / HTAB ) ( %x21-7E / %x80-FF ) ] )
*
* @see https://tools.ietf.org/html/rfc7230#section-3.2.4
*
* @param string $header
* @param array|string $values
*
* @throws InvalidArgumentException;
*
* @return array
*/
private function validateAndTrimHeader($header, $values)
{
if (!\is_string($header) || 1 !== \preg_match('@^[!#$%&\'*+.^_`|~0-9A-Za-z-]+$@', $header)) {
throw new \InvalidArgumentException('Header name must be an RFC 7230 compatible string.');
}
if (!\is_array($values)) {
// This is simple, just one value.
if ((!\is_numeric($values) && !\is_string($values)) || 1 !== \preg_match("@^[ \t\x21-\x7E\x80-\xFF]*$@", (string) $values)) {
throw new \InvalidArgumentException('Header values must be RFC 7230 compatible strings.');
}

return [ \trim((string) $values, " \t") ];
}
if (empty($values)) {
throw new \InvalidArgumentException('Header values must be a string or an array of strings, empty array given.');
}
// Assert Non empty array
$returnValues = [];
foreach ($values as $v) {
if ((!\is_numeric($v) && !\is_string($v)) || 1 !== \preg_match("@^[ \t\x21-\x7E\x80-\xFF]*$@", (string) $v)) {
throw new \InvalidArgumentException('Header values must be RFC 7230 compatible strings.');
}
$returnValues[] = \trim((string) $v, " \t");
}

return $returnValues;
}
}
23 changes: 9 additions & 14 deletions src/Components/Http/Request.php
Expand Up @@ -116,11 +116,10 @@ public function getRequestTarget()
$target = $this->uri->getPath() != ''
? $this->uri->getPath()
: '/';
$target .= $this->uri->getQuery() != ''

return $target .= $this->uri->getQuery() != ''
? '?' . $this->uri->getQuery()
: '';

return $target;
}

/**
Expand Down Expand Up @@ -189,16 +188,12 @@ public function withUri(UriInterface $uri, $preserveHost = false)
{
$clone = clone $this;
$clone->uri = $uri;

if ($preserveHost) {
/**
* Si l'en-tête Host est manquant ou vide, et que le nouvel URI contient
* un composant hôte, cette méthode DOIT mettre à jour l'en-tête Host dans le retour.
*/
$headerHost = $this->getHeader('Host');
if (empty($headerHost) && $uri->getHost() !== '') {
return $clone->withHeader('Host', $uri->getHost());
}
/*
* Si l'en-tête Host est manquant ou vide, et que le nouvel URI contient
* un composant hôte, cette méthode DOIT mettre à jour l'en-tête Host dans le retour.
*/
if (!$preserveHost || !$this->hasHeader('Host') && $uri->getHost() !== '') {
return $clone->withHeader('Host', $uri->getHost());
}

return $clone;
Expand All @@ -225,6 +220,6 @@ protected function filterMethod($method)
throw new \InvalidArgumentException('The method is not valid (only ' . implode('|', $this->methods) . ').');
}

return $methodUp;
return $method;
}
}
4 changes: 2 additions & 2 deletions src/Components/Http/Response.php
Expand Up @@ -205,10 +205,10 @@ public function getReasonPhrase()
*/
protected function filtreCode($code)
{
if (is_numeric($code)) {
if (is_numeric($code) && is_int($code)) {
$code = (int) $code;
}
if (!isset($this->reasonPhraseDefault[ $code ])) {
if (!is_int($code) || !isset($this->reasonPhraseDefault[ $code ])) {
throw new \InvalidArgumentException('Status code is invalid.');
}

Expand Down
21 changes: 18 additions & 3 deletions src/Components/Http/ServerRequest.php
Expand Up @@ -108,6 +108,19 @@ public static function create()

/* Construit le Uri de la requête */
$uri = Uri::create($scheme . '://' . $_SERVER[ 'HTTP_HOST' ] . $_SERVER[ 'REQUEST_URI' ]);
$hasQuery = false;
if (isset($_SERVER['REQUEST_URI'])) {
$requestUriParts = explode('?', $_SERVER['REQUEST_URI'], 2);
$uri = $uri->withPath($requestUriParts[0]);
if (isset($requestUriParts[1])) {
$hasQuery = true;
$uri = $uri->withQuery($requestUriParts[1]);
}
}
if (!$hasQuery && isset($_SERVER['QUERY_STRING'])) {
$uri = $uri->withQuery($_SERVER['QUERY_STRING']);
}

$headers = function_exists('getallheaders')
? getallheaders()
: [];
Expand All @@ -119,7 +132,7 @@ public static function create()
$method,
$uri,
$headers,
new Stream(),
new Stream(fopen('php://input', 'r+')),
$protocol,
$_SERVER,
$_COOKIE,
Expand Down Expand Up @@ -288,6 +301,10 @@ public function getParsedBody()
*/
public function withParsedBody($data)
{
if (!\is_array($data) && !\is_object($data) && null !== $data) {
throw new \InvalidArgumentException('First parameter to withParsedBody MUST be object, array or null');
}

$clone = clone $this;
$clone->parseBody = $data;

Expand Down Expand Up @@ -491,8 +508,6 @@ private static function normaliseUplaod(array $files)
$output[ $key ] = UploadedFile::create($value);
} elseif (is_array($value)) {
$output[ $key ] = self::normaliseUplaod($value);
} else {
throw new \InvalidArgumentException('The input parameter is not in the correct format.');
}
}

Expand Down
41 changes: 17 additions & 24 deletions src/Components/Http/Stream.php
Expand Up @@ -182,17 +182,16 @@ public function getSize()
public function tell()
{
$this->valideAttach();
if (($stream = ftell($this->stream)) === false) {
if (($handle = ftell($this->stream)) === false) {
throw new \RuntimeException('An error has occurred.');
}

return $stream;
return $handle;
}

/**
* Renvoie true si le flux se trouve à la fin du flux.
*
* @throws \RuntimeException Une erreur est survenue.
* @return bool
*/
public function eof()
Expand All @@ -205,15 +204,13 @@ public function eof()
/**
* Renvoie si la position du flux peut-être modifié.
*
* @return bool|array
* @return bool
*/
public function isSeekable()
{
$seekable = $this->getMetadata('seekable');

return $seekable !== null
? $seekable
: false;
return $seekable !== null && $seekable !== false;
}

/**
Expand Down Expand Up @@ -244,7 +241,7 @@ public function seek($offset, $whence = SEEK_SET)
*/
public function rewind()
{
$this->valideAttach();
$this->valideAttach()->valideSeekable();
if (!rewind($this->stream)) {
throw new \RuntimeException('An error has occurred.');
}
Expand All @@ -257,9 +254,7 @@ public function rewind()
*/
public function isWritable()
{
$meta = $this->getMetadata('mode');

return in_array($meta, self::$modes[ 'write' ]);
return in_array($this->getMetadata('mode'), self::$modes[ 'write' ]);
}

/**
Expand All @@ -273,11 +268,11 @@ public function isWritable()
public function write($string)
{
$this->valideAttach()->valideWrite();
if (($stream = fwrite($this->stream, $string)) === false) {
if (($handle = fwrite($this->stream, $string)) === false) {
throw new \RuntimeException('An error has occurred.');
}

return $stream;
return $handle;
}

/**
Expand All @@ -287,9 +282,7 @@ public function write($string)
*/
public function isReadable()
{
$meta = $this->getMetadata('mode');

return in_array($meta, self::$modes[ 'read' ]);
return in_array($this->getMetadata('mode'), self::$modes[ 'read' ]);
}

/**
Expand All @@ -312,11 +305,11 @@ public function read($length)
if ($length === 0) {
return '';
}
if (($stream = fread($this->stream, $length)) === false) {
if (($handle = fread($this->stream, $length)) === false) {
throw new \RuntimeException('An error has occurred.');
}

return $stream;
return $handle;
}

/**
Expand All @@ -328,11 +321,11 @@ public function read($length)
public function getContents()
{
$this->valideAttach()->valideRead();
if (($stream = stream_get_contents($this->stream)) === false) {
if (($handle = stream_get_contents($this->stream)) === false) {
throw new \RuntimeException('An error occurred while reading the stream.');
}

return $stream;
return $handle;
}

/**
Expand Down Expand Up @@ -375,14 +368,14 @@ protected function isAttached()
*/
private function createStreamFromScalar($scalar)
{
$stream = fopen('php://temp', 'r+');
$handle = fopen('php://temp', 'r+');

if ($scalar !== '') {
fwrite($stream, $scalar);
fseek($stream, 0);
fwrite($handle, $scalar);
fseek($handle, 0);
}

$this->stream = $stream;
$this->stream = $handle;
}

/**
Expand Down

0 comments on commit 67e43bf

Please sign in to comment.