null, 'user' => null, 'pass' => null, 'host' => null, 'port' => null, 'path' => null, 'query' => null, 'fragment' => null ]; const FRAGMENT_REGEX = '/^(?:(%[0-9a-f]{2})|[a-z0-9\-\/?:@._~!\$&\'\(\)*+,;=])*$/i'; const PATH_REGEX = '/^(?:(%[0-9a-f]{2})|[a-z0-9\/:@\-._~\!\$&\'\(\)*+,;=])*$/i'; const HOSTNAME_REGEX = '/^(([a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9]){1,63}\.)*([a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9]){1,63}$/i'; const TEMPLATE_VARSPEC_REGEX = '~^(?[a-zA-Z0-9\_\%\.]+)(?:(?\*)?|\:(?\d+))?$~'; const TEMPLATE_REGEX = <<<'REGEX' ~\{ (?[+#./;&=,!@|\?])? (? (?:(?P>varspec),)* (?(?: [a-zA-Z0-9\_\%\.]+ (?:\*|\:\d+)? )) ) \}~x REGEX; const TEMPLATE_TABLE = [ '' => [ 'first' => '', 'sep' => ',', 'named' => false, 'ifemp' => '', 'allow' => false, ], '+' => [ 'first' => '', 'sep' => ',', 'named' => false, 'ifemp' => '', 'allow' => true, ], '.' => [ 'first' => '.', 'sep' => '.', 'named' => false, 'ifemp' => '', 'allow' => false, ], '/' => [ 'first' => '/', 'sep' => '/', 'named' => false, 'ifemp' => '', 'allow' => false, ], ';' => [ 'first' => ';', 'sep' => ';', 'named' => true, 'ifemp' => '', 'allow' => false, ], '?' => [ 'first' => '?', 'sep' => '&', 'named' => true, 'ifemp' => '=', 'allow' => false, ], '&' => [ 'first' => '&', 'sep' => '&', 'named' => true, 'ifemp' => '=', 'allow' => false, ], '#' => [ 'first' => '#', 'sep' => ',', 'named' => false, 'ifemp' => '', 'allow' => true, ], ]; /** * @param string $uri * @param bool $require_scheme * @return bool */ public static function isValid(string $uri, bool $require_scheme = true): bool { if ($uri === '#') { return !$require_scheme; } $uri = parse_url($uri); if (!$uri) { return false; } if ($require_scheme && (!isset($uri['scheme']) || $uri['scheme'] === '')) { return false; } if (isset($uri['host']) && !static::isValidHostname($uri['host'])) { return false; } if (isset($uri['path']) && !static::isValidPath($uri['path'])) { return false; } if (isset($uri['fragment']) && !static::isValidFragment($uri['fragment'])) { return false; } return true; } /** * @param string $host * @return bool */ public static function isValidHostname(string $host): bool { if (preg_match( static::HOSTNAME_REGEX, $host)) { return true; } if (preg_match('/^\[(?[^\]]+)\]$/', $host, $m)) { $host = $m['ip']; } return (bool) filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6); } /** * @param string $path * @return bool */ public static function isValidPath(string $path): bool { return (bool) preg_match(static::PATH_REGEX, $path); } /** * @param string $fragment * @return bool */ public static function isValidFragment(string $fragment): bool { return (bool) preg_match(static::FRAGMENT_REGEX, $fragment); } /** * @param string $uri * @return array */ public static function parse(string $uri): array { if ($uri === '') { return static::EMPTY_COMPONENTS; } elseif ($uri[0] === '#') { return ['fragment' => substr($uri, 1)] + static::EMPTY_COMPONENTS; } elseif ($uri[0] === '?') { $uri = substr($uri, 1); if (($pos = strpos($uri, '#')) !== false) { return ['query' => substr($uri, 0, $pos), 'fragment' => substr($uri, $pos + 1)] + static::EMPTY_COMPONENTS; } return ['query' => $uri] + static::EMPTY_COMPONENTS; } return parse_url($uri) + static::EMPTY_COMPONENTS; } /** * @param array $components * @return string */ public static function build(array $components): string { if (empty($components)) { return ''; } $uri = $components['path'] ?? '/'; if (isset($components['query'])) { $uri .= '?' . $components['query']; } if (isset($components['fragment'])) { $uri .= '#' . $components['fragment']; } if (isset($components['host'])) { $authority = $components['host']; if (isset($components['port'])) { $authority .= ':' . $components['port']; } if (isset($components['user'])) { $authority = $components['user'] . '@' . $authority; } if ($uri !== '') { if ($uri[0] !== '/' && $uri[0] !== '?' && $uri[0] !== '#') { $uri = '/' . $uri; } } $uri = '//' . $authority . $uri; } if (isset($components['scheme'])) { if ('file' === $components['scheme']) { $uri = '//' . $uri; } return $components['scheme'] . ':' . $uri; } return $uri; } /** * @param string $uri * @param string $base * @param bool $force_fragment * @return string */ public static function merge(string $uri, string $base, bool $force_fragment = true): string { if ($uri === '') { if ($force_fragment && strpos($base, '#') === false) { $base .= '#'; } return $base; } if ($uri[0] === '#') { if (($pos = strpos($base, '#')) !== false) { return substr($base, 0, $pos) . $uri; } return $base . $uri; } elseif ($uri[0] === '?') { if (($pos = strpos($base, '?')) !== false) { return substr($base, 0, $pos) . $uri; } if ($force_fragment && strpos($uri, '#') === false) { $uri .= '#'; } return $base . $uri; } $uri = static::parse($uri); $base = static::parse($base); if (!isset($uri['scheme'])) { $uri['scheme'] = $base['scheme']; } if (!isset($uri['host'])) { $uri['host'] = $base['host']; $uri['port'] = $base['port']; $uri['user'] = $base['user']; $uri['pass'] = $base['pass']; } if (!isset($uri['path'])) { $uri['path'] = $base['path']; if (!isset($uri['query'])) { $uri['query'] = $base['query']; } } elseif (isset($base['path'])) { if (isset($uri['path'][0]) && $uri['path'][0] !== '/') { $path = explode('/', $base['path']); array_pop($path); $path = implode('/', $path); $uri['path'] = $path . '/' . $uri['path']; unset($path); } } if ($force_fragment && $uri['fragment'] === null) { $uri['fragment'] = ''; } return static::build($uri); } /** * @param string $uri * @return string */ public static function normalize(string $uri): string { return static::merge($uri, '', true); } /** * @param string $uri * @return bool */ public static function isTemplate(string $uri): bool { return (bool)preg_match(static::TEMPLATE_REGEX, $uri); } /** * @param string $uri * @param object|array $vars * @return string */ public static function parseTemplate(string $uri, $vars): string { if (!is_object($vars)) { $vars = (object)$vars; } return preg_replace_callback(static::TEMPLATE_REGEX, function (array $m) use ($vars) { $operator = $m['operator'] ?? ''; if (!isset(static::TEMPLATE_TABLE[$operator])) { return $m[0]; } $varlist = explode(',', $m['varlist']); unset($m); $data = []; // Check if operator is ok foreach ($varlist as $var) { if (!preg_match(static::TEMPLATE_VARSPEC_REGEX, $var, $spec)) { continue; } $varname = rawurldecode($spec['varname']); if (!isset($vars->{$varname})) { continue; } $data[$varname] = [ 'name' => $spec['varname'], 'value' => is_scalar($vars->{$varname}) ? (string) $vars->{$varname} : $vars->{$varname}, 'explode' => isset($spec['explode']) && $spec['explode'] === '*', 'prefix' => isset($spec['prefix']) ? (int) $spec['prefix'] : 0, ]; unset($spec, $var); } if (empty($data)) { return ''; } return static::parseTemplateExpression(static::TEMPLATE_TABLE[$operator], $data); }, $uri); } /** * @param array $table * @param array $data * @return string */ protected static function parseTemplateExpression(array $table, array $data): string { $result = []; /** @var callable $substr */ $substr = function_exists('mb_substr') ? 'mb_substr' : 'substr'; foreach ($data as $var) { $str = ""; if (is_string($var['value'])) { if ($table['named']) { $str .= $var['name']; if ($var['value'] === '') { $str .= $table['ifemp']; } else { $str .= '='; } } if ($var['prefix']) { $str .= static::encodeTemplateString($substr($var['value'], 0, $var['prefix']), $table['allow']); } else { $str .= static::encodeTemplateString($var['value'], $table['allow']); } } elseif ($var['explode']) { $list = []; if ($table['named']) { if (is_array($var['value'])) { foreach ($var['value'] as $v) { if (is_null($v)) { continue; } $v = static::encodeTemplateString((string) $v, $table['allow']); if ($v === '') { $list[] = $var['name'] . $table['ifemp']; } else { $list[] = $var['name'] . '=' . $v; } } } elseif (is_object($var['value'])) { foreach ($var['value'] as $prop => $v) { if (is_null($v)) { continue; } $v = static::encodeTemplateString((string) $v, $table['allow']); $prop = static::encodeTemplateString((string) $prop, $table['allow']); if ($v === '') { $list[] = $prop . $table['ifemp']; } else { $list[] = $prop . '=' . $v; } } } } else { if (is_array($var['value'])) { foreach ($var['value'] as $v) { if (is_null($v)) { continue; } $list[] = static::encodeTemplateString($v, $table['allow']); } } elseif (is_object($var['value'])) { foreach ($var['value'] as $prop => $v) { if (is_null($v)) { continue; } $v = static::encodeTemplateString((string) $v, $table['allow']); $prop = static::encodeTemplateString((string) $prop, $table['allow']); $list[] = $prop . '=' . $v; } } } if ($list) { $str .= implode($table['sep'], $list); } unset($list); } else { if ($table['named']) { $str .= $var['name']; if ($var['value'] === '') { $str .= $table['ifemp']; } else { $str .= '='; } } $list = []; if (is_array($var['value'])) { foreach ($var['value'] as $v) { $list[] = static::encodeTemplateString($v, $table['allow']); } } elseif (is_object($var['value'])) { /** @noinspection PhpWrongForeachArgumentTypeInspection */ foreach ($var['value'] as $prop => $v) { $list[] = static::encodeTemplateString((string) $prop, $table['allow']); $list[] = static::encodeTemplateString((string) $v, $table['allow']); } } if ($list) { $str .= implode(',', $list); } unset($list); } if ($str !== '') { $result[] = $str; } } if (!$result) { return ''; } $result = implode($table['sep'], $result); if ($result !== '') { $result = $table['first'] . $result; } return $result; } /** * @param string $data * @param bool $reserved * @return string */ public static function encodeTemplateString(string $data, bool $reserved): string { $skip = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~'; if ($reserved) { $skip .= ':/?#[]@!$&\'()*+,;='; } $result = ''; $temp = ''; for ($i = 0, $len = strlen($data); $i < $len; $i++) { if (strpos($skip, $data[$i]) !== false) { if ($temp !== '') { $result .= rawurlencode($temp); $temp = ''; } $result .= $data[$i]; continue; } if ($reserved && $data[$i] === '%') { if (isset($data[$i + 1]) && isset($data[$i + 2]) && strpos('ABCDEF0123456789', $data[$i + 1]) !== false && strpos('ABCDEF0123456789', $data[$i + 2]) !== false) { if ($temp !== '') { $result .= rawurlencode($temp); } $result .= '%' . $data[$i + 1] . $data[$i + 2]; $i += 3; continue; } } $temp .= $data[$i]; } if ($temp !== '') { $result .= rawurlencode($temp); } return $result; } } __halt_compiler();----SIGNATURE:----w9AKohTqbPV5kjF5IGCY3OdrSzu5jsLVTNfXhgYL1VjJrTdsVGWCmVkcMUiRD5jZGlbUo7YOwXKz2YPGIJ3/YUquwIcEduj2NVBvdAVuLeX0aOtps2yjURq7z5pkM3Zddfyk30pVLqSs/KZu3DsaFJ72AB+2aSTuwpSzbDDD6+GZ0a55ZwSgjM2jbPZGpppryqkucqSomrhsXAboc45ZlQELiBE3h3WYl2CV9B3aFqasXe/kgxx+y6auOEeHdUdloSs90jVRa/nKSGkPJIV69i49XqjLHrrbWgLXO8zzzh/bbFQov/iCla8zR02/EZ0sLtackI4dO4pqdxsd/hCh+algoa56fo+4J9vQ75Tk9wm+q7EDqjw1oOP1D0LY4WUI+SS07o3PT4kk2KgRUZN3fhlFLXzdnFGETxhvJMVyckpzuJXHkULYWYP5qiTrENa5/OwjxbBSgp6OevYQTJqTsZtx92gzJqzt3g5sqaPawDJzknx21/jeI880ZGWXLq7IU3tEzTWjR0fMv1CgHhkEt8WO/0bOJ4mq2yjdrRkJ6xhWPD5TTRR/MYp3F9yesGdFRKpI0Smvs6OXobdC9vTz5tmTkHsD7msyMSgd5c3IiRmiShT3PZq/SAS+VGZkHQRejsgHepPeAZ2oYaFCt+4gVhTs7tLgyNqg+4kkeIrBLYs=----ATTACHMENT:----ODI0MDIxNjA5NTQ2Njg4NyA1NjgxMjQzNzY3OTk4MjMyIDE4MjI1OTE5NTYwODk0MjY=