mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-23 17:12:41 +00:00
131 lines
4.6 KiB
PHP
131 lines
4.6 KiB
PHP
<?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 [];
|
|
}
|
|
}
|
|
}
|