J. King
5 years ago
5 changed files with 13 additions and 217 deletions
@ -1,131 +0,0 @@ |
|||||
<?php |
|
||||
/** @license MIT |
|
||||
* Copyright 2017 J. King, Dustin Wilson et al. |
|
||||
* See LICENSE and AUTHORS files for details */ |
|
||||
|
|
||||
declare(strict_types=1); |
|
||||
namespace JKingWeb\Arsse\REST; |
|
||||
|
|
||||
use JKingWeb\Arsse\Misc\ValueInfo; |
|
||||
|
|
||||
class Target { |
|
||||
public $relative = false; |
|
||||
public $index = false; |
|
||||
public $path = []; |
|
||||
public $query = ""; |
|
||||
public $fragment = ""; |
|
||||
|
|
||||
public function __construct(string $target) { |
|
||||
$target = $this->parseFragment($target); |
|
||||
$target = $this->parseQuery($target); |
|
||||
$this->path = $this->parsePath($target); |
|
||||
} |
|
||||
|
|
||||
public function __toString(): string { |
|
||||
$out = ""; |
|
||||
$path = []; |
|
||||
foreach ($this->path as $segment) { |
|
||||
if (is_null($segment)) { |
|
||||
if (!$path) { |
|
||||
$path[] = ".."; |
|
||||
} else { |
|
||||
continue; |
|
||||
} |
|
||||
} elseif ($segment === ".") { |
|
||||
$path[] = "%2E"; |
|
||||
} elseif ($segment === "..") { |
|
||||
$path[] = "%2E%2E"; |
|
||||
} else { |
|
||||
$path[] = rawurlencode(ValueInfo::normalize($segment, ValueInfo::T_STRING)); |
|
||||
} |
|
||||
} |
|
||||
$path = implode("/", $path); |
|
||||
if (!$this->relative) { |
|
||||
$out .= "/"; |
|
||||
} |
|
||||
$out .= $path; |
|
||||
if ($this->index && strlen($path)) { |
|
||||
$out .= "/"; |
|
||||
} |
|
||||
if (strlen($this->query)) { |
|
||||
$out .= "?".$this->query; |
|
||||
} |
|
||||
if (strlen($this->fragment)) { |
|
||||
$out .= "#".rawurlencode($this->fragment); |
|
||||
} |
|
||||
return $out; |
|
||||
} |
|
||||
|
|
||||
public static function normalize(string $target): string { |
|
||||
return (string) new self($target); |
|
||||
} |
|
||||
|
|
||||
protected function parseFragment(string $target): string { |
|
||||
// store and strip off any fragment identifier and return the target without a fragment |
|
||||
$pos = strpos($target, "#"); |
|
||||
if ($pos !== false) { |
|
||||
$this->fragment = rawurldecode(substr($target, $pos + 1)); |
|
||||
$target = substr($target, 0, $pos); |
|
||||
} |
|
||||
return $target; |
|
||||
} |
|
||||
|
|
||||
protected function parseQuery(string $target): string { |
|
||||
// store and strip off any query string and return the target without a query |
|
||||
// note that the function assumes any fragment identifier has already been stripped off |
|
||||
// unlike the other parts the query string is currently neither parsed nor normalized |
|
||||
$pos = strpos($target, "?"); |
|
||||
if ($pos !== false) { |
|
||||
$this->query = substr($target, $pos + 1); |
|
||||
$target = substr($target, 0, $pos); |
|
||||
} |
|
||||
return $target; |
|
||||
} |
|
||||
|
|
||||
protected function parsePath(string $target): array { |
|
||||
// note that the function assumes any fragment identifier or query has already been stripped off |
|
||||
// syntax-based normalization is applied to the path segments (see RFC 3986 sec. 6.2.2) |
|
||||
// duplicate slashes are NOT collapsed |
|
||||
if (substr($target, 0, 1) === "/") { |
|
||||
// if the path starts with a slash, strip it off |
|
||||
$target = substr($target, 1); |
|
||||
} else { |
|
||||
// otherwise this is a relative target |
|
||||
$this->relative = true; |
|
||||
} |
|
||||
if (!strlen($target)) { |
|
||||
// if the target is an empty string, this is an index target |
|
||||
$this->index = true; |
|
||||
} elseif (substr($target, -1, 1) === "/") { |
|
||||
// if the path ends in a slash, this is an index target and the slash should be stripped off |
|
||||
$this->index = true; |
|
||||
$target = substr($target, 0, strlen($target) -1); |
|
||||
} |
|
||||
// after stripping, explode the path parts |
|
||||
if (strlen($target)) { |
|
||||
$target = explode("/", $target); |
|
||||
$out = []; |
|
||||
// resolve relative path segments and decode each retained segment |
|
||||
foreach ($target as $index => $segment) { |
|
||||
if ($segment === ".") { |
|
||||
// self-referential segments can be ignored |
|
||||
continue; |
|
||||
} elseif ($segment === "..") { |
|
||||
if ($index == 0) { |
|
||||
// if the first path segment refers to its parent (which we don't know about) we cannot output a correct path, so we do the best we can |
|
||||
$out[] = null; |
|
||||
} else { |
|
||||
// for any other segments after the first we pop off the last stored segment |
|
||||
array_pop($out); |
|
||||
} |
|
||||
} else { |
|
||||
// any other segment is decoded and retained |
|
||||
$out[] = rawurldecode($segment); |
|
||||
} |
|
||||
} |
|
||||
return $out; |
|
||||
} else { |
|
||||
return []; |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,66 +0,0 @@ |
|||||
<?php |
|
||||
/** @license MIT |
|
||||
* Copyright 2017 J. King, Dustin Wilson et al. |
|
||||
* See LICENSE and AUTHORS files for details */ |
|
||||
|
|
||||
declare(strict_types=1); |
|
||||
namespace JKingWeb\Arsse\TestCase\REST; |
|
||||
|
|
||||
use JKingWeb\Arsse\REST\Target; |
|
||||
|
|
||||
/** @covers \JKingWeb\Arsse\REST\Target */ |
|
||||
class TestTarget extends \JKingWeb\Arsse\Test\AbstractTest { |
|
||||
|
|
||||
/** @dataProvider provideTargetUrls */ |
|
||||
public function testParseTargetUrl(string $target, array $path, bool $relative, bool $index, string $query, string $fragment, string $normalized) { |
|
||||
$test = new Target($target); |
|
||||
$this->assertEquals($path, $test->path, "Path does not match"); |
|
||||
$this->assertSame($path, $test->path, "Path does not match exactly"); |
|
||||
$this->assertSame($relative, $test->relative, "Relative flag does not match"); |
|
||||
$this->assertSame($index, $test->index, "Index flag does not match"); |
|
||||
$this->assertSame($query, $test->query, "Query does not match"); |
|
||||
$this->assertSame($fragment, $test->fragment, "Fragment does not match"); |
|
||||
} |
|
||||
|
|
||||
/** @dataProvider provideTargetUrls */ |
|
||||
public function testNormalizeTargetUrl(string $target, array $path, bool $relative, bool $index, string $query, string $fragment, string $normalized) { |
|
||||
$test = new Target(""); |
|
||||
$test->path = $path; |
|
||||
$test->relative = $relative; |
|
||||
$test->index = $index; |
|
||||
$test->query = $query; |
|
||||
$test->fragment = $fragment; |
|
||||
$this->assertSame($normalized, (string) $test); |
|
||||
$this->assertSame($normalized, Target::normalize($target)); |
|
||||
} |
|
||||
|
|
||||
public function provideTargetUrls() { |
|
||||
return [ |
|
||||
["/", [], false, true, "", "", "/"], |
|
||||
["", [], true, true, "", "", ""], |
|
||||
["/index.php", ["index.php"], false, false, "", "", "/index.php"], |
|
||||
["index.php", ["index.php"], true, false, "", "", "index.php"], |
|
||||
["/ook/", ["ook"], false, true, "", "", "/ook/"], |
|
||||
["ook/", ["ook"], true, true, "", "", "ook/"], |
|
||||
["/eek/../ook/", ["ook"], false, true, "", "", "/ook/"], |
|
||||
["eek/../ook/", ["ook"], true, true, "", "", "ook/"], |
|
||||
["/./ook/", ["ook"], false, true, "", "", "/ook/"], |
|
||||
["./ook/", ["ook"], true, true, "", "", "ook/"], |
|
||||
["/../ook/", [null,"ook"], false, true, "", "", "/../ook/"], |
|
||||
["../ook/", [null,"ook"], true, true, "", "", "../ook/"], |
|
||||
["0", ["0"], true, false, "", "", "0"], |
|
||||
["%6f%6F%6b", ["ook"], true, false, "", "", "ook"], |
|
||||
["%2e%2E%2f%2E%2Fook%2f", [".././ook/"], true, false, "", "", "..%2F.%2Fook%2F"], |
|
||||
["%2e%2E/%2E/ook%2f", ["..",".","ook/"], true, false, "", "", "%2E%2E/%2E/ook%2F"], |
|
||||
["...", ["..."], true, false, "", "", "..."], |
|
||||
["%2e%2e%2e", ["..."], true, false, "", "", "..."], |
|
||||
["/?", [], false, true, "", "", "/"], |
|
||||
["/#", [], false, true, "", "", "/"], |
|
||||
["/?#", [], false, true, "", "", "/"], |
|
||||
["#%2e", [], true, true, "", ".", "#."], |
|
||||
["?%2e", [], true, true, "%2e", "", "?%2e"], |
|
||||
["?%2e#%2f", [], true, true, "%2e", "/", "?%2e#%2F"], |
|
||||
["#%2e?%2f", [], true, true, "", ".?/", "#.%3F%2F"], |
|
||||
]; |
|
||||
} |
|
||||
} |
|
Loading…
Reference in new issue