@@ -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