From 1b7bb0a370ef47284b10ab85189bde52cafb8c35 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Wed, 14 Sep 2016 22:23:25 -0400 Subject: [PATCH] Add requestTarget implementation. Add getRequestTarget() as a replacement for here(false). There is no replacement for here(true). From looking at the core code, we generally rely on here(false), and Router::url() to get the full path. I'm deprecating here() as it has a high degree of overlap with the new method and I think we can get away with one fewer method. --- src/Network/Request.php | 56 ++++++++++++++++++++++++++ tests/TestCase/Network/RequestTest.php | 30 ++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/src/Network/Request.php b/src/Network/Request.php index e0ede25bdd1..abc5b13d804 100644 --- a/src/Network/Request.php +++ b/src/Network/Request.php @@ -202,6 +202,13 @@ class Request implements ArrayAccess */ protected $protocol; + /** + * The request target if overridden + * + * @var string|null + */ + protected $requestTarget; + /** * Wrapper method to create a new request from PHP superglobals. * @@ -921,6 +928,7 @@ public function addPaths(array $paths) * * @param bool $base Include the base path, set to false to trim the base path off. * @return string The current request URL including query string args. + * @deprecated 3.4.0 This method will be removed in 4.0.0. You should use getRequestTarget() instead. */ public function here($base = true) { @@ -1791,6 +1799,54 @@ public function withUri(UriInterface $uri) return $new; } + /** + * Create a new instance with a specific request-target. + * + * You can use this method to overwrite the request target that is + * inferred from the request's Uri. This also lets you change the request + * target's form to an absolute-form, authority-form or asterisk-form + * + * @link http://tools.ietf.org/html/rfc7230#section-2.7 (for the various + * request-target forms allowed in request messages) + * @param string $target The request target. + * @return static + */ + public function withRequestTarget($target) + { + $new = clone $this; + $new->requestTarget = $target; + + return $new; + } + + /** + * Retrieves the request's target. + * + * Retrieves the message's request-target either as it was requested, + * or as set with `withRequestTarget()`. By default this will return the + * application relative path without base directory, and the query string + * defined in the SERVER environment. + * + * @return string + */ + public function getRequestTarget() + { + if ($this->requestTarget !== null) { + return $this->requestTarget; + } + + $target = $this->uri->getPath(); + if ($this->uri->getQuery()) { + $target .= '?' . $this->uri->getQuery(); + } + + if (empty($target)) { + $target = '/'; + } + + return $target; + } + /** * Array access read implementation * diff --git a/tests/TestCase/Network/RequestTest.php b/tests/TestCase/Network/RequestTest.php index af5257cf33f..eb28055de1f 100644 --- a/tests/TestCase/Network/RequestTest.php +++ b/tests/TestCase/Network/RequestTest.php @@ -3183,6 +3183,36 @@ public function testWithoutAttributesDenyEmulatedProperties($prop) $request->withoutAttribute($prop); } + /** + * Test the requestTarget methods. + * + * @return void + */ + public function testWithRequestTarget() + { + $request = new Request([ + 'environment' => [ + 'REQUEST_URI' => '/articles/view/1', + 'QUERY_STRING' => 'comments=1&open=0' + ], + 'base' => '/basedir' + ]); + $this->assertEquals( + '/articles/view/1?comments=1&open=0', + $request->getRequestTarget(), + 'Should not include basedir.' + ); + + $new = $request->withRequestTarget('/articles/view/3'); + $this->assertNotSame($new, $request); + $this->assertEquals( + '/articles/view/1?comments=1&open=0', + $request->getRequestTarget(), + 'should be unchanged.' + ); + $this->assertEquals('/articles/view/3', $new->getRequestTarget(), 'reflects method call'); + } + /** * Data provider for emulated property tests. *