Skip to content

Instantly share code, notes, and snippets.

@jrnickell
Last active December 22, 2015 03:28
Show Gist options
  • Save jrnickell/6410227 to your computer and use it in GitHub Desktop.
Save jrnickell/6410227 to your computer and use it in GitHub Desktop.
<?php
class ResourceLocator implements ResourceLocatorInterface
{
protected $defaultMap;
protected $schemes = [];
public function __construct(UriPathMapInterface $defaultMap = null)
{
$this->defaultMap = $defaultMap ?: new UriPathMap();
}
public function addScheme($scheme, $separator)
{
if (array_key_exists($scheme, $this->schemes)) {
$message = sprintf('Scheme name "%s" is already registered', $scheme);
throw new InvalidArgumentException($message);
}
$pathMap = clone $this->defaultMap;
$pathMap->setSeparator($separator);
$this->schemes[$scheme] = $pathMap;
}
public function getSchemes()
{
return array_keys($this->schemes);
}
public function hasScheme($scheme)
{
return array_key_exists($scheme, $this->schemes);
}
public function addPath($scheme, $prefix, $path, $prepend = false)
{
$this->verifyScheme($scheme);
$this->schemes[$scheme]->addPath($prefix, $path, $prepend);
}
public function getPaths($scheme, $prefix = null)
{
$this->verifyScheme($scheme);
return $this->schemes[$scheme]->getPaths($prefix);
}
public function hasPaths($scheme, $prefix = null)
{
if (!array_key_exists($scheme, $this->schemes)) {
return false;
}
return $this->schemes[$scheme]->hasPaths($prefix);
}
public function removePath($scheme, $prefix, $path)
{
if (!array_key_exists($scheme, $this->schemes)) {
return;
}
$this->schemes[$scheme]->removePath($prefix, $path);
}
public function findResource($uri)
{
return $this->matchUri($uri);
}
public function findResourceVariants($uri)
{
return $this->matchUri($uri, false);
}
protected function parseUri($uri)
{
$index = strpos($uri, ':///');
$scheme = substr($uri, 0, $index);
$path = substr($uri, $index + 4);
return [$scheme, $path];
}
protected function matchUri($uri, $first = true)
{
list($scheme, $path) = $this->parseUri($uri);
$this->verifyScheme($scheme);
$pathMappings = $this->schemes[$scheme]->getPaths();
$separator = $this->schemes[$scheme]->getSeparator();
$path = strtr($path, '/', $separator);
return $this->match($path, $pathMappings, $separator, $first);
}
protected function match($path, array $pathMappings, $separator, $first)
{
$pathLength = strlen($path);
$pathPrefix = $path;
$relativePath = '';
$cursor = -1;
$matches = [];
while (abs($cursor) !== $pathLength) {
if (isset($pathMappings[$pathPrefix])) {
foreach ((array) $pathMappings[$pathPrefix] as $basePath) {
$relativePath = strtr($relativePath, $separator, DIRECTORY_SEPARATOR);
$potential = $basePath.$relativePath;
if (is_readable($potential)) {
if ($first) {
return $potential;
} else {
$matches[] = $potential;
}
}
}
}
$cursor = strrpos($path, $separator, $cursor - 1) - $pathLength;
$relativePath = substr($path, $cursor);
$pathPrefix = substr($path, 0, max($cursor, -$pathLength + 1));
}
if ($first) {
return null;
}
return $matches;
}
protected function verifyScheme($scheme)
{
if (!array_key_exists($scheme, $this->schemes)) {
$message = sprintf('Scheme name "%s" is undefined', $scheme);
throw new InvalidArgumentException($message);
}
}
}
<?php
class UriPathMap implements UriPathMapInterface
{
protected $separator;
protected $paths = [];
public function __construct($separator = '/')
{
$this->separator = (string) $separator;
}
public function setSeparator($separator)
{
$this->separator = (string) $separator;
}
public function getSeparator()
{
return $this->separator;
}
public function addPath($prefix, $path, $prepend = false)
{
if (!array_key_exists($prefix, $this->paths)) {
$this->paths[$prefix] = [];
}
if ($prepend) {
array_unshift($this->paths[$prefix], $path);
} else {
$this->paths[$prefix][] = $path;
}
}
public function getPaths($prefix = null)
{
if (null === $prefix) {
return $this->paths;
}
if (!array_key_exists($prefix, $this->paths)) {
return [];
}
return $this->paths[$prefix];
}
public function hasPaths($prefix = null)
{
return count($this->getPaths($prefix)) > 0;
}
public function removePath($prefix, $path)
{
if (!array_key_exists($prefix, $this->paths)) {
return $this;
}
$key = array_search($path, $this->paths[$prefix]);
if (false !== $key) {
unset($this->paths[$prefix][$key]);
if (empty($this->paths[$prefix])) {
unset($this->paths[$prefix]);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment