renamed RequestBag to ParameterBag, added HeaderBag, changed the Response to use the new HeaderBag, added a class to manage the Cache-Control header
This commit is contained in:
parent
b3a6c6f346
commit
c34da5d6c4
277
src/Symfony/Components/RequestHandler/CacheControl.php
Normal file
277
src/Symfony/Components/RequestHandler/CacheControl.php
Normal file
@ -0,0 +1,277 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Components\RequestHandler;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony framework.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* CacheControl is a wrapper for the Cache-Control HTTP header.
|
||||
*
|
||||
* This class knows about allowed attributes
|
||||
* (and those that only apply to requests or responses).
|
||||
*
|
||||
* @package Symfony
|
||||
* @subpackage Components_RequestHandler
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class CacheControl
|
||||
{
|
||||
protected $bag;
|
||||
protected $attributes;
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Symfony\Components\RequestHandler\HeaderBag $bag A HeaderBag instance
|
||||
* @param string $header The value of the Cache-Control HTTP header
|
||||
* @param string $type The type (null, request, or response)
|
||||
*/
|
||||
public function __construct(HeaderBag $bag, $header, $type = null)
|
||||
{
|
||||
$this->bag = $bag;
|
||||
$this->attributes = $this->parse($header);
|
||||
|
||||
if (null !== $type && !in_array($type, array('request', 'response')))
|
||||
{
|
||||
throw new \InvalidArgumentException(sprintf('The "%s" type is not supported by the CacheControl constructor.', $type));
|
||||
}
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$parts = array();
|
||||
ksort($this->attributes);
|
||||
foreach ($this->attributes as $key => $value)
|
||||
{
|
||||
if (true === $value)
|
||||
{
|
||||
$parts[] = $key;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (preg_match('#[^a-zA-Z0-9._-]#', $value))
|
||||
{
|
||||
$value = '"'.$value.'"';
|
||||
}
|
||||
|
||||
$parts[] = "$key=$value";
|
||||
}
|
||||
}
|
||||
|
||||
return implode(',', $parts);
|
||||
}
|
||||
|
||||
public function getMaxStale()
|
||||
{
|
||||
$this->checkAttribute('max-stale', 'request');
|
||||
|
||||
return array_key_exists('max-stale', $this->attributes) ? $this->attributes['max-stale'] : false;
|
||||
}
|
||||
|
||||
public function setMaxStale($value)
|
||||
{
|
||||
$this->checkAttribute('max-stale', 'request');
|
||||
|
||||
$this->setValue('max-stale', $value);
|
||||
}
|
||||
|
||||
public function getMinFresh()
|
||||
{
|
||||
$this->checkAttribute('min-fresh', 'request');
|
||||
|
||||
return array_key_exists('min-fresh', $this->attributes) ? $this->attributes['min-fresh'] : false;
|
||||
}
|
||||
|
||||
public function setMinFresh($value)
|
||||
{
|
||||
$this->checkAttribute('min-fresh', 'request');
|
||||
|
||||
$this->setValue('min-fresh', $value);
|
||||
}
|
||||
|
||||
public function isOnlyIfCached()
|
||||
{
|
||||
$this->checkAttribute('only-if-cached', 'request');
|
||||
|
||||
return array_key_exists('only-if-cached', $this->attributes);
|
||||
}
|
||||
|
||||
public function setOnlyIfCached($value)
|
||||
{
|
||||
$this->checkAttribute('only-if-cached', 'request');
|
||||
|
||||
$this->setValue('only-if-cached', $value, true);
|
||||
}
|
||||
|
||||
public function isPublic()
|
||||
{
|
||||
$this->checkAttribute('public', 'response');
|
||||
|
||||
return array_key_exists('public', $this->attributes);
|
||||
}
|
||||
|
||||
public function setPublic($value)
|
||||
{
|
||||
$this->checkAttribute('public', 'response');
|
||||
|
||||
$this->setValue('public', $value, true);
|
||||
}
|
||||
|
||||
public function isPrivate()
|
||||
{
|
||||
$this->checkAttribute('private', 'response');
|
||||
|
||||
return array_key_exists('private', $this->attributes);
|
||||
}
|
||||
|
||||
public function getPrivate()
|
||||
{
|
||||
$this->checkAttribute('private', 'response');
|
||||
|
||||
return array_key_exists('private', $this->attributes) ? $this->attributes['private'] : false;
|
||||
}
|
||||
|
||||
public function setPrivate($value)
|
||||
{
|
||||
$this->checkAttribute('private', 'response');
|
||||
|
||||
$this->setValue('private', $value, true);
|
||||
}
|
||||
|
||||
public function isNoCache()
|
||||
{
|
||||
return array_key_exists('no-cache', $this->attributes);
|
||||
}
|
||||
|
||||
public function getNoCache()
|
||||
{
|
||||
return array_key_exists('no-cache', $this->attributes) ? $this->attributes['no-cache'] : false;
|
||||
}
|
||||
|
||||
public function setNoCache($value)
|
||||
{
|
||||
$this->setValue('no-cache', $value, true);
|
||||
}
|
||||
|
||||
public function isNoStore()
|
||||
{
|
||||
return array_key_exists('no-store', $this->attributes);
|
||||
}
|
||||
|
||||
public function setNoStore($value)
|
||||
{
|
||||
$this->setValue('no-store', $value, true);
|
||||
}
|
||||
|
||||
public function isNoTransform()
|
||||
{
|
||||
return array_key_exists('no-tranform', $this->attributes);
|
||||
}
|
||||
|
||||
public function setNoTransform($value)
|
||||
{
|
||||
$this->setValue('no-transform', $value, true);
|
||||
}
|
||||
|
||||
public function getMaxAge()
|
||||
{
|
||||
return array_key_exists('max-age', $this->attributes) ? $this->attributes['max-age'] : null;
|
||||
}
|
||||
|
||||
public function setMaxAge($age)
|
||||
{
|
||||
$this->setValue('max-age', (integer) $age);
|
||||
}
|
||||
|
||||
public function getSharedMaxAge()
|
||||
{
|
||||
$this->checkAttribute('s-maxage', 'response');
|
||||
|
||||
return array_key_exists('s-maxage', $this->attributes) ? $this->attributes['s-maxage'] : null;
|
||||
}
|
||||
|
||||
public function setSharedMaxAge($age)
|
||||
{
|
||||
$this->checkAttribute('s-maxage', 'response');
|
||||
|
||||
$this->setValue('s-maxage', (integer) $age);
|
||||
}
|
||||
|
||||
public function mustRevalidate()
|
||||
{
|
||||
$this->checkAttribute('must-revalidate', 'response');
|
||||
|
||||
return array_key_exists('must-revalidate', $this->attributes);
|
||||
}
|
||||
|
||||
public function setMustRevalidate($value)
|
||||
{
|
||||
$this->checkAttribute('must-revalidate', 'response');
|
||||
|
||||
$this->setValue('must-revalidate', $value);
|
||||
}
|
||||
|
||||
public function mustProxyRevalidate()
|
||||
{
|
||||
$this->checkAttribute('proxy-revalidate', 'response');
|
||||
|
||||
return array_key_exists('proxy-revalidate', $this->attributes);
|
||||
}
|
||||
|
||||
public function setProxyRevalidate($value)
|
||||
{
|
||||
$this->checkAttribute('proxy-revalidate', 'response');
|
||||
|
||||
$this->setValue('proxy-revalidate', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a Cache-Control HTTP header.
|
||||
*
|
||||
* @param string $header The value of the Cache-Control HTTP header
|
||||
*
|
||||
* @param array An array representing the attribute values
|
||||
*/
|
||||
protected function parse($header)
|
||||
{
|
||||
$attributes = array();
|
||||
preg_match_all('#([a-zA-Z][a-zA-Z_-]*)\s*(?:=(?:"([^"]*)"|([^ \t",;]*)))?#', $header, $matches, PREG_SET_ORDER);
|
||||
foreach ($matches as $match)
|
||||
{
|
||||
$attributes[strtolower($match[1])] = isset($match[2]) && $match[2] ? $match[2] : (isset($match[3]) ? $match[3] : true);
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
protected function setValue($key, $value, $isBoolean = false)
|
||||
{
|
||||
if (false === $value)
|
||||
{
|
||||
unset($this->attributes[$key]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->attributes[$key] = $isBoolean ? true : $value;
|
||||
}
|
||||
|
||||
$this->bag->set('Cache-Control', (string) $this);
|
||||
}
|
||||
|
||||
protected function checkAttribute($name, $expected)
|
||||
{
|
||||
if (null !== $this->type && $expected !== $this->type)
|
||||
{
|
||||
throw new \LogicException(sprintf("The property %s only applies to the %s Cache-Control.", $name, $expected));
|
||||
}
|
||||
}
|
||||
}
|
134
src/Symfony/Components/RequestHandler/HeaderBag.php
Normal file
134
src/Symfony/Components/RequestHandler/HeaderBag.php
Normal file
@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Components\RequestHandler;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* HeaderBag is a ParameterBag optimized to hold header HTTP headers.
|
||||
*
|
||||
* @package Symfony
|
||||
* @subpackage Components_RequestHandler
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class HeaderBag extends ParameterBag
|
||||
{
|
||||
protected $cacheControl;
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* Replaces the current HTTP headers by a new set.
|
||||
*
|
||||
* @param array $parameters An array of HTTP headers
|
||||
* @param string $type The type (null, request, or response)
|
||||
*/
|
||||
public function replace(array $parameters = array(), $type = null)
|
||||
{
|
||||
$this->cacheControl = null;
|
||||
$this->parameters = array();
|
||||
foreach ($parameters as $key => $value)
|
||||
{
|
||||
$this->parameters[strtr(strtolower($key), '_', '-')] = $value;
|
||||
}
|
||||
|
||||
if (null !== $type && !in_array($type, array('request', 'response')))
|
||||
{
|
||||
throw new \InvalidArgumentException(sprintf('The "%s" type is not supported by the HeaderBag constructor.', $type));
|
||||
}
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a header value by name.
|
||||
*
|
||||
* @param string $key The key
|
||||
* @param mixed $default The default value
|
||||
*/
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
$key = strtr(strtolower($key), '_', '-');
|
||||
|
||||
return array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a header by name.
|
||||
*
|
||||
* @param string $key The key
|
||||
* @param mixed $value The value
|
||||
* @param Boolean $replace Whether to replace the actual value of not (true by default)
|
||||
*/
|
||||
public function set($key, $value, $replace = true)
|
||||
{
|
||||
$key = strtr(strtolower($key), '_', '-');
|
||||
|
||||
if (false === $replace)
|
||||
{
|
||||
$current = $this->get($key, '');
|
||||
$value = ($current ? $current.', ' : '').$value;
|
||||
}
|
||||
|
||||
$this->parameters[$key] = $value;
|
||||
|
||||
if ('cache-control' == $key)
|
||||
{
|
||||
$this->cacheControl = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the HTTP header is defined.
|
||||
*
|
||||
* @param string $key The HTTP header
|
||||
*
|
||||
* @return Boolean true if the parameter exists, false otherwise
|
||||
*/
|
||||
public function has($key)
|
||||
{
|
||||
return array_key_exists(strtr(strtolower($key), '_', '-'), $this->parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a header.
|
||||
*
|
||||
* @param string $key The HTTP header name
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
unset($this->parameters[strtr(strtolower($key), '_', '-')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance able to manage the Cache-Control header.
|
||||
*
|
||||
* @return Symfony\Components\RequestHandler\Cache\CacheControl A CacheControl instance
|
||||
*/
|
||||
public function getCacheControl()
|
||||
{
|
||||
if (null === $this->cacheControl)
|
||||
{
|
||||
$this->cacheControl = new CacheControl($this, $this->get('Cache-Control'), $this->type);
|
||||
}
|
||||
|
||||
return $this->cacheControl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a HTTP header name.
|
||||
*
|
||||
* @param string $key The HTTP header name
|
||||
*
|
||||
* @return string The normalized HTTP header name
|
||||
*/
|
||||
static public function normalizeHeaderName($key)
|
||||
{
|
||||
return strtr(strtolower($key), '_', '-');
|
||||
}
|
||||
}
|
@ -12,34 +12,44 @@ namespace Symfony\Components\RequestHandler;
|
||||
*/
|
||||
|
||||
/**
|
||||
* RequestBag is a container for key/value pairs.
|
||||
* ParameterBag is a container for key/value pairs.
|
||||
*
|
||||
* @package Symfony
|
||||
* @subpackage Components_RequestHandler
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class RequestBag
|
||||
class ParameterBag
|
||||
{
|
||||
protected $input;
|
||||
protected $parameters;
|
||||
|
||||
public function __construct($input)
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $parameters An array of parameters
|
||||
*/
|
||||
public function __construct(array $parameters = array())
|
||||
{
|
||||
$this->replace($input);
|
||||
$this->replace($parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the input.
|
||||
* Returns the parameters.
|
||||
*
|
||||
* @return array An array of input
|
||||
* @return array An array of parameters
|
||||
*/
|
||||
public function all()
|
||||
{
|
||||
return $this->input;
|
||||
return $this->parameters;
|
||||
}
|
||||
|
||||
public function replace($input)
|
||||
/**
|
||||
* Replaces the current parameters by a new set.
|
||||
*
|
||||
* @param array $parameters An array of parameters
|
||||
*/
|
||||
public function replace(array $parameters = array())
|
||||
{
|
||||
$this->input = $input;
|
||||
$this->parameters = $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -50,7 +60,7 @@ class RequestBag
|
||||
*/
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
return array_key_exists($key, $this->input) ? $this->input[$key] : $default;
|
||||
return array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,14 +69,21 @@ class RequestBag
|
||||
* @param string $key The key
|
||||
* @param mixed $value The value
|
||||
*/
|
||||
public function set($key, $value)
|
||||
public function set($key, $value, $replace = true)
|
||||
{
|
||||
$this->input[$key] = $value;
|
||||
$this->parameters[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the parameter is defined.
|
||||
*
|
||||
* @param string $key The key
|
||||
*
|
||||
* @return Boolean true if the parameter exists, false otherwise
|
||||
*/
|
||||
public function has($key)
|
||||
{
|
||||
return array_key_exists($key, $this->input);
|
||||
return array_key_exists($key, $this->parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -76,7 +93,7 @@ class RequestBag
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
unset($this->input[$key]);
|
||||
unset($this->parameters[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -130,4 +147,19 @@ class RequestBag
|
||||
{
|
||||
return (int) $this->get($key, $default);
|
||||
}
|
||||
|
||||
public function getDate($key, \DateTime $default = null)
|
||||
{
|
||||
if (null === $value = $this->get($key))
|
||||
{
|
||||
return $default;
|
||||
}
|
||||
|
||||
if (false === $date = \DateTime::createFromFormat(DATE_RFC2822, $value))
|
||||
{
|
||||
throw new \RuntimeException(sprintf('The %s HTTP header is not parseable (%s).', $key, $value));
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
}
|
@ -12,12 +12,7 @@ namespace Symfony\Components\RequestHandler;
|
||||
*/
|
||||
|
||||
/**
|
||||
* Request is the base implementation of a user request.
|
||||
*
|
||||
* After initialization, the request is read-only. The only writable
|
||||
* values are the query ones (mostly by the router).
|
||||
*
|
||||
* You can reinitialize the request by calling the initialize() method.
|
||||
* Request represents an HTTP request.
|
||||
*
|
||||
* @package Symfony
|
||||
* @subpackage Components_RequestHandler
|
||||
@ -75,13 +70,13 @@ class Request
|
||||
*/
|
||||
public function initialize(array $query = null, array $request = null, array $path = null, array $cookies = null, array $files = null, array $server = null)
|
||||
{
|
||||
$this->request = new RequestBag(null !== $request ? $request : $_POST);
|
||||
$this->query = new RequestBag(null !== $query ? $query : $_GET);
|
||||
$this->path = new RequestBag(null !== $path ? $path : array());
|
||||
$this->cookies = new RequestBag(null !== $cookies ? $cookies : $_COOKIE);
|
||||
$this->files = new RequestBag($this->convertFileInformation(null !== $files ? $files : $_FILES));
|
||||
$this->server = new RequestBag(null !== $server ? $server : $_SERVER);
|
||||
$this->headers = new RequestBag($this->initializeHeaders());
|
||||
$this->request = new ParameterBag(null !== $request ? $request : $_POST);
|
||||
$this->query = new ParameterBag(null !== $query ? $query : $_GET);
|
||||
$this->path = new ParameterBag(null !== $path ? $path : array());
|
||||
$this->cookies = new ParameterBag(null !== $cookies ? $cookies : $_COOKIE);
|
||||
$this->files = new ParameterBag($this->convertFileInformation(null !== $files ? $files : $_FILES));
|
||||
$this->server = new HeaderBag(null !== $server ? $server : $_SERVER, 'request');
|
||||
$this->headers = new HeaderBag($this->initializeHeaders(), 'request');
|
||||
|
||||
$this->languages = null;
|
||||
$this->charsets = null;
|
||||
@ -107,12 +102,26 @@ class Request
|
||||
*
|
||||
* @return Request A Request instance
|
||||
*/
|
||||
static public function createFromUri($uri, $method = 'get', $parameters = array(), $cookies = array(), $files = array(), $server = array())
|
||||
static public function create($uri, $method = 'get', $parameters = array(), $cookies = array(), $files = array(), $server = array())
|
||||
{
|
||||
if (in_array($method, array('post', 'put', 'delete')))
|
||||
$defaults = array(
|
||||
'SERVER_NAME' => 'localhost',
|
||||
'SERVER_PORT' => 80,
|
||||
'HTTP_HOST' => 'localhost',
|
||||
'HTTP_USER_AGENT' => 'Symfony/X.X',
|
||||
'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
||||
'HTTP_ACCEPT_LANGUAGE' => 'en-us,en;q=0.5',
|
||||
'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
|
||||
'REMOTE_ADDR' => '127.0.0.1',
|
||||
'SCRIPT_NAME' => '',
|
||||
'SCRIPT_FILENAME' => '',
|
||||
);
|
||||
|
||||
if (in_array(strtolower($method), array('post', 'put', 'delete')))
|
||||
{
|
||||
$request = $parameters;
|
||||
$query = array();
|
||||
$defaults['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -127,18 +136,7 @@ class Request
|
||||
$query = array_replace($qs, $query);
|
||||
}
|
||||
|
||||
$server = array_replace(array(
|
||||
'SERVER_NAME' => 'localhost',
|
||||
'SERVER_PORT' => 80,
|
||||
'HTTP_HOST' => 'localhost',
|
||||
'HTTP_USER_AGENT' => 'Symfony/X.X',
|
||||
'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
||||
'HTTP_ACCEPT_LANGUAGE' => 'en-us,en;q=0.5',
|
||||
'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
|
||||
'REMOTE_ADDR' => '127.0.0.1',
|
||||
'SCRIPT_NAME' => '',
|
||||
'SCRIPT_FILENAME' => '',
|
||||
), $server, array(
|
||||
$server = array_replace($defaults, $server, array(
|
||||
'REQUEST_METHOD' => strtoupper($method),
|
||||
'PATH_INFO' => '',
|
||||
'REQUEST_URI' => $uri,
|
||||
@ -426,6 +424,16 @@ class Request
|
||||
return $this->format;
|
||||
}
|
||||
|
||||
public function isMethodSafe()
|
||||
{
|
||||
return in_array(strtolower($this->getMethod()), array('get', 'head'));
|
||||
}
|
||||
|
||||
public function isNoCache()
|
||||
{
|
||||
return $this->headers->getCacheControl()->isNoCache() || 'no-cache' == $this->headers->get('Pragma');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the preferred language.
|
||||
*
|
||||
@ -464,7 +472,7 @@ class Request
|
||||
return $this->languages;
|
||||
}
|
||||
|
||||
$languages = $this->splitHttpAcceptHeader($this->headers->get('ACCEPT_LANGUAGE'));
|
||||
$languages = $this->splitHttpAcceptHeader($this->headers->get('Accept-Language'));
|
||||
foreach ($languages as $lang)
|
||||
{
|
||||
if (strstr($lang, '-'))
|
||||
@ -514,7 +522,7 @@ class Request
|
||||
return $this->charsets;
|
||||
}
|
||||
|
||||
return $this->charsets = $this->splitHttpAcceptHeader($this->headers->get('ACCEPT_CHARSET'));
|
||||
return $this->charsets = $this->splitHttpAcceptHeader($this->headers->get('Accept-Charset'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -529,7 +537,7 @@ class Request
|
||||
return $this->acceptableContentTypes;
|
||||
}
|
||||
|
||||
return $this->acceptableContentTypes = $this->splitHttpAcceptHeader($this->headers->get('ACCEPT'));
|
||||
return $this->acceptableContentTypes = $this->splitHttpAcceptHeader($this->headers->get('Accept'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -542,7 +550,7 @@ class Request
|
||||
*/
|
||||
public function isXmlHttpRequest()
|
||||
{
|
||||
return 'XMLHttpRequest' == $this->headers->get('X_REQUESTED_WITH');
|
||||
return 'XMLHttpRequest' == $this->headers->get('X-Requested-With');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -785,9 +793,9 @@ class Request
|
||||
$headers = array();
|
||||
foreach ($this->server->all() as $key => $value)
|
||||
{
|
||||
if ('http_' === strtolower(substr($key, 0, 5)))
|
||||
if ('http-' === strtolower(substr($key, 0, 5)))
|
||||
{
|
||||
$headers[strtoupper(strtr(substr($key, 5), '-', '_'))] = $value;
|
||||
$headers[substr($key, 5)] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,11 +20,12 @@ namespace Symfony\Components\RequestHandler;
|
||||
*/
|
||||
class Response
|
||||
{
|
||||
public $headers;
|
||||
|
||||
protected $content;
|
||||
protected $version;
|
||||
protected $statusCode;
|
||||
protected $statusText;
|
||||
protected $headers;
|
||||
protected $cookies;
|
||||
|
||||
static public $statusTexts = array(
|
||||
@ -82,11 +83,7 @@ class Response
|
||||
$this->setContent($content);
|
||||
$this->setStatusCode($status);
|
||||
$this->setProtocolVersion('1.0');
|
||||
$this->headers = array();
|
||||
foreach ($headers as $name => $value)
|
||||
{
|
||||
$this->setHeader($name, $value);
|
||||
}
|
||||
$this->headers = new HeaderBag($headers, 'response');
|
||||
$this->cookies = array();
|
||||
}
|
||||
|
||||
@ -102,6 +99,57 @@ class Response
|
||||
return (string) $this->getContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones the current Response instance.
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
$this->headers = clone $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends HTTP headers, including cookies.
|
||||
*/
|
||||
public function sendHeaders()
|
||||
{
|
||||
if (!$this->headers->has('Content-Type'))
|
||||
{
|
||||
$this->headers->set('Content-Type', 'text/html');
|
||||
}
|
||||
|
||||
// status
|
||||
header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText));
|
||||
|
||||
// headers
|
||||
foreach ($this->headers->all() as $name => $value)
|
||||
{
|
||||
header($name.': '.$value);
|
||||
}
|
||||
|
||||
// cookies
|
||||
foreach ($this->cookies as $cookie)
|
||||
{
|
||||
setrawcookie($cookie['name'], $cookie['value'], $cookie['expire'], $cookie['path'], $cookie['domain'], $cookie['secure'], $cookie['httpOnly']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends content for the current web response.
|
||||
*/
|
||||
public function sendContent()
|
||||
{
|
||||
echo $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends HTTP headers and content.
|
||||
*/
|
||||
public function send()
|
||||
{
|
||||
$this->sendHeaders();
|
||||
$this->sendContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the response content
|
||||
*
|
||||
@ -222,126 +270,4 @@ class Response
|
||||
{
|
||||
return $this->statusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a HTTP header.
|
||||
*
|
||||
* @param string $name HTTP header name
|
||||
* @param string $value Value (if null, remove the HTTP header)
|
||||
* @param bool $replace Replace for the value
|
||||
*
|
||||
*/
|
||||
public function setHeader($name, $value, $replace = true)
|
||||
{
|
||||
$name = $this->normalizeHeaderName($name);
|
||||
|
||||
if (null === $value)
|
||||
{
|
||||
unset($this->headers[$name]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$replace)
|
||||
{
|
||||
$current = isset($this->headers[$name]) ? $this->headers[$name] : '';
|
||||
$value = ($current ? $current.', ' : '').$value;
|
||||
}
|
||||
|
||||
$this->headers[$name] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets HTTP header current value.
|
||||
*
|
||||
* @param string $name HTTP header name
|
||||
* @param string $default Default value returned if named HTTP header is not found
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getHeader($name, $default = null)
|
||||
{
|
||||
$name = $this->normalizeHeaderName($name);
|
||||
|
||||
return isset($this->headers[$name]) ? $this->headers[$name] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the response has given HTTP header.
|
||||
*
|
||||
* @param string $name HTTP header name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasHeader($name)
|
||||
{
|
||||
return array_key_exists($this->normalizeHeaderName($name), $this->headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves HTTP headers from the current web response.
|
||||
*
|
||||
* @return string HTTP headers
|
||||
*/
|
||||
public function getHeaders()
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends HTTP headers, including cookies.
|
||||
*/
|
||||
public function sendHeaders()
|
||||
{
|
||||
if (!$this->hasHeader('Content-Type'))
|
||||
{
|
||||
$this->setHeader('Content-Type', 'text/html');
|
||||
}
|
||||
|
||||
// status
|
||||
header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText));
|
||||
|
||||
// headers
|
||||
foreach ($this->headers as $name => $value)
|
||||
{
|
||||
header($name.': '.$value);
|
||||
}
|
||||
|
||||
// cookies
|
||||
foreach ($this->cookies as $cookie)
|
||||
{
|
||||
setrawcookie($cookie['name'], $cookie['value'], $cookie['expire'], $cookie['path'], $cookie['domain'], $cookie['secure'], $cookie['httpOnly']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends content for the current web response.
|
||||
*/
|
||||
public function sendContent()
|
||||
{
|
||||
echo $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends HTTP headers and content.
|
||||
*/
|
||||
public function send()
|
||||
{
|
||||
$this->sendHeaders();
|
||||
$this->sendContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a HTTP header name.
|
||||
*
|
||||
* @param string $name The HTTP header name
|
||||
*
|
||||
* @return string The normalized HTTP header name
|
||||
*/
|
||||
protected function normalizeHeaderName($name)
|
||||
{
|
||||
return strtr(strtolower($name), '_', '-');
|
||||
}
|
||||
}
|
||||
|
@ -156,7 +156,7 @@ EOF;
|
||||
$uri = '/'.$matches[2];
|
||||
}
|
||||
|
||||
return Request::createFromUri($uri, $request->getMethod(), $request->getParameters(), $request->getFiles(), $request->getCookies(), $request->getServer());
|
||||
return Request::create($uri, $request->getMethod(), $request->getParameters(), $request->getFiles(), $request->getCookies(), $request->getServer());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -168,7 +168,7 @@ EOF;
|
||||
*/
|
||||
protected function filterResponse($response)
|
||||
{
|
||||
return new DomResponse($response->getContent(), $response->getStatusCode(), $response->getHeaders(), $response->getCookies());
|
||||
return new DomResponse($response->getContent(), $response->getStatusCode(), $response->headers, $response->getCookies());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -38,7 +38,7 @@ class ResponseTester extends Tester
|
||||
if (class_exists('Symfony\Components\DomCrawler\Crawler'))
|
||||
{
|
||||
$this->crawler = new Crawler();
|
||||
$this->crawler->addContent($this->response->getContent(), $this->response->getHeader('Content-Type'));
|
||||
$this->crawler->addContent($this->response->getContent(), $this->response->headers->get('Content-Type'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,16 +121,16 @@ class ResponseTester extends Tester
|
||||
*/
|
||||
public function assertHeaderEquals($key, $value)
|
||||
{
|
||||
$headers = explode(', ', $this->response->getHeader($key));
|
||||
$headers = explode(', ', $this->response->headers->get($key));
|
||||
foreach ($headers as $header)
|
||||
{
|
||||
if ($header == $value)
|
||||
{
|
||||
return $this->test->pass(sprintf('Response header "%s" is "%s" (%s)', $key, $value, $this->response->getHeader($key)));
|
||||
return $this->test->pass(sprintf('Response header "%s" is "%s" (%s)', $key, $value, $this->response->headers->get($key)));
|
||||
}
|
||||
}
|
||||
|
||||
$this->test->fail(sprintf('Response header "%s" matches "%s" (%s)', $key, $value, $this->response->getHeader($key)));
|
||||
$this->test->fail(sprintf('Response header "%s" matches "%s" (%s)', $key, $value, $this->response->headers->get($key)));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -141,16 +141,16 @@ class ResponseTester extends Tester
|
||||
*/
|
||||
public function assertNotHeaderEquals($key, $value)
|
||||
{
|
||||
$headers = explode(', ', $this->response->getHeader($key));
|
||||
$headers = explode(', ', $this->response->headers->get($key));
|
||||
foreach ($headers as $header)
|
||||
{
|
||||
if ($header == $value)
|
||||
{
|
||||
return $this->test->fail(sprintf('Response header "%s" is not "%s" (%s)', $key, $value, $this->response->getHeader($key)));
|
||||
return $this->test->fail(sprintf('Response header "%s" is not "%s" (%s)', $key, $value, $this->response->headers->get($key)));
|
||||
}
|
||||
}
|
||||
|
||||
$this->test->pass(sprintf('Response header "%s" does not match "%s" (%s)', $key, $value, $this->response->getHeader($key)));
|
||||
$this->test->pass(sprintf('Response header "%s" does not match "%s" (%s)', $key, $value, $this->response->headers->get($key)));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -161,16 +161,16 @@ class ResponseTester extends Tester
|
||||
*/
|
||||
public function assertHeaderRegExp($key, $regex)
|
||||
{
|
||||
$headers = explode(', ', $this->response->getHeader($key));
|
||||
$headers = explode(', ', $this->response->headers->get($key));
|
||||
foreach ($headers as $header)
|
||||
{
|
||||
if (preg_match($regex, $header))
|
||||
{
|
||||
return $this->test->pass(sprintf('Response header "%s" matches "%s" (%s)', $key, $value, $this->response->getHeader($key)));
|
||||
return $this->test->pass(sprintf('Response header "%s" matches "%s" (%s)', $key, $value, $this->response->headers->get($key)));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->test->fail(sprintf('Response header "%s" matches "%s" (%s)', $key, $value, $this->response->getHeader($key)));
|
||||
return $this->test->fail(sprintf('Response header "%s" matches "%s" (%s)', $key, $value, $this->response->headers->get($key)));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -181,16 +181,16 @@ class ResponseTester extends Tester
|
||||
*/
|
||||
public function assertNotHeaderRegExp($key, $regex)
|
||||
{
|
||||
$headers = explode(', ', $this->response->getHeader($key));
|
||||
$headers = explode(', ', $this->response->headers->get($key));
|
||||
foreach ($headers as $header)
|
||||
{
|
||||
if (!preg_match($regex, $header))
|
||||
{
|
||||
return $this->test->pass(sprintf('Response header "%s" matches "%s" (%s)', $key, $value, $this->response->getHeader($key)));
|
||||
return $this->test->pass(sprintf('Response header "%s" matches "%s" (%s)', $key, $value, $this->response->headers->get($key)));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->test->fail(sprintf('Response header "%s" matches "%s" (%s)', $key, $value, $this->response->getHeader($key)));
|
||||
return $this->test->fail(sprintf('Response header "%s" matches "%s" (%s)', $key, $value, $this->response->headers->get($key)));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -337,7 +337,7 @@ class ResponseTester extends Tester
|
||||
|
||||
if (null !== $location)
|
||||
{
|
||||
$this->test->assertEquals($location, $this->response->getHeader('Location'), sprintf('Page redirected to "%s"', $location));
|
||||
$this->test->assertEquals($location, $this->response->headers->get('Location'), sprintf('Page redirected to "%s"', $location));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ class AppDataCollector extends DataCollector
|
||||
return array(
|
||||
'route' => $request->path->get('_route') ? $request->path->get('_route') : '<span style="color: #a33">NONE</span>',
|
||||
'format' => $request->getRequestFormat(),
|
||||
'content_type' => $this->manager->getResponse()->getHeader('Content-Type') ? $this->manager->getResponse()->getHeader('Content-Type') : 'text/html',
|
||||
'content_type' => $this->manager->getResponse()->headers->get('Content-Type') ? $this->manager->getResponse()->headers->get('Content-Type') : 'text/html',
|
||||
'code' => $this->manager->getResponse()->getStatusCode(),
|
||||
);
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ class WebDebugToolbar
|
||||
if (
|
||||
'3' === substr($response->getStatusCode(), 0, 1)
|
||||
||
|
||||
($response->hasHeader('Content-Type') && false === strpos($response->getHeader('Content-Type'), 'html'))
|
||||
($response->headers->has('Content-Type') && false === strpos($response->headers->get('Content-Type'), 'html'))
|
||||
||
|
||||
'html' !== $request->getRequestFormat()
|
||||
||
|
||||
|
@ -64,7 +64,7 @@ class Controller
|
||||
$response->setStatusCode($status);
|
||||
foreach ($headers as $name => $value)
|
||||
{
|
||||
$response->setHeader($name, $value);
|
||||
$response->headers->set($name, $value);
|
||||
}
|
||||
|
||||
return $response;
|
||||
@ -96,7 +96,7 @@ class Controller
|
||||
{
|
||||
$response = $this->container->getResponseService();
|
||||
$response->setStatusCode($status);
|
||||
$response->setHeader('Location', $url);
|
||||
$response->headers->set('Location', $url);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ class ControllerLoader
|
||||
$controller[0]->setRequest($request);
|
||||
|
||||
$r = new \ReflectionObject($controller[0]);
|
||||
$arguments = $this->getMethodArguments($r->getMethod($controller[1]), $event->getParameter('request')->path->all(), sprintf('%s::%s()', get_class($controller[0]), $controller[1]));
|
||||
$arguments = $this->getMethodArguments($r->getMethod($controller[1]), $request->path->all(), sprintf('%s::%s()', get_class($controller[0]), $controller[1]));
|
||||
|
||||
$event->setReturnValue(array($controller, $arguments));
|
||||
|
||||
|
@ -39,7 +39,7 @@ class ResponseFilter
|
||||
|
||||
public function filter(Event $event, Response $response)
|
||||
{
|
||||
if (!$event->getParameter('main_request') || $response->hasHeader('Content-Type'))
|
||||
if (!$event->getParameter('main_request') || $response->headers->has('Content-Type'))
|
||||
{
|
||||
return $response;
|
||||
}
|
||||
@ -48,7 +48,7 @@ class ResponseFilter
|
||||
$format = $request->getRequestFormat();
|
||||
if ((null !== $format) && $mimeType = $request->getMimeType($format))
|
||||
{
|
||||
$response->setHeader('Content-Type', $mimeType);
|
||||
$response->headers->set('Content-Type', $mimeType);
|
||||
}
|
||||
|
||||
return $response;
|
||||
|
@ -11,12 +11,12 @@
|
||||
|
||||
namespace Symfony\Tests\Components\RequestHandler;
|
||||
|
||||
use Symfony\Components\RequestHandler\RequestBag;
|
||||
use Symfony\Components\RequestHandler\ParameterBag;
|
||||
|
||||
class RequestBagTest extends \PHPUnit_Framework_TestCase
|
||||
class ParameterBagTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @covers Symfony\Components\RequestHandler\RequestBag::__construct
|
||||
* @covers Symfony\Components\RequestHandler\ParameterBag::__construct
|
||||
*/
|
||||
public function testConstructor()
|
||||
{
|
||||
@ -24,20 +24,20 @@ class RequestBagTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers Symfony\Components\RequestHandler\RequestBag::all
|
||||
* @covers Symfony\Components\RequestHandler\ParameterBag::all
|
||||
*/
|
||||
public function testAll()
|
||||
{
|
||||
$bag = new RequestBag(array('foo' => 'bar'));
|
||||
$bag = new ParameterBag(array('foo' => 'bar'));
|
||||
$this->assertEquals(array('foo' => 'bar'), $bag->all(), '->all() gets all the input');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers Symfony\Components\RequestHandler\RequestBag::replace
|
||||
* @covers Symfony\Components\RequestHandler\ParameterBag::replace
|
||||
*/
|
||||
public function testReplace()
|
||||
{
|
||||
$bag = new RequestBag(array('foo' => 'bar'));
|
||||
$bag = new ParameterBag(array('foo' => 'bar'));
|
||||
|
||||
$bag->replace(array('FOO' => 'BAR'));
|
||||
$this->assertEquals(array('FOO' => 'BAR'), $bag->all(), '->replace() replaces the input with the argument');
|
||||
@ -45,11 +45,11 @@ class RequestBagTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers Symfony\Components\RequestHandler\RequestBag::get
|
||||
* @covers Symfony\Components\RequestHandler\ParameterBag::get
|
||||
*/
|
||||
public function testGet()
|
||||
{
|
||||
$bag = new RequestBag(array('foo' => 'bar', 'null' => null));
|
||||
$bag = new ParameterBag(array('foo' => 'bar', 'null' => null));
|
||||
|
||||
$this->assertEquals('bar', $bag->get('foo'), '->get() gets the value of a parameter');
|
||||
$this->assertEquals('default', $bag->get('unknown', 'default'), '->get() returns second argument as default if a parameter is not defined');
|
||||
@ -57,11 +57,11 @@ class RequestBagTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers Symfony\Components\RequestHandler\RequestBag::set
|
||||
* @covers Symfony\Components\RequestHandler\ParameterBag::set
|
||||
*/
|
||||
public function testSet()
|
||||
{
|
||||
$bag = new RequestBag(array());
|
||||
$bag = new ParameterBag(array());
|
||||
|
||||
$bag->set('foo', 'bar');
|
||||
$this->assertEquals('bar', $bag->get('foo'), '->set() sets the value of parameter');
|
||||
@ -71,55 +71,55 @@ class RequestBagTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers Symfony\Components\RequestHandler\RequestBag::has
|
||||
* @covers Symfony\Components\RequestHandler\ParameterBag::has
|
||||
*/
|
||||
public function testHas()
|
||||
{
|
||||
$bag = new RequestBag(array('foo' => 'bar'));
|
||||
$bag = new ParameterBag(array('foo' => 'bar'));
|
||||
|
||||
$this->assertTrue($bag->has('foo'), '->has() returns true if a parameter is defined');
|
||||
$this->assertFalse($bag->has('unknown'), '->has() return false if a parameter is not defined');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers Symfony\Components\RequestHandler\RequestBag::getAlpha
|
||||
* @covers Symfony\Components\RequestHandler\ParameterBag::getAlpha
|
||||
*/
|
||||
public function testGetAlpha()
|
||||
{
|
||||
$bag = new RequestBag(array('word' => 'foo_BAR_012'));
|
||||
$bag = new ParameterBag(array('word' => 'foo_BAR_012'));
|
||||
|
||||
$this->assertEquals('fooBAR', $bag->getAlpha('word'), '->getAlpha() gets only alphabetic characters');
|
||||
$this->assertEquals('', $bag->getAlpha('unknown'), '->getAlpha() returns empty string if a parameter is not defined');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers Symfony\Components\RequestHandler\RequestBag::getAlnum
|
||||
* @covers Symfony\Components\RequestHandler\ParameterBag::getAlnum
|
||||
*/
|
||||
public function testGetAlnum()
|
||||
{
|
||||
$bag = new RequestBag(array('word' => 'foo_BAR_012'));
|
||||
$bag = new ParameterBag(array('word' => 'foo_BAR_012'));
|
||||
|
||||
$this->assertEquals('fooBAR012', $bag->getAlnum('word'), '->getAlnum() gets only alphanumeric characters');
|
||||
$this->assertEquals('', $bag->getAlnum('unknown'), '->getAlnum() returns empty string if a parameter is not defined');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers Symfony\Components\RequestHandler\RequestBag::getDigits
|
||||
* @covers Symfony\Components\RequestHandler\ParameterBag::getDigits
|
||||
*/
|
||||
public function testGetDigits()
|
||||
{
|
||||
$bag = new RequestBag(array('word' => 'foo_BAR_012'));
|
||||
$bag = new ParameterBag(array('word' => 'foo_BAR_012'));
|
||||
|
||||
$this->assertEquals('012', $bag->getDigits('word'), '->getDigits() gets only digits as string');
|
||||
$this->assertEquals('', $bag->getDigits('unknown'), '->getDigits() returns empty string if a parameter is not defined');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers Symfony\Components\RequestHandler\RequestBag::getInt
|
||||
* @covers Symfony\Components\RequestHandler\ParameterBag::getInt
|
||||
*/
|
||||
public function testGetInt()
|
||||
{
|
||||
$bag = new RequestBag(array('digits' => '0123'));
|
||||
$bag = new ParameterBag(array('digits' => '0123'));
|
||||
|
||||
$this->assertEquals(123, $bag->getInt('digits'), '->getInt() gets a value of parameter as integer');
|
||||
$this->assertEquals(0, $bag->getInt('unknown'), '->getInt() returns zero if a parameter is not defined');
|
@ -61,7 +61,7 @@ class RequestTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals(array('foo' => 'foobar'), $dup->query->all(), '->duplicate() overrides the query parameters if provided');
|
||||
$this->assertEquals(array('foo' => 'foobar'), $dup->request->all(), '->duplicate() overrides the request parameters if provided');
|
||||
$this->assertEquals(array('foo' => 'foobar'), $dup->path->all(), '->duplicate() overrides the path parameters if provided');
|
||||
$this->assertEquals(array('FOO' => 'foobar'), $dup->headers->all(), '->duplicate() overrides the HTTP header if provided');
|
||||
$this->assertEquals(array('foo' => 'foobar'), $dup->headers->all(), '->duplicate() overrides the HTTP header if provided');
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user