From 28497cdfe379b2e5a185c7857270f32aa035e641 Mon Sep 17 00:00:00 2001 From: Andrey Mikheychik Date: Sun, 8 Feb 2015 17:59:08 -0500 Subject: [PATCH 1/2] RequestInterface added --- .../HttpFoundation/RequestInterface.php | 194 ++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 src/Symfony/Component/HttpFoundation/RequestInterface.php diff --git a/src/Symfony/Component/HttpFoundation/RequestInterface.php b/src/Symfony/Component/HttpFoundation/RequestInterface.php new file mode 100644 index 0000000000000..79e1d01abfa0d --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/RequestInterface.php @@ -0,0 +1,194 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +interface RequestInterface +{ + /** + * Gets the request attributes. + * + * @return \Symfony\Component\HttpFoundation\ParameterBag + */ + public function getAttributes(); + + /** + * Returns the client IP address. + * + * This method can read the client IP address from the "X-Forwarded-For" header + * when trusted proxies were set via "setTrustedProxies()". The "X-Forwarded-For" + * header value is a comma+space separated list of IP addresses, the left-most + * being the original client, and each successive proxy that passed the request + * adding the IP address where it received the request from. + * + * If your reverse proxy uses a different header name than "X-Forwarded-For", + * ("Client-Ip" for instance), configure it via "setTrustedHeaderName()" with + * the "client-ip" key. + * + * @return string The client IP address + * + * @see getClientIps() + * @see http://en.wikipedia.org/wiki/X-Forwarded-For + * + * @api + */ + public function getClientIp(); + + /** + * Gets the Etags. + * + * @return array The entity tags + */ + public function getETags(); + + /** + * Gets the request headers. + * + * @return \Symfony\Component\HttpFoundation\HeaderBag + */ + public function getHeaders(); + + /** + * Returns the host name. + * + * This method can read the client port from the "X-Forwarded-Host" header + * when trusted proxies were set via "setTrustedProxies()". + * + * The "X-Forwarded-Host" header must contain the client host name. + * + * If your reverse proxy uses a different header name than "X-Forwarded-Host", + * configure it via "setTrustedHeaderName()" with the "client-host" key. + * + * @return string + * + * @throws \UnexpectedValueException when the host name is invalid + * + * @api + */ + public function getHost(); + + /** + * Gets the request "intended" method. + * + * If the X-HTTP-Method-Override header is set, and if the method is a POST, + * then it is used to determine the "real" intended HTTP method. + * + * The _method request parameter can also be used to determine the HTTP method, + * but only if enableHttpMethodParameterOverride() has been called. + * + * The method is always an uppercased string. + * + * @return string The request method + * + * @api + * + * @see getRealMethod() + */ + public function getMethod(); + + /** + * Gets the mime type associated with the format. + * + * @param string $format The format + * + * @return string The associated mime type (null if not found) + * + * @api + */ + public function getMimeType($format); + + /** + * Returns the path being requested relative to the executed script. + * + * The path info always starts with a /. + * + * Suppose this request is instantiated from /mysite on localhost: + * + * * http://localhost/mysite returns an empty string + * * http://localhost/mysite/about returns '/about' + * * http://localhost/mysite/enco%20ded returns '/enco%20ded' + * * http://localhost/mysite/about?var=1 returns '/about' + * + * @return string The raw path (i.e. not urldecoded) + * + * @api + */ + public function getPathInfo(); + + /** + * Gets the request format. + * + * Here is the process to determine the format: + * + * * format defined by the user (with setRequestFormat()) + * * _format request parameter + * * $default + * + * @param string $default The default format + * + * @return string The request format + * + * @api + */ + public function getRequestFormat($default = 'html'); + + /** + * Gets the request's scheme. + * + * @return string + * + * @api + */ + public function getScheme(); + + /** + * Gets the request server. + * + * @return \Symfony\Component\HttpFoundation\ServerBag + */ + public function getServer(); + + /** + * Checks if the request method is of specified type. + * + * @param string $method Uppercase request method (GET, POST etc). + * + * @return bool + */ + public function isMethod($method); + + /** + * Checks whether the method is safe or not. + * + * @return bool + * + * @api + */ + public function isMethodSafe(); + + /** + * Checks whether the request is secure or not. + * + * This method can read the client port from the "X-Forwarded-Proto" header + * when trusted proxies were set via "setTrustedProxies()". + * + * The "X-Forwarded-Proto" header must contain the protocol: "https" or "http". + * + * If your reverse proxy uses a different header name than "X-Forwarded-Proto" + * ("SSL_HTTPS" for instance), configure it via "setTrustedHeaderName()" with + * the "client-proto" key. + * + * @return bool + * + * @api + */ + public function isSecure(); +} From 0e7b7592dbf0a17a5b88f13ae85f68ae9b238cf7 Mon Sep 17 00:00:00 2001 From: Andrey Mikheychik Date: Sun, 8 Feb 2015 18:01:33 -0500 Subject: [PATCH 2/2] Refactor: use RequestInterface insteaf of Request Some methods use Request class in type hints. It's inconvenient when a developer want to use his own class for request, without extending the original Symfony's one. --- .../HttpFoundation/BinaryFileResponse.php | 18 ++-- .../ExpressionRequestMatcher.php | 4 +- .../Component/HttpFoundation/Request.php | 85 ++++++++----------- .../HttpFoundation/RequestMatcher.php | 6 +- .../RequestMatcherInterface.php | 4 +- .../Component/HttpFoundation/RequestStack.php | 2 +- .../Component/HttpFoundation/Response.php | 19 +++-- 7 files changed, 63 insertions(+), 75 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php index 685642f147c59..484b7d57906da 100644 --- a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php +++ b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php @@ -167,7 +167,7 @@ public function setContentDisposition($disposition, $filename = '', $filenameFal /** * {@inheritdoc} */ - public function prepare(Request $request) + public function prepare(RequestInterface $request) { $this->headers->set('Content-Length', $this->file->getSize()); @@ -180,7 +180,7 @@ public function prepare(Request $request) $this->headers->set('Content-Type', $this->file->getMimeType() ?: 'application/octet-stream'); } - if ('HTTP/1.0' != $request->server->get('SERVER_PROTOCOL')) { + if ('HTTP/1.0' != $request->getServer()->get('SERVER_PROTOCOL')) { $this->setProtocolVersion('1.1'); } @@ -189,13 +189,13 @@ public function prepare(Request $request) $this->offset = 0; $this->maxlen = -1; - if (self::$trustXSendfileTypeHeader && $request->headers->has('X-Sendfile-Type')) { + if (self::$trustXSendfileTypeHeader && $request->getHeaders()->has('X-Sendfile-Type')) { // Use X-Sendfile, do not send any content. - $type = $request->headers->get('X-Sendfile-Type'); + $type = $request->getHeaders()->get('X-Sendfile-Type'); $path = $this->file->getRealPath(); if (strtolower($type) == 'x-accel-redirect') { // Do X-Accel-Mapping substitutions. - foreach (explode(',', $request->headers->get('X-Accel-Mapping', '')) as $mapping) { + foreach (explode(',', $request->getHeaders()->get('X-Accel-Mapping', '')) as $mapping) { $mapping = explode('=', $mapping, 2); if (2 == count($mapping)) { @@ -211,10 +211,12 @@ public function prepare(Request $request) } $this->headers->set($type, $path); $this->maxlen = 0; - } elseif ($request->headers->has('Range')) { + } elseif ($request->getHeaders()->has('Range')) { // Process the range headers. - if (!$request->headers->has('If-Range') || $this->getEtag() == $request->headers->get('If-Range')) { - $range = $request->headers->get('Range'); + if (!$request->getHeaders()->has('If-Range') + || $this->getEtag() == $request->getHeaders()->get('If-Range') + ) { + $range = $request->getHeaders()->get('Range'); $fileSize = $this->file->getSize(); list($start, $end) = explode('-', substr($range, 6), 2) + array(0); diff --git a/src/Symfony/Component/HttpFoundation/ExpressionRequestMatcher.php b/src/Symfony/Component/HttpFoundation/ExpressionRequestMatcher.php index e9c8441ce314b..773323f9758ce 100644 --- a/src/Symfony/Component/HttpFoundation/ExpressionRequestMatcher.php +++ b/src/Symfony/Component/HttpFoundation/ExpressionRequestMatcher.php @@ -29,7 +29,7 @@ public function setExpression(ExpressionLanguage $language, $expression) $this->expression = $expression; } - public function matches(Request $request) + public function matches(RequestInterface $request) { if (!$this->language) { throw new \LogicException('Unable to match the request as the expression language is not available.'); @@ -41,7 +41,7 @@ public function matches(Request $request) 'path' => rawurldecode($request->getPathInfo()), 'host' => $request->getHost(), 'ip' => $request->getClientIp(), - 'attributes' => $request->attributes->all(), + 'attributes' => $request->getAttributes()->all(), )) && parent::matches($request); } } diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index e809db04c9a41..e0b143c6d0cc3 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -28,7 +28,7 @@ * * @api */ -class Request +class Request implements RequestInterface { const HEADER_CLIENT_IP = 'client_ip'; const HEADER_CLIENT_HOST = 'client_host'; @@ -963,9 +963,7 @@ public function getBaseUrl() } /** - * Gets the request's scheme. - * - * @return string + * {@inheritdoc} * * @api */ @@ -1210,18 +1208,7 @@ public function getQueryString() } /** - * Checks whether the request is secure or not. - * - * This method can read the client port from the "X-Forwarded-Proto" header - * when trusted proxies were set via "setTrustedProxies()". - * - * The "X-Forwarded-Proto" header must contain the protocol: "https" or "http". - * - * If your reverse proxy uses a different header name than "X-Forwarded-Proto" - * ("SSL_HTTPS" for instance), configure it via "setTrustedHeaderName()" with - * the "client-proto" key. - * - * @return bool + * {@inheritdoc} * * @api */ @@ -1311,17 +1298,7 @@ public function setMethod($method) } /** - * Gets the request "intended" method. - * - * If the X-HTTP-Method-Override header is set, and if the method is a POST, - * then it is used to determine the "real" intended HTTP method. - * - * The _method request parameter can also be used to determine the HTTP method, - * but only if enableHttpMethodParameterOverride() has been called. - * - * The method is always an uppercased string. - * - * @return string The request method + * {@inheritdoc} * * @api * @@ -1357,11 +1334,7 @@ public function getRealMethod() } /** - * Gets the mime type associated with the format. - * - * @param string $format The format - * - * @return string The associated mime type (null if not found) + * {@inheritdoc} * * @api */ @@ -1418,17 +1391,7 @@ public function setFormat($format, $mimeTypes) } /** - * Gets the request format. - * - * Here is the process to determine the format: - * - * * format defined by the user (with setRequestFormat()) - * * _format request parameter - * * $default - * - * @param string $default The default format - * - * @return string The request format + * {@inheritdoc} * * @api */ @@ -1514,9 +1477,7 @@ public function getLocale() } /** - * Checks if the request method is of specified type. - * - * @param string $method Uppercase request method (GET, POST etc). + * {@inheritdoc} * * @return bool */ @@ -1566,9 +1527,7 @@ public function getContent($asResource = false) } /** - * Gets the Etags. - * - * @return array The entity tags + * {@inheritdoc} */ public function getETags() { @@ -1725,6 +1684,30 @@ public function isXmlHttpRequest() return 'XMLHttpRequest' == $this->headers->get('X-Requested-With'); } + /** + * {@inheritdoc} + */ + public function getAttributes() + { + return $this->attributes; + } + + /** + * {@inheritdoc} + */ + public function getHeaders() + { + return $this->headers; + } + + /** + * {@inheritdoc} + */ + public function getServer() + { + return $this->server; + } + /* * The following methods are derived from code of the Zend Framework (1.10dev - 2010-01-24) * @@ -1963,8 +1946,8 @@ private static function createRequestFromFactory(array $query = array(), array $ if (self::$requestFactory) { $request = call_user_func(self::$requestFactory, $query, $request, $attributes, $cookies, $files, $server, $content); - if (!$request instanceof Request) { - throw new \LogicException('The Request factory must return an instance of Symfony\Component\HttpFoundation\Request.'); + if (!$request instanceof RequestInterface) { + throw new \LogicException('The Request factory must return an instance of Symfony\Component\HttpFoundation\RequestInterface.'); } return $request; diff --git a/src/Symfony/Component/HttpFoundation/RequestMatcher.php b/src/Symfony/Component/HttpFoundation/RequestMatcher.php index c571c604b4f12..989f3368416a2 100644 --- a/src/Symfony/Component/HttpFoundation/RequestMatcher.php +++ b/src/Symfony/Component/HttpFoundation/RequestMatcher.php @@ -147,7 +147,7 @@ public function matchAttribute($key, $regexp) * * @api */ - public function matches(Request $request) + public function matches(RequestInterface $request) { if ($this->schemes && !in_array($request->getScheme(), $this->schemes)) { return false; @@ -158,7 +158,7 @@ public function matches(Request $request) } foreach ($this->attributes as $key => $pattern) { - if (!preg_match('{'.$pattern.'}', $request->attributes->get($key))) { + if (!preg_match('{'.$pattern.'}', $request->getAttributes()->get($key))) { return false; } } @@ -175,7 +175,7 @@ public function matches(Request $request) return true; } - // Note to future implementors: add additional checks above the + // Note to future implementers: add additional checks above the // foreach above or else your check might not be run! return count($this->ips) === 0; } diff --git a/src/Symfony/Component/HttpFoundation/RequestMatcherInterface.php b/src/Symfony/Component/HttpFoundation/RequestMatcherInterface.php index b45f80ce8f24f..fc8d2e6125b0d 100644 --- a/src/Symfony/Component/HttpFoundation/RequestMatcherInterface.php +++ b/src/Symfony/Component/HttpFoundation/RequestMatcherInterface.php @@ -23,11 +23,11 @@ interface RequestMatcherInterface /** * Decides whether the rule(s) implemented by the strategy matches the supplied request. * - * @param Request $request The request to check for a match + * @param RequestInterface $request The request to check for a match * * @return bool true if the request matches, false otherwise * * @api */ - public function matches(Request $request); + public function matches(RequestInterface $request); } diff --git a/src/Symfony/Component/HttpFoundation/RequestStack.php b/src/Symfony/Component/HttpFoundation/RequestStack.php index 3d9cfd0c64194..cb4dda7d20e2d 100644 --- a/src/Symfony/Component/HttpFoundation/RequestStack.php +++ b/src/Symfony/Component/HttpFoundation/RequestStack.php @@ -29,7 +29,7 @@ class RequestStack * This method should generally not be called directly as the stack * management should be taken care of by the application itself. */ - public function push(Request $request) + public function push(RequestInterface $request) { $this->requests[] = $request; } diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index 219cb527ff110..25454b0692f92 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -260,11 +260,11 @@ public function __clone() * compliant with RFC 2616. Most of the changes are based on * the Request that is "associated" with this Response. * - * @param Request $request A Request instance + * @param RequestInterface $request A Request instance * * @return Response The current response. */ - public function prepare(Request $request) + public function prepare(RequestInterface $request) { $headers = $this->headers; @@ -306,7 +306,7 @@ public function prepare(Request $request) } // Fix protocol - if ('HTTP/1.0' != $request->server->get('SERVER_PROTOCOL')) { + if ('HTTP/1.0' != $request->getServer()->get('SERVER_PROTOCOL')) { $this->setProtocolVersion('1.1'); } @@ -1064,13 +1064,13 @@ public function setVary($headers, $replace = true) * If the Response is not modified, it sets the status code to 304 and * removes the actual content by calling the setNotModified() method. * - * @param Request $request A Request instance + * @param RequestInterface $request A Request instance * * @return bool true if the Response validators match the Request, false otherwise * * @api */ - public function isNotModified(Request $request) + public function isNotModified(RequestInterface $request) { if (!$request->isMethodSafe()) { return false; @@ -1078,7 +1078,7 @@ public function isNotModified(Request $request) $notModified = false; $lastModified = $this->headers->get('Last-Modified'); - $modifiedSince = $request->headers->get('If-Modified-Since'); + $modifiedSince = $request->getHeaders()->get('If-Modified-Since'); if ($etags = $request->getEtags()) { $notModified = in_array($this->getEtag(), $etags) || in_array('*', $etags); @@ -1264,9 +1264,12 @@ public static function closeOutputBuffers($targetLevel, $flush) * * @link http://support.microsoft.com/kb/323308 */ - protected function ensureIEOverSSLCompatibility(Request $request) + protected function ensureIEOverSSLCompatibility(RequestInterface $request) { - if (false !== stripos($this->headers->get('Content-Disposition'), 'attachment') && preg_match('/MSIE (.*?);/i', $request->server->get('HTTP_USER_AGENT'), $match) == 1 && true === $request->isSecure()) { + if (false !== stripos($this->headers->get('Content-Disposition'), 'attachment') + && preg_match('/MSIE (.*?);/i', $request->getServer()->get('HTTP_USER_AGENT'), $match) == 1 + && true === $request->isSecure() + ) { if (intval(preg_replace("/(MSIE )(.*?);/", "$2", $match[0])) < 9) { $this->headers->remove('Cache-Control'); }