Skip to content

Commit 9dc4405

Browse files
committed
Add server logic for partial paths when matching link-metadata.
1 parent c1191f2 commit 9dc4405

1 file changed

Lines changed: 77 additions & 44 deletions

File tree

src/Server.php

Lines changed: 77 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -762,61 +762,46 @@ private function handleLinkMetadata(Response $response, string $path)
762762
{
763763
$returnResponse = null;
764764

765-
// @FIXME: If a $path is a file underneath a directory that has been marked as moved, the response should also be a redirect/404/410.
766-
767765
if ($this->hasDescribedBy($path)) {
768766
$linkMeta = $this->parseLinkedMetadata($path);
769767

770-
if (count($linkMeta) > 1) {
771-
throw Exception::create(self::ERROR_MULTIPLE_LINK_METADATA_FOUND, [$path]);
768+
if (isset($linkMeta['type'], $linkMeta['url'])) {
769+
$returnResponse = $this->buildLinkMetadataResponse($response, $linkMeta['type'], $linkMeta['url']);
772770
}
773-
774-
$returnResponse = $this->buildLinkMetadataResponse($linkMeta, $response);
775771
}
776772

777773
return $returnResponse;
778774
}
779775

780-
private function buildLinkMetadataResponse(array $linkMeta, Response $response)
776+
private function buildLinkMetadataResponse(Response $response, $type, $url = null)
781777
{
782-
$returnResponse = null;
783-
784-
if (count($linkMeta) > 0) {
785-
$linkMetaType = array_key_first($linkMeta);
786-
787-
$type = substr($linkMetaType, strrpos($linkMetaType, '#') + 1);
788-
789-
$linkMetaValue = reset($linkMeta);
790-
$value = array_pop($linkMetaValue);
791-
$url = $value['value'] ?? null;
792-
793-
switch ($type) {
794-
case 'deleted':
795-
$returnResponse = $response->withStatus(404);
796-
break;
778+
switch ($type) {
779+
case 'deleted':
780+
$returnResponse = $response->withStatus(404);
781+
break;
797782

798-
case 'forget':
799-
$returnResponse = $response->withStatus(410);
800-
break;
783+
case 'forget':
784+
$returnResponse = $response->withStatus(410);
785+
break;
801786

802-
case 'redirectPermanent':
803-
if ($url === null) {
804-
throw Exception::create(self::ERROR_CAN_NOT_REDIRECT_WITHOUT_URL, [$type]);
805-
}
806-
$returnResponse = $response->withHeader('Location', $url)->withStatus(308);
807-
break;
787+
case 'redirectPermanent':
788+
if ($url === null) {
789+
throw Exception::create(self::ERROR_CAN_NOT_REDIRECT_WITHOUT_URL, [$type]);
790+
}
791+
$returnResponse = $response->withHeader('Location', $url)->withStatus(308);
792+
break;
808793

809-
case 'redirectTemporary':
810-
if ($url === null) {
811-
throw Exception::create(self::ERROR_CAN_NOT_REDIRECT_WITHOUT_URL, [$type]);
812-
}
813-
$returnResponse = $response->withHeader('Location', $url)->withStatus(307);
814-
break;
794+
case 'redirectTemporary':
795+
if ($url === null) {
796+
throw Exception::create(self::ERROR_CAN_NOT_REDIRECT_WITHOUT_URL, [$type]);
797+
}
798+
$returnResponse = $response->withHeader('Location', $url)->withStatus(307);
799+
break;
815800

816-
default:
817-
// No (known) Link Metadata present = follow regular logic
818-
break;
819-
}
801+
default:
802+
// No (known) Link Metadata present = follow regular logic
803+
$returnResponse = null;
804+
break;
820805
}
821806

822807
return $returnResponse;
@@ -844,10 +829,11 @@ private function parseLinkedMetadata(string $path)
844829

845830
$toRdfPhp = $graph->toRdfPhp();
846831

847-
$path = ltrim($path, '/');
832+
$rdfPaths = array_keys($toRdfPhp);
833+
$foundPath = $this->findPath($rdfPaths, $path);
848834

849-
if (isset($toRdfPhp[$path])) {
850-
$linkMeta = array_filter($toRdfPhp[$path], static function ($key) {
835+
if (isset($toRdfPhp[$foundPath])) {
836+
$filteredRdfData = array_filter($toRdfPhp[$foundPath], static function ($key) {
851837
$uris = implode('|', [
852838
'pdsinterop.org/solid-link-metadata/links.ttl',
853839
'purl.org/pdsinterop/link-metadata',
@@ -856,8 +842,55 @@ private function parseLinkedMetadata(string $path)
856842
return (bool) preg_match("#({$uris})#",
857843
$key);
858844
}, ARRAY_FILTER_USE_KEY);
845+
846+
if (count($filteredRdfData) > 1) {
847+
throw Exception::create(self::ERROR_MULTIPLE_LINK_METADATA_FOUND, [$path]);
848+
}
849+
850+
if (count($filteredRdfData) > 0) {
851+
$linkMetaType = array_key_first($filteredRdfData);
852+
$type = substr($linkMetaType, strrpos($linkMetaType, '#') + 1);
853+
854+
$linkMetaValue = reset($filteredRdfData);
855+
$value = array_pop($linkMetaValue);
856+
$url = $value['value'] ?? null;
857+
858+
if ($path !== $foundPath) {
859+
// Change the path from the request to the redirect (or not found) path
860+
$url = substr_replace($path,
861+
$url,
862+
strpos($path, $foundPath),
863+
strlen($foundPath));
864+
}
865+
866+
$linkMeta = [
867+
'type' => $type,
868+
'url' => $url,
869+
];
870+
}
859871
}
860872

861873
return $linkMeta;
862874
}
875+
876+
private function findPath(array $rdfPaths, string $path)
877+
{
878+
$path = ltrim($path, '/');
879+
880+
foreach ($rdfPaths as $rdfPath) {
881+
if (
882+
strrpos($path, $rdfPath) === 0
883+
&& $this->filesystem->has($rdfPath)
884+
) {
885+
// @FIXME: We have no way of knowing if the file is a directory or a file.
886+
// This means that, unless we make a trialing slash `/` required,
887+
// (using the example for `forget.ttl`) forget.ttl/foo.txt will
888+
// also work although semantically is should not
889+
$path = $rdfPath;
890+
break;
891+
}
892+
}
893+
894+
return $path;
895+
}
863896
}

0 commit comments

Comments
 (0)