1<?php 2 3/* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <fabien@symfony.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Symfony\Component\HttpFoundation\Tests; 13 14use Symfony\Component\HttpFoundation\Request; 15use Symfony\Component\HttpFoundation\Response; 16 17/** 18 * @group time-sensitive 19 */ 20class ResponseTest extends ResponseTestCase 21{ 22 public function testCreate() 23 { 24 $response = Response::create('foo', 301, array('Foo' => 'bar')); 25 26 $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response); 27 $this->assertEquals(301, $response->getStatusCode()); 28 $this->assertEquals('bar', $response->headers->get('foo')); 29 } 30 31 public function testToString() 32 { 33 $response = new Response(); 34 $response = explode("\r\n", $response); 35 $this->assertEquals('HTTP/1.0 200 OK', $response[0]); 36 $this->assertEquals('Cache-Control: no-cache, private', $response[1]); 37 } 38 39 public function testClone() 40 { 41 $response = new Response(); 42 $responseClone = clone $response; 43 $this->assertEquals($response, $responseClone); 44 } 45 46 public function testSendHeaders() 47 { 48 $response = new Response(); 49 $headers = $response->sendHeaders(); 50 $this->assertObjectHasAttribute('headers', $headers); 51 $this->assertObjectHasAttribute('content', $headers); 52 $this->assertObjectHasAttribute('version', $headers); 53 $this->assertObjectHasAttribute('statusCode', $headers); 54 $this->assertObjectHasAttribute('statusText', $headers); 55 $this->assertObjectHasAttribute('charset', $headers); 56 } 57 58 public function testSend() 59 { 60 $response = new Response(); 61 $responseSend = $response->send(); 62 $this->assertObjectHasAttribute('headers', $responseSend); 63 $this->assertObjectHasAttribute('content', $responseSend); 64 $this->assertObjectHasAttribute('version', $responseSend); 65 $this->assertObjectHasAttribute('statusCode', $responseSend); 66 $this->assertObjectHasAttribute('statusText', $responseSend); 67 $this->assertObjectHasAttribute('charset', $responseSend); 68 } 69 70 public function testGetCharset() 71 { 72 $response = new Response(); 73 $charsetOrigin = 'UTF-8'; 74 $response->setCharset($charsetOrigin); 75 $charset = $response->getCharset(); 76 $this->assertEquals($charsetOrigin, $charset); 77 } 78 79 public function testIsCacheable() 80 { 81 $response = new Response(); 82 $this->assertFalse($response->isCacheable()); 83 } 84 85 public function testIsCacheableWithErrorCode() 86 { 87 $response = new Response('', 500); 88 $this->assertFalse($response->isCacheable()); 89 } 90 91 public function testIsCacheableWithNoStoreDirective() 92 { 93 $response = new Response(); 94 $response->headers->set('cache-control', 'private'); 95 $this->assertFalse($response->isCacheable()); 96 } 97 98 public function testIsCacheableWithSetTtl() 99 { 100 $response = new Response(); 101 $response->setTtl(10); 102 $this->assertTrue($response->isCacheable()); 103 } 104 105 public function testMustRevalidate() 106 { 107 $response = new Response(); 108 $this->assertFalse($response->mustRevalidate()); 109 } 110 111 public function testMustRevalidateWithMustRevalidateCacheControlHeader() 112 { 113 $response = new Response(); 114 $response->headers->set('cache-control', 'must-revalidate'); 115 116 $this->assertTrue($response->mustRevalidate()); 117 } 118 119 public function testMustRevalidateWithProxyRevalidateCacheControlHeader() 120 { 121 $response = new Response(); 122 $response->headers->set('cache-control', 'proxy-revalidate'); 123 124 $this->assertTrue($response->mustRevalidate()); 125 } 126 127 public function testSetNotModified() 128 { 129 $response = new Response(); 130 $modified = $response->setNotModified(); 131 $this->assertObjectHasAttribute('headers', $modified); 132 $this->assertObjectHasAttribute('content', $modified); 133 $this->assertObjectHasAttribute('version', $modified); 134 $this->assertObjectHasAttribute('statusCode', $modified); 135 $this->assertObjectHasAttribute('statusText', $modified); 136 $this->assertObjectHasAttribute('charset', $modified); 137 $this->assertEquals(304, $modified->getStatusCode()); 138 } 139 140 public function testIsSuccessful() 141 { 142 $response = new Response(); 143 $this->assertTrue($response->isSuccessful()); 144 } 145 146 public function testIsNotModified() 147 { 148 $response = new Response(); 149 $modified = $response->isNotModified(new Request()); 150 $this->assertFalse($modified); 151 } 152 153 public function testIsNotModifiedNotSafe() 154 { 155 $request = Request::create('/homepage', 'POST'); 156 157 $response = new Response(); 158 $this->assertFalse($response->isNotModified($request)); 159 } 160 161 public function testIsNotModifiedLastModified() 162 { 163 $before = 'Sun, 25 Aug 2013 18:32:31 GMT'; 164 $modified = 'Sun, 25 Aug 2013 18:33:31 GMT'; 165 $after = 'Sun, 25 Aug 2013 19:33:31 GMT'; 166 167 $request = new Request(); 168 $request->headers->set('If-Modified-Since', $modified); 169 170 $response = new Response(); 171 172 $response->headers->set('Last-Modified', $modified); 173 $this->assertTrue($response->isNotModified($request)); 174 175 $response->headers->set('Last-Modified', $before); 176 $this->assertTrue($response->isNotModified($request)); 177 178 $response->headers->set('Last-Modified', $after); 179 $this->assertFalse($response->isNotModified($request)); 180 181 $response->headers->set('Last-Modified', ''); 182 $this->assertFalse($response->isNotModified($request)); 183 } 184 185 public function testIsNotModifiedEtag() 186 { 187 $etagOne = 'randomly_generated_etag'; 188 $etagTwo = 'randomly_generated_etag_2'; 189 190 $request = new Request(); 191 $request->headers->set('if_none_match', sprintf('%s, %s, %s', $etagOne, $etagTwo, 'etagThree')); 192 193 $response = new Response(); 194 195 $response->headers->set('ETag', $etagOne); 196 $this->assertTrue($response->isNotModified($request)); 197 198 $response->headers->set('ETag', $etagTwo); 199 $this->assertTrue($response->isNotModified($request)); 200 201 $response->headers->set('ETag', ''); 202 $this->assertFalse($response->isNotModified($request)); 203 } 204 205 public function testIsNotModifiedLastModifiedAndEtag() 206 { 207 $before = 'Sun, 25 Aug 2013 18:32:31 GMT'; 208 $modified = 'Sun, 25 Aug 2013 18:33:31 GMT'; 209 $after = 'Sun, 25 Aug 2013 19:33:31 GMT'; 210 $etag = 'randomly_generated_etag'; 211 212 $request = new Request(); 213 $request->headers->set('if_none_match', sprintf('%s, %s', $etag, 'etagThree')); 214 $request->headers->set('If-Modified-Since', $modified); 215 216 $response = new Response(); 217 218 $response->headers->set('ETag', $etag); 219 $response->headers->set('Last-Modified', $after); 220 $this->assertFalse($response->isNotModified($request)); 221 222 $response->headers->set('ETag', 'non-existent-etag'); 223 $response->headers->set('Last-Modified', $before); 224 $this->assertFalse($response->isNotModified($request)); 225 226 $response->headers->set('ETag', $etag); 227 $response->headers->set('Last-Modified', $modified); 228 $this->assertTrue($response->isNotModified($request)); 229 } 230 231 public function testIsNotModifiedIfModifiedSinceAndEtagWithoutLastModified() 232 { 233 $modified = 'Sun, 25 Aug 2013 18:33:31 GMT'; 234 $etag = 'randomly_generated_etag'; 235 236 $request = new Request(); 237 $request->headers->set('if_none_match', sprintf('%s, %s', $etag, 'etagThree')); 238 $request->headers->set('If-Modified-Since', $modified); 239 240 $response = new Response(); 241 242 $response->headers->set('ETag', $etag); 243 $this->assertTrue($response->isNotModified($request)); 244 245 $response->headers->set('ETag', 'non-existent-etag'); 246 $this->assertFalse($response->isNotModified($request)); 247 } 248 249 public function testIsValidateable() 250 { 251 $response = new Response('', 200, array('Last-Modified' => $this->createDateTimeOneHourAgo()->format(DATE_RFC2822))); 252 $this->assertTrue($response->isValidateable(), '->isValidateable() returns true if Last-Modified is present'); 253 254 $response = new Response('', 200, array('ETag' => '"12345"')); 255 $this->assertTrue($response->isValidateable(), '->isValidateable() returns true if ETag is present'); 256 257 $response = new Response(); 258 $this->assertFalse($response->isValidateable(), '->isValidateable() returns false when no validator is present'); 259 } 260 261 public function testGetDate() 262 { 263 $oneHourAgo = $this->createDateTimeOneHourAgo(); 264 $response = new Response('', 200, array('Date' => $oneHourAgo->format(DATE_RFC2822))); 265 $date = $response->getDate(); 266 $this->assertEquals($oneHourAgo->getTimestamp(), $date->getTimestamp(), '->getDate() returns the Date header if present'); 267 268 $response = new Response(); 269 $date = $response->getDate(); 270 $this->assertEquals(time(), $date->getTimestamp(), '->getDate() returns the current Date if no Date header present'); 271 272 $response = new Response('', 200, array('Date' => $this->createDateTimeOneHourAgo()->format(DATE_RFC2822))); 273 $now = $this->createDateTimeNow(); 274 $response->headers->set('Date', $now->format(DATE_RFC2822)); 275 $date = $response->getDate(); 276 $this->assertEquals($now->getTimestamp(), $date->getTimestamp(), '->getDate() returns the date when the header has been modified'); 277 278 $response = new Response('', 200); 279 $now = $this->createDateTimeNow(); 280 $response->headers->remove('Date'); 281 $date = $response->getDate(); 282 $this->assertEquals($now->getTimestamp(), $date->getTimestamp(), '->getDate() returns the current Date when the header has previously been removed'); 283 } 284 285 public function testGetMaxAge() 286 { 287 $response = new Response(); 288 $response->headers->set('Cache-Control', 's-maxage=600, max-age=0'); 289 $this->assertEquals(600, $response->getMaxAge(), '->getMaxAge() uses s-maxage cache control directive when present'); 290 291 $response = new Response(); 292 $response->headers->set('Cache-Control', 'max-age=600'); 293 $this->assertEquals(600, $response->getMaxAge(), '->getMaxAge() falls back to max-age when no s-maxage directive present'); 294 295 $response = new Response(); 296 $response->headers->set('Cache-Control', 'must-revalidate'); 297 $response->headers->set('Expires', $this->createDateTimeOneHourLater()->format(DATE_RFC2822)); 298 $this->assertEquals(3600, $response->getMaxAge(), '->getMaxAge() falls back to Expires when no max-age or s-maxage directive present'); 299 300 $response = new Response(); 301 $response->headers->set('Cache-Control', 'must-revalidate'); 302 $response->headers->set('Expires', -1); 303 $this->assertEquals('Sat, 01 Jan 00 00:00:00 +0000', $response->getExpires()->format(DATE_RFC822)); 304 305 $response = new Response(); 306 $this->assertNull($response->getMaxAge(), '->getMaxAge() returns null if no freshness information available'); 307 } 308 309 public function testSetSharedMaxAge() 310 { 311 $response = new Response(); 312 $response->setSharedMaxAge(20); 313 314 $cacheControl = $response->headers->get('Cache-Control'); 315 $this->assertEquals('public, s-maxage=20', $cacheControl); 316 } 317 318 public function testIsPrivate() 319 { 320 $response = new Response(); 321 $response->headers->set('Cache-Control', 'max-age=100'); 322 $response->setPrivate(); 323 $this->assertEquals(100, $response->headers->getCacheControlDirective('max-age'), '->isPrivate() adds the private Cache-Control directive when set to true'); 324 $this->assertTrue($response->headers->getCacheControlDirective('private'), '->isPrivate() adds the private Cache-Control directive when set to true'); 325 326 $response = new Response(); 327 $response->headers->set('Cache-Control', 'public, max-age=100'); 328 $response->setPrivate(); 329 $this->assertEquals(100, $response->headers->getCacheControlDirective('max-age'), '->isPrivate() adds the private Cache-Control directive when set to true'); 330 $this->assertTrue($response->headers->getCacheControlDirective('private'), '->isPrivate() adds the private Cache-Control directive when set to true'); 331 $this->assertFalse($response->headers->hasCacheControlDirective('public'), '->isPrivate() removes the public Cache-Control directive'); 332 } 333 334 public function testExpire() 335 { 336 $response = new Response(); 337 $response->headers->set('Cache-Control', 'max-age=100'); 338 $response->expire(); 339 $this->assertEquals(100, $response->headers->get('Age'), '->expire() sets the Age to max-age when present'); 340 341 $response = new Response(); 342 $response->headers->set('Cache-Control', 'max-age=100, s-maxage=500'); 343 $response->expire(); 344 $this->assertEquals(500, $response->headers->get('Age'), '->expire() sets the Age to s-maxage when both max-age and s-maxage are present'); 345 346 $response = new Response(); 347 $response->headers->set('Cache-Control', 'max-age=5, s-maxage=500'); 348 $response->headers->set('Age', '1000'); 349 $response->expire(); 350 $this->assertEquals(1000, $response->headers->get('Age'), '->expire() does nothing when the response is already stale/expired'); 351 352 $response = new Response(); 353 $response->expire(); 354 $this->assertFalse($response->headers->has('Age'), '->expire() does nothing when the response does not include freshness information'); 355 356 $response = new Response(); 357 $response->headers->set('Expires', -1); 358 $response->expire(); 359 $this->assertNull($response->headers->get('Age'), '->expire() does not set the Age when the response is expired'); 360 } 361 362 public function testGetTtl() 363 { 364 $response = new Response(); 365 $this->assertNull($response->getTtl(), '->getTtl() returns null when no Expires or Cache-Control headers are present'); 366 367 $response = new Response(); 368 $response->headers->set('Expires', $this->createDateTimeOneHourLater()->format(DATE_RFC2822)); 369 $this->assertEquals(3600, $response->getTtl(), '->getTtl() uses the Expires header when no max-age is present'); 370 371 $response = new Response(); 372 $response->headers->set('Expires', $this->createDateTimeOneHourAgo()->format(DATE_RFC2822)); 373 $this->assertLessThan(0, $response->getTtl(), '->getTtl() returns negative values when Expires is in past'); 374 375 $response = new Response(); 376 $response->headers->set('Expires', $response->getDate()->format(DATE_RFC2822)); 377 $response->headers->set('Age', 0); 378 $this->assertSame(0, $response->getTtl(), '->getTtl() correctly handles zero'); 379 380 $response = new Response(); 381 $response->headers->set('Cache-Control', 'max-age=60'); 382 $this->assertEquals(60, $response->getTtl(), '->getTtl() uses Cache-Control max-age when present'); 383 } 384 385 public function testSetClientTtl() 386 { 387 $response = new Response(); 388 $response->setClientTtl(10); 389 390 $this->assertEquals($response->getMaxAge(), $response->getAge() + 10); 391 } 392 393 public function testGetSetProtocolVersion() 394 { 395 $response = new Response(); 396 397 $this->assertEquals('1.0', $response->getProtocolVersion()); 398 399 $response->setProtocolVersion('1.1'); 400 401 $this->assertEquals('1.1', $response->getProtocolVersion()); 402 } 403 404 public function testGetVary() 405 { 406 $response = new Response(); 407 $this->assertEquals(array(), $response->getVary(), '->getVary() returns an empty array if no Vary header is present'); 408 409 $response = new Response(); 410 $response->headers->set('Vary', 'Accept-Language'); 411 $this->assertEquals(array('Accept-Language'), $response->getVary(), '->getVary() parses a single header name value'); 412 413 $response = new Response(); 414 $response->headers->set('Vary', 'Accept-Language User-Agent X-Foo'); 415 $this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->getVary() parses multiple header name values separated by spaces'); 416 417 $response = new Response(); 418 $response->headers->set('Vary', 'Accept-Language,User-Agent, X-Foo'); 419 $this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->getVary() parses multiple header name values separated by commas'); 420 421 $vary = array('Accept-Language', 'User-Agent', 'X-foo'); 422 423 $response = new Response(); 424 $response->headers->set('Vary', $vary); 425 $this->assertEquals($vary, $response->getVary(), '->getVary() parses multiple header name values in arrays'); 426 427 $response = new Response(); 428 $response->headers->set('Vary', 'Accept-Language, User-Agent, X-foo'); 429 $this->assertEquals($vary, $response->getVary(), '->getVary() parses multiple header name values in arrays'); 430 } 431 432 public function testSetVary() 433 { 434 $response = new Response(); 435 $response->setVary('Accept-Language'); 436 $this->assertEquals(array('Accept-Language'), $response->getVary()); 437 438 $response->setVary('Accept-Language, User-Agent'); 439 $this->assertEquals(array('Accept-Language', 'User-Agent'), $response->getVary(), '->setVary() replace the vary header by default'); 440 441 $response->setVary('X-Foo', false); 442 $this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->setVary() doesn\'t wipe out earlier Vary headers if replace is set to false'); 443 } 444 445 public function testDefaultContentType() 446 { 447 $headerMock = $this->getMockBuilder('Symfony\Component\HttpFoundation\ResponseHeaderBag')->setMethods(array('set'))->getMock(); 448 $headerMock->expects($this->at(0)) 449 ->method('set') 450 ->with('Content-Type', 'text/html'); 451 $headerMock->expects($this->at(1)) 452 ->method('set') 453 ->with('Content-Type', 'text/html; charset=UTF-8'); 454 455 $response = new Response('foo'); 456 $response->headers = $headerMock; 457 458 $response->prepare(new Request()); 459 } 460 461 public function testContentTypeCharset() 462 { 463 $response = new Response(); 464 $response->headers->set('Content-Type', 'text/css'); 465 466 // force fixContentType() to be called 467 $response->prepare(new Request()); 468 469 $this->assertEquals('text/css; charset=UTF-8', $response->headers->get('Content-Type')); 470 } 471 472 public function testPrepareDoesNothingIfContentTypeIsSet() 473 { 474 $response = new Response('foo'); 475 $response->headers->set('Content-Type', 'text/plain'); 476 477 $response->prepare(new Request()); 478 479 $this->assertEquals('text/plain; charset=UTF-8', $response->headers->get('content-type')); 480 } 481 482 public function testPrepareDoesNothingIfRequestFormatIsNotDefined() 483 { 484 $response = new Response('foo'); 485 486 $response->prepare(new Request()); 487 488 $this->assertEquals('text/html; charset=UTF-8', $response->headers->get('content-type')); 489 } 490 491 public function testPrepareSetContentType() 492 { 493 $response = new Response('foo'); 494 $request = Request::create('/'); 495 $request->setRequestFormat('json'); 496 497 $response->prepare($request); 498 499 $this->assertEquals('application/json', $response->headers->get('content-type')); 500 } 501 502 public function testPrepareRemovesContentForHeadRequests() 503 { 504 $response = new Response('foo'); 505 $request = Request::create('/', 'HEAD'); 506 507 $length = 12345; 508 $response->headers->set('Content-Length', $length); 509 $response->prepare($request); 510 511 $this->assertEquals('', $response->getContent()); 512 $this->assertEquals($length, $response->headers->get('Content-Length'), 'Content-Length should be as if it was GET; see RFC2616 14.13'); 513 } 514 515 public function testPrepareRemovesContentForInformationalResponse() 516 { 517 $response = new Response('foo'); 518 $request = Request::create('/'); 519 520 $response->setContent('content'); 521 $response->setStatusCode(101); 522 $response->prepare($request); 523 $this->assertEquals('', $response->getContent()); 524 $this->assertFalse($response->headers->has('Content-Type')); 525 $this->assertFalse($response->headers->has('Content-Type')); 526 527 $response->setContent('content'); 528 $response->setStatusCode(304); 529 $response->prepare($request); 530 $this->assertEquals('', $response->getContent()); 531 $this->assertFalse($response->headers->has('Content-Type')); 532 $this->assertFalse($response->headers->has('Content-Length')); 533 } 534 535 public function testPrepareRemovesContentLength() 536 { 537 $response = new Response('foo'); 538 $request = Request::create('/'); 539 540 $response->headers->set('Content-Length', 12345); 541 $response->prepare($request); 542 $this->assertEquals(12345, $response->headers->get('Content-Length')); 543 544 $response->headers->set('Transfer-Encoding', 'chunked'); 545 $response->prepare($request); 546 $this->assertFalse($response->headers->has('Content-Length')); 547 } 548 549 public function testPrepareSetsPragmaOnHttp10Only() 550 { 551 $request = Request::create('/', 'GET'); 552 $request->server->set('SERVER_PROTOCOL', 'HTTP/1.0'); 553 554 $response = new Response('foo'); 555 $response->prepare($request); 556 $this->assertEquals('no-cache', $response->headers->get('pragma')); 557 $this->assertEquals('-1', $response->headers->get('expires')); 558 559 $request->server->set('SERVER_PROTOCOL', 'HTTP/1.1'); 560 $response = new Response('foo'); 561 $response->prepare($request); 562 $this->assertFalse($response->headers->has('pragma')); 563 $this->assertFalse($response->headers->has('expires')); 564 } 565 566 public function testSetCache() 567 { 568 $response = new Response(); 569 //array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public') 570 try { 571 $response->setCache(array('wrong option' => 'value')); 572 $this->fail('->setCache() throws an InvalidArgumentException if an option is not supported'); 573 } catch (\Exception $e) { 574 $this->assertInstanceOf('InvalidArgumentException', $e, '->setCache() throws an InvalidArgumentException if an option is not supported'); 575 $this->assertContains('"wrong option"', $e->getMessage()); 576 } 577 578 $options = array('etag' => '"whatever"'); 579 $response->setCache($options); 580 $this->assertEquals($response->getEtag(), '"whatever"'); 581 582 $now = $this->createDateTimeNow(); 583 $options = array('last_modified' => $now); 584 $response->setCache($options); 585 $this->assertEquals($response->getLastModified()->getTimestamp(), $now->getTimestamp()); 586 587 $options = array('max_age' => 100); 588 $response->setCache($options); 589 $this->assertEquals($response->getMaxAge(), 100); 590 591 $options = array('s_maxage' => 200); 592 $response->setCache($options); 593 $this->assertEquals($response->getMaxAge(), 200); 594 595 $this->assertTrue($response->headers->hasCacheControlDirective('public')); 596 $this->assertFalse($response->headers->hasCacheControlDirective('private')); 597 598 $response->setCache(array('public' => true)); 599 $this->assertTrue($response->headers->hasCacheControlDirective('public')); 600 $this->assertFalse($response->headers->hasCacheControlDirective('private')); 601 602 $response->setCache(array('public' => false)); 603 $this->assertFalse($response->headers->hasCacheControlDirective('public')); 604 $this->assertTrue($response->headers->hasCacheControlDirective('private')); 605 606 $response->setCache(array('private' => true)); 607 $this->assertFalse($response->headers->hasCacheControlDirective('public')); 608 $this->assertTrue($response->headers->hasCacheControlDirective('private')); 609 610 $response->setCache(array('private' => false)); 611 $this->assertTrue($response->headers->hasCacheControlDirective('public')); 612 $this->assertFalse($response->headers->hasCacheControlDirective('private')); 613 614 $response->setCache(array('immutable' => true)); 615 $this->assertTrue($response->headers->hasCacheControlDirective('immutable')); 616 617 $response->setCache(array('immutable' => false)); 618 $this->assertFalse($response->headers->hasCacheControlDirective('immutable')); 619 } 620 621 public function testSendContent() 622 { 623 $response = new Response('test response rendering', 200); 624 625 ob_start(); 626 $response->sendContent(); 627 $string = ob_get_clean(); 628 $this->assertContains('test response rendering', $string); 629 } 630 631 public function testSetPublic() 632 { 633 $response = new Response(); 634 $response->setPublic(); 635 636 $this->assertTrue($response->headers->hasCacheControlDirective('public')); 637 $this->assertFalse($response->headers->hasCacheControlDirective('private')); 638 } 639 640 public function testSetImmutable() 641 { 642 $response = new Response(); 643 $response->setImmutable(); 644 645 $this->assertTrue($response->headers->hasCacheControlDirective('immutable')); 646 } 647 648 public function testIsImmutable() 649 { 650 $response = new Response(); 651 $response->setImmutable(); 652 653 $this->assertTrue($response->isImmutable()); 654 } 655 656 public function testSetExpires() 657 { 658 $response = new Response(); 659 $response->setExpires(null); 660 661 $this->assertNull($response->getExpires(), '->setExpires() remove the header when passed null'); 662 663 $now = $this->createDateTimeNow(); 664 $response->setExpires($now); 665 666 $this->assertEquals($response->getExpires()->getTimestamp(), $now->getTimestamp()); 667 } 668 669 public function testSetLastModified() 670 { 671 $response = new Response(); 672 $response->setLastModified($this->createDateTimeNow()); 673 $this->assertNotNull($response->getLastModified()); 674 675 $response->setLastModified(null); 676 $this->assertNull($response->getLastModified()); 677 } 678 679 public function testIsInvalid() 680 { 681 $response = new Response(); 682 683 try { 684 $response->setStatusCode(99); 685 $this->fail(); 686 } catch (\InvalidArgumentException $e) { 687 $this->assertTrue($response->isInvalid()); 688 } 689 690 try { 691 $response->setStatusCode(650); 692 $this->fail(); 693 } catch (\InvalidArgumentException $e) { 694 $this->assertTrue($response->isInvalid()); 695 } 696 697 $response = new Response('', 200); 698 $this->assertFalse($response->isInvalid()); 699 } 700 701 /** 702 * @dataProvider getStatusCodeFixtures 703 */ 704 public function testSetStatusCode($code, $text, $expectedText) 705 { 706 $response = new Response(); 707 708 $response->setStatusCode($code, $text); 709 710 $statusText = new \ReflectionProperty($response, 'statusText'); 711 $statusText->setAccessible(true); 712 713 $this->assertEquals($expectedText, $statusText->getValue($response)); 714 } 715 716 public function getStatusCodeFixtures() 717 { 718 return array( 719 array('200', null, 'OK'), 720 array('200', false, ''), 721 array('200', 'foo', 'foo'), 722 array('199', null, 'unknown status'), 723 array('199', false, ''), 724 array('199', 'foo', 'foo'), 725 ); 726 } 727 728 public function testIsInformational() 729 { 730 $response = new Response('', 100); 731 $this->assertTrue($response->isInformational()); 732 733 $response = new Response('', 200); 734 $this->assertFalse($response->isInformational()); 735 } 736 737 public function testIsRedirectRedirection() 738 { 739 foreach (array(301, 302, 303, 307) as $code) { 740 $response = new Response('', $code); 741 $this->assertTrue($response->isRedirection()); 742 $this->assertTrue($response->isRedirect()); 743 } 744 745 $response = new Response('', 304); 746 $this->assertTrue($response->isRedirection()); 747 $this->assertFalse($response->isRedirect()); 748 749 $response = new Response('', 200); 750 $this->assertFalse($response->isRedirection()); 751 $this->assertFalse($response->isRedirect()); 752 753 $response = new Response('', 404); 754 $this->assertFalse($response->isRedirection()); 755 $this->assertFalse($response->isRedirect()); 756 757 $response = new Response('', 301, array('Location' => '/good-uri')); 758 $this->assertFalse($response->isRedirect('/bad-uri')); 759 $this->assertTrue($response->isRedirect('/good-uri')); 760 } 761 762 public function testIsNotFound() 763 { 764 $response = new Response('', 404); 765 $this->assertTrue($response->isNotFound()); 766 767 $response = new Response('', 200); 768 $this->assertFalse($response->isNotFound()); 769 } 770 771 public function testIsEmpty() 772 { 773 foreach (array(204, 304) as $code) { 774 $response = new Response('', $code); 775 $this->assertTrue($response->isEmpty()); 776 } 777 778 $response = new Response('', 200); 779 $this->assertFalse($response->isEmpty()); 780 } 781 782 public function testIsForbidden() 783 { 784 $response = new Response('', 403); 785 $this->assertTrue($response->isForbidden()); 786 787 $response = new Response('', 200); 788 $this->assertFalse($response->isForbidden()); 789 } 790 791 public function testIsOk() 792 { 793 $response = new Response('', 200); 794 $this->assertTrue($response->isOk()); 795 796 $response = new Response('', 404); 797 $this->assertFalse($response->isOk()); 798 } 799 800 public function testIsServerOrClientError() 801 { 802 $response = new Response('', 404); 803 $this->assertTrue($response->isClientError()); 804 $this->assertFalse($response->isServerError()); 805 806 $response = new Response('', 500); 807 $this->assertFalse($response->isClientError()); 808 $this->assertTrue($response->isServerError()); 809 } 810 811 public function testHasVary() 812 { 813 $response = new Response(); 814 $this->assertFalse($response->hasVary()); 815 816 $response->setVary('User-Agent'); 817 $this->assertTrue($response->hasVary()); 818 } 819 820 public function testSetEtag() 821 { 822 $response = new Response('', 200, array('ETag' => '"12345"')); 823 $response->setEtag(); 824 825 $this->assertNull($response->headers->get('Etag'), '->setEtag() removes Etags when call with null'); 826 } 827 828 /** 829 * @dataProvider validContentProvider 830 */ 831 public function testSetContent($content) 832 { 833 $response = new Response(); 834 $response->setContent($content); 835 $this->assertEquals((string) $content, $response->getContent()); 836 } 837 838 /** 839 * @expectedException \UnexpectedValueException 840 * @dataProvider invalidContentProvider 841 */ 842 public function testSetContentInvalid($content) 843 { 844 $response = new Response(); 845 $response->setContent($content); 846 } 847 848 public function testSettersAreChainable() 849 { 850 $response = new Response(); 851 852 $setters = array( 853 'setProtocolVersion' => '1.0', 854 'setCharset' => 'UTF-8', 855 'setPublic' => null, 856 'setPrivate' => null, 857 'setDate' => $this->createDateTimeNow(), 858 'expire' => null, 859 'setMaxAge' => 1, 860 'setSharedMaxAge' => 1, 861 'setTtl' => 1, 862 'setClientTtl' => 1, 863 ); 864 865 foreach ($setters as $setter => $arg) { 866 $this->assertEquals($response, $response->{$setter}($arg)); 867 } 868 } 869 870 public function testNoDeprecationsAreTriggered() 871 { 872 new DefaultResponse(); 873 $this->getMockBuilder(Response::class)->getMock(); 874 875 // we just need to ensure that subclasses of Response can be created without any deprecations 876 // being triggered if the subclass does not override any final methods 877 $this->addToAssertionCount(1); 878 } 879 880 public function validContentProvider() 881 { 882 return array( 883 'obj' => array(new StringableObject()), 884 'string' => array('Foo'), 885 'int' => array(2), 886 ); 887 } 888 889 public function invalidContentProvider() 890 { 891 return array( 892 'obj' => array(new \stdClass()), 893 'array' => array(array()), 894 'bool' => array(true, '1'), 895 ); 896 } 897 898 protected function createDateTimeOneHourAgo() 899 { 900 return $this->createDateTimeNow()->sub(new \DateInterval('PT1H')); 901 } 902 903 protected function createDateTimeOneHourLater() 904 { 905 return $this->createDateTimeNow()->add(new \DateInterval('PT1H')); 906 } 907 908 protected function createDateTimeNow() 909 { 910 $date = new \DateTime(); 911 912 return $date->setTimestamp(time()); 913 } 914 915 protected function provideResponse() 916 { 917 return new Response(); 918 } 919 920 /** 921 * @see http://github.com/zendframework/zend-diactoros for the canonical source repository 922 * 923 * @author Fábio Pacheco 924 * @copyright Copyright (c) 2015-2016 Zend Technologies USA Inc. (http://www.zend.com) 925 * @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License 926 */ 927 public function ianaCodesReasonPhrasesProvider() 928 { 929 if (!in_array('https', stream_get_wrappers(), true)) { 930 $this->markTestSkipped('The "https" wrapper is not available'); 931 } 932 933 $ianaHttpStatusCodes = new \DOMDocument(); 934 935 libxml_set_streams_context(stream_context_create(array( 936 'http' => array( 937 'method' => 'GET', 938 'timeout' => 30, 939 ), 940 ))); 941 942 $ianaHttpStatusCodes->load('https://www.iana.org/assignments/http-status-codes/http-status-codes.xml'); 943 if (!$ianaHttpStatusCodes->relaxNGValidate(__DIR__.'/schema/http-status-codes.rng')) { 944 self::fail('Invalid IANA\'s HTTP status code list.'); 945 } 946 947 $ianaCodesReasonPhrases = array(); 948 949 $xpath = new \DOMXPath($ianaHttpStatusCodes); 950 $xpath->registerNamespace('ns', 'http://www.iana.org/assignments'); 951 952 $records = $xpath->query('//ns:record'); 953 foreach ($records as $record) { 954 $value = $xpath->query('.//ns:value', $record)->item(0)->nodeValue; 955 $description = $xpath->query('.//ns:description', $record)->item(0)->nodeValue; 956 957 if (in_array($description, array('Unassigned', '(Unused)'), true)) { 958 continue; 959 } 960 961 if (preg_match('/^([0-9]+)\s*\-\s*([0-9]+)$/', $value, $matches)) { 962 for ($value = $matches[1]; $value <= $matches[2]; ++$value) { 963 $ianaCodesReasonPhrases[] = array($value, $description); 964 } 965 } else { 966 $ianaCodesReasonPhrases[] = array($value, $description); 967 } 968 } 969 970 return $ianaCodesReasonPhrases; 971 } 972 973 /** 974 * @dataProvider ianaCodesReasonPhrasesProvider 975 */ 976 public function testReasonPhraseDefaultsAgainstIana($code, $reasonPhrase) 977 { 978 $this->assertEquals($reasonPhrase, Response::$statusTexts[$code]); 979 } 980} 981 982class StringableObject 983{ 984 public function __toString() 985 { 986 return 'Foo'; 987 } 988} 989 990class DefaultResponse extends Response 991{ 992} 993 994class ExtendedResponse extends Response 995{ 996 public function setLastModified(\DateTime $date = null) 997 { 998 } 999 1000 public function getDate() 1001 { 1002 } 1003} 1004