Skip to content

Commit

Permalink
Merge pull request #4644 from MGatner/uri-dots
Browse files Browse the repository at this point in the history
URI::removeDotSegments()
  • Loading branch information
MGatner authored May 5, 2021
2 parents 5cbf51c + 0275e69 commit a4edd89
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 116 deletions.
223 changes: 109 additions & 114 deletions system/HTTP/URI.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,113 @@ class URI

//--------------------------------------------------------------------

/**
* Builds a representation of the string from the component parts.
*
* @param string $scheme
* @param string $authority
* @param string $path
* @param string $query
* @param string $fragment
*
* @return string
*/
public static function createURIString(string $scheme = null, string $authority = null, string $path = null, string $query = null, string $fragment = null): string
{
$uri = '';
if (! empty($scheme))
{
$uri .= $scheme . '://';
}

if (! empty($authority))
{
$uri .= $authority;
}

if ($path !== '')
{
$uri .= substr($uri, -1, 1) !== '/' ? '/' . ltrim($path, '/') : ltrim($path, '/');
}

if ($query)
{
$uri .= '?' . $query;
}

if ($fragment)
{
$uri .= '#' . $fragment;
}

return $uri;
}

/**
* Used when resolving and merging paths to correctly interpret and
* remove single and double dot segments from the path per
* RFC 3986 Section 5.2.4
*
* @see http://tools.ietf.org/html/rfc3986#section-5.2.4
*
* @param string $path
*
* @return string
* @internal
*/
public static function removeDotSegments(string $path): string
{
if ($path === '' || $path === '/')
{
return $path;
}

$output = [];

$input = explode('/', $path);

if ($input[0] === '')
{
unset($input[0]);
$input = array_values($input);
}

// This is not a perfect representation of the
// RFC, but matches most cases and is pretty
// much what Guzzle uses. Should be good enough
// for almost every real use case.
foreach ($input as $segment)
{
if ($segment === '..')
{
array_pop($output);
}
elseif ($segment !== '.' && $segment !== '')
{
$output[] = $segment;
}
}

$output = implode('/', $output);
$output = trim($output, '/ ');

// Add leading slash if necessary
if (strpos($path, '/') === 0)
{
$output = '/' . $output;
}

// Add trailing slash if necessary
if ($output !== '/' && substr($path, -1, 1) === '/')
{
$output .= '/';
}

return $output;
}

//--------------------------------------------------------------------

/**
* Constructor.
*
Expand Down Expand Up @@ -592,51 +699,7 @@ public function __toString(): string
//--------------------------------------------------------------------

/**
* Builds a representation of the string from the component parts.
*
* @param string $scheme
* @param string $authority
* @param string $path
* @param string $query
* @param string $fragment
*
* @return string
*/
public static function createURIString(string $scheme = null, string $authority = null, string $path = null, string $query = null, string $fragment = null): string
{
$uri = '';
if (! empty($scheme))
{
$uri .= $scheme . '://';
}

if (! empty($authority))
{
$uri .= $authority;
}

if ($path !== '')
{
$uri .= substr($uri, -1, 1) !== '/' ? '/' . ltrim($path, '/') : ltrim($path, '/');
}

if ($query)
{
$uri .= '?' . $query;
}

if ($fragment)
{
$uri .= '#' . $fragment;
}

return $uri;
}

//--------------------------------------------------------------------

/**
* Parses the given string an saves the appropriate authority pieces.
* Parses the given string and saves the appropriate authority pieces.
*
* @param string $str
*
Expand Down Expand Up @@ -947,7 +1010,7 @@ protected function filterPath(string $path = null): string
$path = urldecode($path);

// Remove dot segments
$path = $this->removeDotSegments($path);
$path = self::removeDotSegments($path);

// Fix up some leading slash edge cases...
if (strpos($orig, './') === 0)
Expand Down Expand Up @@ -1140,74 +1203,6 @@ protected function mergePaths(URI $base, URI $reference): string

//--------------------------------------------------------------------

/**
* Used when resolving and merging paths to correctly interpret and
* remove single and double dot segments from the path per
* RFC 3986 Section 5.2.4
*
* @see http://tools.ietf.org/html/rfc3986#section-5.2.4
*
* @param string $path
*
* @return string
* @internal param \CodeIgniter\HTTP\URI $uri
*/
public function removeDotSegments(string $path): string
{
if ($path === '' || $path === '/')
{
return $path;
}

$output = [];

$input = explode('/', $path);

if ($input[0] === '')
{
unset($input[0]);
$input = array_values($input);
}

// This is not a perfect representation of the
// RFC, but matches most cases and is pretty
// much what Guzzle uses. Should be good enough
// for almost every real use case.
foreach ($input as $segment)
{
if ($segment === '..')
{
array_pop($output);
}
elseif ($segment !== '.' && $segment !== '')
{
$output[] = $segment;
}
}

$output = implode('/', $output);
$output = ltrim($output, '/ ');

if ($output !== '/')
{
// Add leading slash if necessary
if (strpos($path, '/') === 0)
{
$output = '/' . $output;
}

// Add trailing slash if necessary
if (substr($path, -1, 1) === '/')
{
$output .= '/';
}
}

return $output;
}

//--------------------------------------------------------------------

/**
* This is equivalent to the native PHP parse_str() function.
* This version allows the dot to be used as a key of the query string.
Expand Down
31 changes: 29 additions & 2 deletions tests/system/HTTP/URITest.php
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,34 @@ public function testSetAuthorityReconstitutes()
public function defaultDots()
{
return [
[
'',
'',
],
[
'/',
'/',
],
[
'.',
'',
],
[
'..',
'',
],
[
'/.',
'/',
],
[
'/..',
'/',
],
[
'//',
'/',
],
[
'/foo/..',
'/',
Expand Down Expand Up @@ -641,8 +669,7 @@ public function defaultDots()
*/
public function testRemoveDotSegments($path, $expected)
{
$uri = new URI();
$this->assertEquals($expected, $uri->removeDotSegments($path));
$this->assertEquals($expected, URI::removeDotSegments($path));
}

//--------------------------------------------------------------------
Expand Down

0 comments on commit a4edd89

Please sign in to comment.