horros /
agavi2
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | namespace Agavi\Response; |
||
| 3 | |||
| 4 | // +---------------------------------------------------------------------------+ |
||
| 5 | // | This file is part of the Agavi package. | |
||
| 6 | // | Copyright (c) 2005-2011 the Agavi Project. | |
||
| 7 | // | | |
||
| 8 | // | For the full copyright and license information, please view the LICENSE | |
||
| 9 | // | file that was distributed with this source code. You can also view the | |
||
| 10 | // | LICENSE file online at http://www.agavi.org/LICENSE.txt | |
||
| 11 | // | vi: set noexpandtab: | |
||
| 12 | // | Local Variables: | |
||
| 13 | // | indent-tabs-mode: t | |
||
| 14 | // | End: | |
||
| 15 | // +---------------------------------------------------------------------------+ |
||
| 16 | use Agavi\Config\Config; |
||
| 17 | use Agavi\Core\Context; |
||
| 18 | use Agavi\Dispatcher\OutputType; |
||
| 19 | use Agavi\Exception\AgaviException; |
||
| 20 | use Agavi\Request\WebRequest; |
||
| 21 | use Agavi\Routing\WebRouting; |
||
| 22 | |||
| 23 | /** |
||
| 24 | * AgaviWebResponse handles HTTP responses. |
||
| 25 | * |
||
| 26 | * @package agavi |
||
| 27 | * @subpackage response |
||
| 28 | * |
||
| 29 | * @author David Zülke <[email protected]> |
||
| 30 | * @copyright Authors |
||
| 31 | * @copyright The Agavi Project |
||
| 32 | * |
||
| 33 | * @since 0.11.0 |
||
| 34 | * |
||
| 35 | * @version $Id$ |
||
| 36 | */ |
||
| 37 | class WebResponse extends Response |
||
| 38 | { |
||
| 39 | /** |
||
| 40 | * @var array An array of all HTTP 1.0 status codes and their message. |
||
| 41 | */ |
||
| 42 | protected $http10StatusCodes = array( |
||
| 43 | '200' => "HTTP/1.0 200 OK", |
||
| 44 | '201' => "HTTP/1.0 201 Created", |
||
| 45 | '202' => "HTTP/1.0 202 Accepted", |
||
| 46 | '204' => "HTTP/1.0 204 No Content", |
||
| 47 | '205' => "HTTP/1.0 205 Reset Content", |
||
| 48 | '206' => "HTTP/1.0 206 Partial Content", |
||
| 49 | '300' => "HTTP/1.0 300 Multiple Choices", |
||
| 50 | '301' => "HTTP/1.0 301 Moved Permanently", |
||
| 51 | '302' => "HTTP/1.0 302 Found", |
||
| 52 | '304' => "HTTP/1.0 304 Not Modified", |
||
| 53 | '400' => "HTTP/1.0 400 Bad Request", |
||
| 54 | '401' => "HTTP/1.0 401 Unauthorized", |
||
| 55 | '402' => "HTTP/1.0 402 Payment Required", |
||
| 56 | '403' => "HTTP/1.0 403 Forbidden", |
||
| 57 | '404' => "HTTP/1.0 404 Not Found", |
||
| 58 | '405' => "HTTP/1.0 405 Method Not Allowed", |
||
| 59 | '406' => "HTTP/1.0 406 Not Acceptable", |
||
| 60 | '407' => "HTTP/1.0 407 Proxy Authentication Required", |
||
| 61 | '408' => "HTTP/1.0 408 Request Timeout", |
||
| 62 | '409' => "HTTP/1.0 409 Conflict", |
||
| 63 | '410' => "HTTP/1.0 410 Gone", |
||
| 64 | '411' => "HTTP/1.0 411 Length Required", |
||
| 65 | '412' => "HTTP/1.0 412 Precondition Failed", |
||
| 66 | '413' => "HTTP/1.0 413 Request Entity Too Large", |
||
| 67 | '414' => "HTTP/1.0 414 Request-URI Too Long", |
||
| 68 | '415' => "HTTP/1.0 415 Unsupported Media Type", |
||
| 69 | '416' => "HTTP/1.0 416 Requested Range Not Satisfiable", |
||
| 70 | '417' => "HTTP/1.0 417 Expectation Failed", |
||
| 71 | '500' => "HTTP/1.0 500 Internal Server Error", |
||
| 72 | '501' => "HTTP/1.0 501 Not Implemented", |
||
| 73 | '502' => "HTTP/1.0 502 Bad Gateway", |
||
| 74 | '503' => "HTTP/1.0 503 Service Unavailable", |
||
| 75 | '504' => "HTTP/1.0 504 Gateway Timeout", |
||
| 76 | '505' => "HTTP/1.0 505 HTTP Version Not Supported", |
||
| 77 | ); |
||
| 78 | |||
| 79 | /** |
||
| 80 | * @var array An array of all HTTP 1.1 status codes and their message. |
||
| 81 | */ |
||
| 82 | protected $http11StatusCodes = array( |
||
| 83 | '100' => "HTTP/1.1 100 Continue", |
||
| 84 | '101' => "HTTP/1.1 101 Switching Protocols", |
||
| 85 | '200' => "HTTP/1.1 200 OK", |
||
| 86 | '201' => "HTTP/1.1 201 Created", |
||
| 87 | '202' => "HTTP/1.1 202 Accepted", |
||
| 88 | '203' => "HTTP/1.1 203 Non-Authoritative Information", |
||
| 89 | '204' => "HTTP/1.1 204 No Content", |
||
| 90 | '205' => "HTTP/1.1 205 Reset Content", |
||
| 91 | '206' => "HTTP/1.1 206 Partial Content", |
||
| 92 | '300' => "HTTP/1.1 300 Multiple Choices", |
||
| 93 | '301' => "HTTP/1.1 301 Moved Permanently", |
||
| 94 | '302' => "HTTP/1.1 302 Found", |
||
| 95 | '303' => "HTTP/1.1 303 See Other", |
||
| 96 | '304' => "HTTP/1.1 304 Not Modified", |
||
| 97 | '305' => "HTTP/1.1 305 Use Proxy", |
||
| 98 | '307' => "HTTP/1.1 307 Temporary Redirect", |
||
| 99 | '400' => "HTTP/1.1 400 Bad Request", |
||
| 100 | '401' => "HTTP/1.1 401 Unauthorized", |
||
| 101 | '402' => "HTTP/1.1 402 Payment Required", |
||
| 102 | '403' => "HTTP/1.1 403 Forbidden", |
||
| 103 | '404' => "HTTP/1.1 404 Not Found", |
||
| 104 | '405' => "HTTP/1.1 405 Method Not Allowed", |
||
| 105 | '406' => "HTTP/1.1 406 Not Acceptable", |
||
| 106 | '407' => "HTTP/1.1 407 Proxy Authentication Required", |
||
| 107 | '408' => "HTTP/1.1 408 Request Timeout", |
||
| 108 | '409' => "HTTP/1.1 409 Conflict", |
||
| 109 | '410' => "HTTP/1.1 410 Gone", |
||
| 110 | '411' => "HTTP/1.1 411 Length Required", |
||
| 111 | '412' => "HTTP/1.1 412 Precondition Failed", |
||
| 112 | '413' => "HTTP/1.1 413 Request Entity Too Large", |
||
| 113 | '414' => "HTTP/1.1 414 Request-URI Too Long", |
||
| 114 | '415' => "HTTP/1.1 415 Unsupported Media Type", |
||
| 115 | '416' => "HTTP/1.1 416 Requested Range Not Satisfiable", |
||
| 116 | '417' => "HTTP/1.1 417 Expectation Failed", |
||
| 117 | '500' => "HTTP/1.1 500 Internal Server Error", |
||
| 118 | '501' => "HTTP/1.1 501 Not Implemented", |
||
| 119 | '502' => "HTTP/1.1 502 Bad Gateway", |
||
| 120 | '503' => "HTTP/1.1 503 Service Unavailable", |
||
| 121 | '504' => "HTTP/1.1 504 Gateway Timeout", |
||
| 122 | '505' => "HTTP/1.1 505 HTTP Version Not Supported", |
||
| 123 | ); |
||
| 124 | |||
| 125 | /** |
||
| 126 | * @var array The array with the HTTP status codes to be used here. |
||
| 127 | */ |
||
| 128 | protected $httpStatusCodes = null; |
||
| 129 | |||
| 130 | /** |
||
| 131 | * @var string The HTTP status code to send for the response. |
||
| 132 | */ |
||
| 133 | protected $httpStatusCode = '200'; |
||
| 134 | |||
| 135 | /** |
||
| 136 | * @var array The HTTP headers scheduled to be sent with the response. |
||
| 137 | */ |
||
| 138 | protected $httpHeaders = array(); |
||
| 139 | |||
| 140 | /** |
||
| 141 | * @var array The Cookies scheduled to be sent with the response. |
||
| 142 | */ |
||
| 143 | protected $cookies = array(); |
||
| 144 | |||
| 145 | /** |
||
| 146 | * @var array An array of redirect information, or null if no redirect. |
||
| 147 | */ |
||
| 148 | protected $redirect = null; |
||
| 149 | |||
| 150 | /** |
||
| 151 | * Initialize this Response. |
||
| 152 | * |
||
| 153 | * @param Context $context A Context instance. |
||
| 154 | * @param array $parameters An array of initialization parameters. |
||
| 155 | * |
||
| 156 | * @author David Zülke <[email protected]> |
||
| 157 | * @since 0.11.0 |
||
| 158 | */ |
||
| 159 | public function initialize(Context $context, array $parameters = array()) |
||
| 160 | { |
||
| 161 | parent::initialize($context, $parameters); |
||
| 162 | |||
| 163 | /** @var WebRequest $request */ |
||
| 164 | $request = $context->getRequest(); |
||
| 165 | |||
| 166 | // if 'cookie_secure' is set, and null, then we need to set whatever WebRequest::isHttps() returns |
||
| 167 | if (array_key_exists('cookie_secure', $parameters) && $parameters['cookie_secure'] === null) { |
||
| 168 | $parameters['cookie_secure'] = $request->isHttps(); |
||
| 169 | } |
||
| 170 | |||
| 171 | $this->setParameters(array( |
||
| 172 | 'cookie_lifetime' => isset($parameters['cookie_lifetime']) ? $parameters['cookie_lifetime'] : 0, |
||
| 173 | 'cookie_path' => isset($parameters['cookie_path']) ? $parameters['cookie_path'] : null, |
||
| 174 | 'cookie_domain' => isset($parameters['cookie_domain']) ? $parameters['cookie_domain'] : "", |
||
| 175 | 'cookie_secure' => isset($parameters['cookie_secure']) ? $parameters['cookie_secure'] : false, |
||
| 176 | 'cookie_httponly' => isset($parameters['cookie_httponly']) ? $parameters['cookie_httponly'] : false, |
||
| 177 | // For historical reasons, PHP's setcookie() encodes cookies with urlencode(), which |
||
| 178 | // is not compliant with RFC 6265 as it encodes spaces as a "+" sign instead of "%20". |
||
| 179 | // This makes most client-side Javascript cookie libraries decode it not as a space |
||
| 180 | // but as an actual plus sign. We sadly cannot change the default encoding of cookies |
||
| 181 | // as it would be a breaking change, but introduced a setting instead, which we |
||
| 182 | // recommend to set to "rawurlencode" for new projects. |
||
| 183 | 'cookie_encode_callback' => isset($parameters['cookie_encode_callback']) ? $parameters['cookie_encode_callback'] : 'urlencode', |
||
| 184 | )); |
||
| 185 | |||
| 186 | switch ($request->getProtocol()) { |
||
| 187 | case 'HTTP/1.1': |
||
| 188 | $this->httpStatusCodes = $this->http11StatusCodes; |
||
| 189 | break; |
||
| 190 | default: |
||
| 191 | $this->httpStatusCodes = $this->http10StatusCodes; |
||
| 192 | } |
||
| 193 | } |
||
| 194 | |||
| 195 | /** |
||
| 196 | * Send all response data to the client. |
||
| 197 | * |
||
| 198 | * @param OutputType $outputType An optional Output Type object with information |
||
| 199 | * the response can use to send additional data, |
||
| 200 | * such as HTTP headers |
||
| 201 | * |
||
| 202 | * @author David Zülke <[email protected]> |
||
| 203 | * @since 0.11.0 |
||
| 204 | */ |
||
| 205 | public function send(OutputType $outputType = null) |
||
| 206 | { |
||
| 207 | if ($this->redirect) { |
||
|
0 ignored issues
–
show
|
|||
| 208 | $location = $this->redirect['location']; |
||
| 209 | if (!preg_match('#^[^:]+://#', $location)) { |
||
| 210 | if (isset($location[0]) && $location[0] == '/') { |
||
| 211 | /** @var WebRequest $rq */ |
||
| 212 | $rq = $this->context->getRequest(); |
||
| 213 | $location = $rq->getUrlScheme() . '://' . $rq->getUrlAuthority() . $location; |
||
| 214 | } else { |
||
| 215 | $location = $this->context->getRouting()->getBaseHref() . $location; |
||
|
0 ignored issues
–
show
It seems like you code against a specific sub-type and not the parent class
Agavi\Routing\Routing as the method getBaseHref() does only exist in the following sub-classes of Agavi\Routing\Routing: Agavi\Routing\WebRouting. Maybe you want to instanceof check for one of these explicitly?
Let’s take a look at an example: abstract class User
{
/** @return string */
abstract public function getPassword();
}
class MyUser extends User
{
public function getPassword()
{
// return something
}
public function getDisplayName()
{
// return some name.
}
}
class AuthSystem
{
public function authenticate(User $user)
{
$this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
// do something.
}
}
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break. Available Fixes
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types
inside the if block in such a case.
Loading history...
|
|||
| 216 | } |
||
| 217 | } |
||
| 218 | $this->setHttpHeader('Location', $location); |
||
| 219 | $this->setHttpStatusCode($this->redirect['code']); |
||
| 220 | if ($this->getParameter('send_content_length', true) && !$this->hasHttpHeader('Content-Length') && !$this->getParameter('send_redirect_content', false)) { |
||
| 221 | $this->setHttpHeader('Content-Length', 0); |
||
| 222 | } |
||
| 223 | } |
||
| 224 | $this->sendHttpResponseHeaders($outputType); |
||
| 225 | if (!$this->redirect || $this->getParameter('send_redirect_content', false)) { |
||
|
0 ignored issues
–
show
The expression
$this->redirect of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using Loading history...
|
|||
| 226 | $this->sendContent(); |
||
| 227 | } |
||
| 228 | } |
||
| 229 | |||
| 230 | /** |
||
| 231 | * Send the content for this response |
||
| 232 | * |
||
| 233 | * @author David Zülke <[email protected]> |
||
| 234 | * @since 0.11.0 |
||
| 235 | */ |
||
| 236 | public function sendContent() |
||
| 237 | { |
||
| 238 | if (is_resource($this->content) && $this->getParameter('use_sendfile_header', false)) { |
||
| 239 | $info = stream_get_meta_data($this->content); |
||
| 240 | if ($info['wrapper_type'] == 'plainfile') { |
||
| 241 | header($this->getParameter('sendfile_header_name', 'X-Sendfile') . ': ' . $info['uri']); |
||
| 242 | return; |
||
| 243 | } |
||
| 244 | } |
||
| 245 | return parent::sendContent(); |
||
| 246 | } |
||
| 247 | |||
| 248 | /** |
||
| 249 | * Clear all response data. |
||
| 250 | * |
||
| 251 | * @author David Zülke <[email protected]> |
||
| 252 | * @since 0.11.0 |
||
| 253 | */ |
||
| 254 | public function clear() |
||
| 255 | { |
||
| 256 | $this->clearContent(); |
||
| 257 | $this->httpStatusCode = '200'; |
||
| 258 | $this->httpHeaders = array(); |
||
| 259 | $this->cookies = array(); |
||
| 260 | $this->redirect = null; |
||
|
0 ignored issues
–
show
It seems like
null of type null is incompatible with the declared type array of property $redirect.
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. Loading history...
|
|||
| 261 | } |
||
| 262 | |||
| 263 | /** |
||
| 264 | * Check whether or not some content is set. |
||
| 265 | * |
||
| 266 | * @return bool If any content is set, false otherwise. |
||
| 267 | * |
||
| 268 | * @author David Zülke <[email protected]> |
||
| 269 | * @since 0.11.6 |
||
| 270 | */ |
||
| 271 | public function hasContent() |
||
| 272 | { |
||
| 273 | return $this->content !== null && $this->content !== ''; |
||
| 274 | } |
||
| 275 | |||
| 276 | /** |
||
| 277 | * Set the content type for the response. |
||
| 278 | * |
||
| 279 | * @param string $type A content type. |
||
| 280 | * |
||
| 281 | * @author David Zülke <[email protected]> |
||
| 282 | * @since 0.9.0 |
||
| 283 | */ |
||
| 284 | public function setContentType($type) |
||
| 285 | { |
||
| 286 | $this->setHttpHeader('Content-Type', $type); |
||
| 287 | } |
||
| 288 | |||
| 289 | /** |
||
| 290 | * Retrieve the content type set for the response. |
||
| 291 | * |
||
| 292 | * @return string A content type, or null if none is set. |
||
| 293 | * |
||
| 294 | * @author David Zülke <[email protected]> |
||
| 295 | * @since 0.9.0 |
||
| 296 | */ |
||
| 297 | public function getContentType() |
||
| 298 | { |
||
| 299 | $retval = $this->getHttpHeader('Content-Type'); |
||
| 300 | if (is_array($retval) && count($retval)) { |
||
| 301 | return $retval[0]; |
||
| 302 | } else { |
||
| 303 | return null; |
||
| 304 | } |
||
| 305 | } |
||
| 306 | |||
| 307 | /** |
||
| 308 | * Import response metadata (headers, cookies) from another response. |
||
| 309 | * |
||
| 310 | * @param Response $otherResponse The other response to import information from. |
||
| 311 | * |
||
| 312 | * @author David Zülke <[email protected]> |
||
| 313 | * @since 0.11.0 |
||
| 314 | */ |
||
| 315 | public function merge(Response $otherResponse) |
||
| 316 | { |
||
| 317 | parent::merge($otherResponse); |
||
| 318 | |||
| 319 | if ($otherResponse instanceof WebResponse) { |
||
| 320 | foreach ($otherResponse->getHttpHeaders() as $name => $value) { |
||
| 321 | if (!$this->hasHttpHeader($name)) { |
||
| 322 | $this->setHttpHeader($name, $value); |
||
| 323 | } |
||
| 324 | } |
||
| 325 | foreach ($otherResponse->getCookies() as $name => $cookie) { |
||
| 326 | if (!$this->hasCookie($name)) { |
||
| 327 | $this->setCookie($name, $cookie['value'], $cookie['lifetime'], $cookie['path'], $cookie['domain'], $cookie['secure'], $cookie['httponly'], $cookie['encode_callback']); |
||
| 328 | } |
||
| 329 | } |
||
| 330 | if ($otherResponse->hasRedirect() && !$this->hasRedirect()) { |
||
| 331 | $redirect = $otherResponse->getRedirect(); |
||
| 332 | $this->setRedirect($redirect['location'], $redirect['code']); |
||
| 333 | } |
||
| 334 | } |
||
| 335 | } |
||
| 336 | |||
| 337 | /** |
||
| 338 | * Check if the given HTTP status code is valid. |
||
| 339 | * |
||
| 340 | * @param string $code A numeric HTTP status code. |
||
| 341 | * |
||
| 342 | * @return bool True, if the code is valid, or false otherwise. |
||
| 343 | * |
||
| 344 | * @author David Zülke <[email protected]> |
||
| 345 | * @since 0.11.3 |
||
| 346 | */ |
||
| 347 | public function validateHttpStatusCode($code) |
||
| 348 | { |
||
| 349 | $code = (string)$code; |
||
| 350 | return isset($this->httpStatusCodes[$code]); |
||
| 351 | } |
||
| 352 | |||
| 353 | /** |
||
| 354 | * Sets a HTTP status code for the response. |
||
| 355 | * |
||
| 356 | * @param string $code A numeric HTTP status code. |
||
| 357 | * |
||
| 358 | * @author David Zülke <[email protected]> |
||
| 359 | * @since 0.11.0 |
||
| 360 | */ |
||
| 361 | public function setHttpStatusCode($code) |
||
| 362 | { |
||
| 363 | $code = (string)$code; |
||
| 364 | View Code Duplication | if ($this->validateHttpStatusCode($code)) { |
|
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 365 | $this->httpStatusCode = $code; |
||
| 366 | } else { |
||
| 367 | throw new AgaviException(sprintf('Invalid %s Status code: %s', $this->context->getRequest()->getProtocol(), $code)); |
||
|
0 ignored issues
–
show
It seems like you code against a specific sub-type and not the parent class
Agavi\Request\Request as the method getProtocol() does only exist in the following sub-classes of Agavi\Request\Request: Agavi\Request\SecureWebRequest, Agavi\Request\WebRequest. Maybe you want to instanceof check for one of these explicitly?
Let’s take a look at an example: abstract class User
{
/** @return string */
abstract public function getPassword();
}
class MyUser extends User
{
public function getPassword()
{
// return something
}
public function getDisplayName()
{
// return some name.
}
}
class AuthSystem
{
public function authenticate(User $user)
{
$this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
// do something.
}
}
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break. Available Fixes
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types
inside the if block in such a case.
Loading history...
|
|||
| 368 | } |
||
| 369 | } |
||
| 370 | |||
| 371 | /** |
||
| 372 | * Gets the HTTP status code set for the response. |
||
| 373 | * |
||
| 374 | * @return string A numeric HTTP status code between 100 and 505, or null |
||
| 375 | * if no status code has been set. |
||
| 376 | * |
||
| 377 | * @author David Zülke <[email protected]> |
||
| 378 | * @since 0.11.0 |
||
| 379 | */ |
||
| 380 | public function getHttpStatusCode() |
||
| 381 | { |
||
| 382 | return $this->httpStatusCode; |
||
| 383 | } |
||
| 384 | |||
| 385 | /** |
||
| 386 | * Normalizes a HTTP header names |
||
| 387 | * |
||
| 388 | * @param string $name A HTTP header name |
||
| 389 | * |
||
| 390 | * @return string A normalized HTTP header name |
||
| 391 | * |
||
| 392 | * @author David Zülke <[email protected]> |
||
| 393 | * @since 0.11.0 |
||
| 394 | */ |
||
| 395 | public function normalizeHttpHeaderName($name) |
||
| 396 | { |
||
| 397 | if (strtolower($name) == "etag") { |
||
| 398 | return "ETag"; |
||
| 399 | } elseif (strtolower($name) == "www-authenticate") { |
||
| 400 | return "WWW-Authenticate"; |
||
| 401 | } else { |
||
| 402 | return str_replace(' ', '-', ucwords(str_replace('-', ' ', strtolower($name)))); |
||
| 403 | } |
||
| 404 | } |
||
| 405 | |||
| 406 | /** |
||
| 407 | * Retrieve the HTTP header values set for the response. |
||
| 408 | * |
||
| 409 | * @param string $name A HTTP header field name. |
||
| 410 | * |
||
| 411 | * @return array All values set for that header, or null if no headers set |
||
| 412 | * |
||
| 413 | * @author David Zülke <[email protected]> |
||
| 414 | * @since 0.11.0 |
||
| 415 | */ |
||
| 416 | View Code Duplication | public function getHttpHeader($name) |
|
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 417 | { |
||
| 418 | $name = $this->normalizeHttpHeaderName($name); |
||
| 419 | $retval = null; |
||
| 420 | if (isset($this->httpHeaders[$name])) { |
||
| 421 | $retval = $this->httpHeaders[$name]; |
||
| 422 | } |
||
| 423 | return $retval; |
||
| 424 | } |
||
| 425 | |||
| 426 | /** |
||
| 427 | * Retrieve the HTTP headers set for the response. |
||
| 428 | * |
||
| 429 | * @return array An associative array of HTTP header names and values. |
||
| 430 | * |
||
| 431 | * @author David Zülke <[email protected]> |
||
| 432 | * @since 0.11.0 |
||
| 433 | */ |
||
| 434 | public function getHttpHeaders() |
||
| 435 | { |
||
| 436 | return $this->httpHeaders; |
||
| 437 | } |
||
| 438 | |||
| 439 | /** |
||
| 440 | * Check if an HTTP header has been set for the response. |
||
| 441 | * |
||
| 442 | * @param string $name A HTTP header field name. |
||
| 443 | * |
||
| 444 | * @return bool true if the header exists, false otherwise. |
||
| 445 | * |
||
| 446 | * @author David Zülke <[email protected]> |
||
| 447 | * @since 0.11.0 |
||
| 448 | */ |
||
| 449 | View Code Duplication | public function hasHttpHeader($name) |
|
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 450 | { |
||
| 451 | $name = $this->normalizeHttpHeaderName($name); |
||
| 452 | $retval = false; |
||
| 453 | if (isset($this->httpHeaders[$name])) { |
||
| 454 | $retval = true; |
||
| 455 | } |
||
| 456 | return $retval; |
||
| 457 | } |
||
| 458 | |||
| 459 | /** |
||
| 460 | * Set a HTTP header for the response |
||
| 461 | * |
||
| 462 | * @param string $name A HTTP header field name. |
||
| 463 | * @param mixed $value A HTTP header field value, of an array of values. |
||
| 464 | * @param bool $replace If true, a header with that name will be overwritten, |
||
| 465 | * otherwise, the value will be appended. |
||
| 466 | * |
||
| 467 | * @author David Zülke <[email protected]> |
||
| 468 | * @since 0.11.0 |
||
| 469 | */ |
||
| 470 | public function setHttpHeader($name, $value, $replace = true) |
||
| 471 | { |
||
| 472 | $name = $this->normalizeHttpHeaderName($name); |
||
| 473 | if (!isset($this->httpHeaders[$name]) || $replace) { |
||
| 474 | $this->httpHeaders[$name] = array(); |
||
| 475 | } |
||
| 476 | if (is_array($value)) { |
||
| 477 | $this->httpHeaders[$name] = array_merge($this->httpHeaders[$name], $value); |
||
| 478 | } else { |
||
| 479 | $this->httpHeaders[$name][] = $value; |
||
| 480 | } |
||
| 481 | } |
||
| 482 | |||
| 483 | /** |
||
| 484 | * Send a cookie. |
||
| 485 | * |
||
| 486 | * @param string $name A cookie name. |
||
| 487 | * @param mixed $value Data to store into a cookie. If null or empty cookie |
||
| 488 | * will be tried to be removed. |
||
| 489 | * @param mixed $lifetime The lifetime of the cookie in seconds. When you pass 0 |
||
| 490 | * the cookie will be valid until the browser is closed. |
||
| 491 | * You can also use a strtotime() string instead of an int. |
||
| 492 | * @param string $path The path on the server the cookie will be available on. |
||
| 493 | * @param string $domain The domain the cookie is available on. |
||
| 494 | * @param bool $secure Indicates that the cookie should only be transmitted |
||
| 495 | * over a secure HTTPS connection. |
||
| 496 | * @param bool $httponly Whether the cookie will be made accessible only through |
||
| 497 | * the HTTP protocol, and not to client-side scripts. |
||
| 498 | * @param callable|bool $encodeCallback Callback to encode the cookie value. Set to false |
||
| 499 | * if you did already encode the value on your own. |
||
| 500 | * |
||
| 501 | * @throws AgaviException If $encodeCallback is neither false nor callable. |
||
| 502 | * |
||
| 503 | * @author Veikko Mäkinen <[email protected]> |
||
| 504 | * @author David Zülke <[email protected]> |
||
| 505 | * @since 0.11.0 |
||
| 506 | */ |
||
| 507 | public function setCookie($name, $value, $lifetime = null, $path = null, $domain = null, $secure = null, $httponly = null, $encodeCallback = null) |
||
| 508 | { |
||
| 509 | $lifetime = $lifetime !== null ? $lifetime : $this->getParameter('cookie_lifetime'); |
||
| 510 | $path = $path !== null ? $path : $this->getParameter('cookie_path'); |
||
| 511 | $domain = $domain !== null ? $domain : $this->getParameter('cookie_domain'); |
||
| 512 | $secure = (bool) ($secure !== null ? $secure : $this->getParameter('cookie_secure')); |
||
| 513 | $httponly = (bool) ($httponly !== null ? $httponly : $this->getParameter('cookie_httponly')); |
||
| 514 | $encodeCallback = $encodeCallback !== null ? $encodeCallback : $this->getParameter('cookie_encode_callback'); |
||
| 515 | |||
| 516 | if ($encodeCallback !== false && !is_callable($encodeCallback)) { |
||
| 517 | throw new AgaviException(sprintf('setCookie() $encodeCallback argument is not callable: %s', $encodeCallback)); |
||
| 518 | } |
||
| 519 | |||
| 520 | $this->cookies[$name] = array( |
||
| 521 | 'value' => $value, |
||
| 522 | 'lifetime' => $lifetime, |
||
| 523 | 'path' => $path, |
||
| 524 | 'domain' => $domain, |
||
| 525 | 'secure' => $secure, |
||
| 526 | 'httponly' => $httponly, |
||
| 527 | 'encode_callback' => $encodeCallback |
||
| 528 | ); |
||
| 529 | } |
||
| 530 | |||
| 531 | /** |
||
| 532 | * Unset an existing cookie. |
||
| 533 | * All arguments must reflect the values of the cookie that is already set. |
||
| 534 | * |
||
| 535 | * @param string $name A cookie name. |
||
| 536 | * @param string $path The path on the server the cookie will be available on. |
||
| 537 | * @param string $domain The domain the cookie is available on. |
||
| 538 | * @param bool $secure Indicates that the cookie should only be transmitted |
||
| 539 | * over a secure HTTPS connection. |
||
| 540 | * @param bool $httponly Whether the cookie will be made accessible only through |
||
| 541 | * the HTTP protocol, and not to client-side scripts. |
||
| 542 | * |
||
| 543 | * @author Ross Lawley <[email protected]> |
||
| 544 | * @author David Zülke <[email protected]> |
||
| 545 | * @since 0.11.0 |
||
| 546 | */ |
||
| 547 | public function unsetCookie($name, $path = null, $domain = null, $secure = null, $httponly = null) |
||
| 548 | { |
||
| 549 | // false as the value, triggers deletion |
||
| 550 | // null for the lifetime, since Agavi automatically sets that when the value is false or null |
||
| 551 | $this->setCookie($name, false, null, $path, $domain, $secure, $httponly); |
||
| 552 | } |
||
| 553 | |||
| 554 | /** |
||
| 555 | * Get a cookie set for later sending. |
||
| 556 | * |
||
| 557 | * @param string $name The name of the cookie. |
||
| 558 | * |
||
| 559 | * @return array An associative array containing the cookie data or null |
||
| 560 | * if no cookie with that name has been set. |
||
| 561 | * |
||
| 562 | * @author David Zülke <[email protected]> |
||
| 563 | * @since 0.11.0 |
||
| 564 | */ |
||
| 565 | public function getCookie($name) |
||
| 566 | { |
||
| 567 | if (isset($this->cookies[$name])) { |
||
| 568 | return $this->cookies[$name]; |
||
| 569 | } |
||
| 570 | } |
||
| 571 | |||
| 572 | /** |
||
| 573 | * Check if a cookie has been set for later sending. |
||
| 574 | * |
||
| 575 | * @param string $name The name of the cookie. |
||
| 576 | * |
||
| 577 | * @return bool True if a cookie with that name has been set, else false. |
||
| 578 | * |
||
| 579 | * @author David Zülke <[email protected]> |
||
| 580 | * @since 0.11.0 |
||
| 581 | */ |
||
| 582 | public function hasCookie($name) |
||
| 583 | { |
||
| 584 | return isset($this->cookies[$name]); |
||
| 585 | } |
||
| 586 | |||
| 587 | /** |
||
| 588 | * Remove a cookie previously set for later sending. |
||
| 589 | * |
||
| 590 | * This method cannot be used to unset a cookie. It's purpose is to remove a |
||
| 591 | * cookie from the list of cookies to be sent along with the response. If you |
||
| 592 | * wish to remove an existing cookie, use the setCookie method and supply null |
||
| 593 | * as the value. |
||
| 594 | * |
||
| 595 | * @param string $name The name of the cookie. |
||
| 596 | * |
||
| 597 | * @author David Zülke <[email protected]> |
||
| 598 | * @since 0.11.0 |
||
| 599 | */ |
||
| 600 | public function removeCookie($name) |
||
| 601 | { |
||
| 602 | if (isset($this->cookies[$name])) { |
||
| 603 | unset($this->cookies[$name]); |
||
| 604 | } |
||
| 605 | } |
||
| 606 | |||
| 607 | /** |
||
| 608 | * Get a list of cookies set for later sending. |
||
| 609 | * |
||
| 610 | * @return array An associative array of cookie names (key) and cookie |
||
| 611 | * information (value, associative array). |
||
| 612 | * |
||
| 613 | * @author David Zülke <[email protected]> |
||
| 614 | * @since 0.11.0 |
||
| 615 | */ |
||
| 616 | public function getCookies() |
||
| 617 | { |
||
| 618 | return $this->cookies; |
||
| 619 | } |
||
| 620 | |||
| 621 | /** |
||
| 622 | * Remove the HTTP header set for the response |
||
| 623 | * |
||
| 624 | * @param string $name A HTTP header field name. |
||
| 625 | * |
||
| 626 | * @return mixed The removed header's value or null if header was not set. |
||
| 627 | * |
||
| 628 | * @author David Zülke <[email protected]> |
||
| 629 | * @since 0.11.0 |
||
| 630 | */ |
||
| 631 | View Code Duplication | public function removeHttpHeader($name) |
|
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 632 | { |
||
| 633 | $name = $this->normalizeHttpHeaderName($name); |
||
| 634 | $retval = null; |
||
| 635 | if (isset($this->httpHeaders[$name])) { |
||
| 636 | $retval = $this->httpHeaders[$name]; |
||
| 637 | unset($this->httpHeaders[$name]); |
||
| 638 | } |
||
| 639 | return $retval; |
||
| 640 | } |
||
| 641 | |||
| 642 | /** |
||
| 643 | * Clears the HTTP headers set for this response. |
||
| 644 | * |
||
| 645 | * @author David Zülke <[email protected]> |
||
| 646 | * @since 0.11.0 |
||
| 647 | */ |
||
| 648 | public function clearHttpHeaders() |
||
| 649 | { |
||
| 650 | $this->httpHeaders = array(); |
||
| 651 | } |
||
| 652 | |||
| 653 | /** |
||
| 654 | * Sends HTTP Status code, headers and cookies |
||
| 655 | * |
||
| 656 | * @author David Zülke <[email protected]> |
||
| 657 | * @since 0.11.0 |
||
| 658 | */ |
||
| 659 | protected function sendHttpResponseHeaders(OutputType $outputType = null) |
||
| 660 | { |
||
| 661 | if ($outputType === null) { |
||
| 662 | $outputType = $this->getOutputType(); |
||
| 663 | } |
||
| 664 | |||
| 665 | // send HTTP status code |
||
| 666 | if (isset($this->httpStatusCode) && isset($this->httpStatusCodes[$this->httpStatusCode])) { |
||
| 667 | header($this->httpStatusCodes[$this->httpStatusCode]); |
||
| 668 | } |
||
| 669 | |||
| 670 | if ($outputType !== null) { |
||
| 671 | $httpHeaders = $outputType->getParameter('http_headers'); |
||
| 672 | if (!is_array($httpHeaders)) { |
||
| 673 | $httpHeaders = array(); |
||
| 674 | } |
||
| 675 | foreach ($httpHeaders as $name => $value) { |
||
| 676 | if (!$this->hasHttpHeader($name)) { |
||
| 677 | $this->setHttpHeader($name, $value); |
||
| 678 | } |
||
| 679 | } |
||
| 680 | } |
||
| 681 | |||
| 682 | if ($this->getParameter('send_content_length', true) && !$this->hasHttpHeader('Content-Length') && ($contentSize = $this->getContentSize()) !== false) { |
||
| 683 | $this->setHttpHeader('Content-Length', $contentSize); |
||
| 684 | } |
||
| 685 | |||
| 686 | if ($this->getParameter('expose_agavi', true) && !$this->hasHttpHeader('X-Powered-By')) { |
||
| 687 | if (Config::get('expose_agavi_version', $expose_php = ini_get('expose_php'))) { |
||
| 688 | $xpbh = Config::get('agavi.release'); |
||
| 689 | } else { |
||
| 690 | $xpbh = Config::get('agavi.name'); |
||
| 691 | } |
||
| 692 | if ($expose_php) { |
||
| 693 | $xpbh .= ' on PHP/' . PHP_VERSION; |
||
| 694 | } |
||
| 695 | $this->setHttpHeader('X-Powered-By', $xpbh); |
||
| 696 | } |
||
| 697 | |||
| 698 | $routing = $this->context->getRouting(); |
||
| 699 | if ($routing instanceof WebRouting) { |
||
| 700 | $basePath = $routing->getBasePath(); |
||
| 701 | } else { |
||
| 702 | $basePath = '/'; |
||
| 703 | } |
||
| 704 | |||
| 705 | // send cookies |
||
| 706 | foreach ($this->cookies as $name => $values) { |
||
| 707 | if (is_string($values['lifetime'])) { |
||
| 708 | // a string, so we pass it to strtotime() |
||
| 709 | $expire = strtotime($values['lifetime']); |
||
| 710 | } else { |
||
| 711 | // do we want to set expiration time or not? |
||
| 712 | $expire = ($values['lifetime'] != 0) ? time() + $values['lifetime'] : 0; |
||
| 713 | } |
||
| 714 | |||
| 715 | if ($values['value'] === false || $values['value'] === null || $values['value'] === '') { |
||
| 716 | $expire = time() - 3600 * 24; |
||
| 717 | } |
||
| 718 | |||
| 719 | if ($values['encode_callback']) { |
||
| 720 | $values['value'] = call_user_func($values['encode_callback'], $values['value']); |
||
| 721 | } |
||
| 722 | |||
| 723 | if ($values['path'] === null) { |
||
| 724 | $values['path'] = $basePath; |
||
| 725 | } |
||
| 726 | |||
| 727 | setrawcookie($name, $values['value'], $expire, $values['path'], $values['domain'], $values['secure'], $values['httponly']); |
||
| 728 | } |
||
| 729 | |||
| 730 | // send headers |
||
| 731 | foreach ($this->httpHeaders as $name => $values) { |
||
| 732 | foreach ($values as $key => $value) { |
||
| 733 | if ($key == 0) { |
||
| 734 | header($name . ': ' . $value); |
||
| 735 | } else { |
||
| 736 | header($name . ': ' . $value, false); |
||
| 737 | } |
||
| 738 | } |
||
| 739 | } |
||
| 740 | } |
||
| 741 | |||
| 742 | /** |
||
| 743 | * Redirect externally. |
||
| 744 | * |
||
| 745 | * @param mixed $location Where to redirect. |
||
| 746 | * @param int $code |
||
| 747 | * |
||
| 748 | * @author David Zülke <[email protected]> |
||
| 749 | * @since 0.11.0 |
||
| 750 | */ |
||
| 751 | public function setRedirect($location, $code = 302) |
||
| 752 | { |
||
| 753 | View Code Duplication | if (!$this->validateHttpStatusCode($code)) { |
|
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 754 | throw new AgaviException(sprintf('Invalid %s Redirect Status code: %s', $this->context->getRequest()->getProtocol(), $code)); |
||
|
0 ignored issues
–
show
It seems like you code against a specific sub-type and not the parent class
Agavi\Request\Request as the method getProtocol() does only exist in the following sub-classes of Agavi\Request\Request: Agavi\Request\SecureWebRequest, Agavi\Request\WebRequest. Maybe you want to instanceof check for one of these explicitly?
Let’s take a look at an example: abstract class User
{
/** @return string */
abstract public function getPassword();
}
class MyUser extends User
{
public function getPassword()
{
// return something
}
public function getDisplayName()
{
// return some name.
}
}
class AuthSystem
{
public function authenticate(User $user)
{
$this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
// do something.
}
}
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break. Available Fixes
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types
inside the if block in such a case.
Loading history...
|
|||
| 755 | } |
||
| 756 | $this->redirect = array('location' => $location, 'code' => $code); |
||
| 757 | } |
||
| 758 | |||
| 759 | /** |
||
| 760 | * Get info about the set redirect. |
||
| 761 | * |
||
| 762 | * @return array An assoc array of redirect info, or null if none set. |
||
| 763 | * |
||
| 764 | * @author David Zülke <[email protected]> |
||
| 765 | * @since 0.11.0 |
||
| 766 | */ |
||
| 767 | public function getRedirect() |
||
| 768 | { |
||
| 769 | return $this->redirect; |
||
| 770 | } |
||
| 771 | |||
| 772 | /** |
||
| 773 | * Check if a redirect is set. |
||
| 774 | * |
||
| 775 | * @return bool true, if a redirect is set, otherwise false |
||
| 776 | * |
||
| 777 | * @author David Zülke <[email protected]> |
||
| 778 | * @since 0.11.0 |
||
| 779 | */ |
||
| 780 | public function hasRedirect() |
||
| 781 | { |
||
| 782 | return $this->redirect !== null; |
||
| 783 | } |
||
| 784 | |||
| 785 | /** |
||
| 786 | * Clear any set redirect information. |
||
| 787 | * |
||
| 788 | * @author David Zülke <[email protected]> |
||
| 789 | * @since 0.11.0 |
||
| 790 | */ |
||
| 791 | public function clearRedirect() |
||
| 792 | { |
||
| 793 | $this->redirect = null; |
||
|
0 ignored issues
–
show
It seems like
null of type null is incompatible with the declared type array of property $redirect.
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. Loading history...
|
|||
| 794 | } |
||
| 795 | } |
||
| 796 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)or! empty(...)instead.