[PHP] 2 absolute URL's naar relatieve URL's

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • remcotolsma
  • Registratie: December 2005
  • Laatst online: 08-09 11:11
Ejz,

Ik ben al een tijdje opzoek naar een functie waaraan ik 2 URL's kan meegeven en dat die wanneer mogelijk is een relatieve URL terug geeft.

Voorbeeld:
Ik geef onderstaande URL's mee aan de functie.

URL 1: http://gathering.tweakers.net/forum/insert_topic/14
URL 2: http://gathering.tweakers.net/

De functie zou dan ../../ terug moeten geven.

Andersom

URL 1: http://gathering.tweakers.net/
URL 2: http://gathering.tweakers.net/forum/insert_topic/14

De functie zou dan forum/insert_topic/14 terug moeten geven.

Bij 2 totaal verschillende URL's.
URL 1: http://gathering.tweakers.net/
URL 2: http://www.nu.nl/

Zal die gewoon http://www.nu.nl/ terug moeten geven.

Ik heb al op Google en de PHP website gezocht naar een functie die iets vergelijkbaars doet, maar helaas niks gevonden. Het moet ongeveer de omgekeerde zijn van de realpath functie van PHP.

Ik heb ondertussen zelf al wel iets geschreven, maar ik vroeg me af of er niet een ingebakken functie voor zo iets is. De code moet namelijk zo efficiënt / snel / goed mogelijk zijn, ik heb de twijfels over mijn eigen code.

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
function determineRelativeURL($url = NULL, $relativeUrl = NULL)
{
  if(!isset($url) || !isset($relativeUrl))
    return '';

  $urlData = parse_url($url);
  $relativeUrlData = parse_url($relativeUrl);

  if($urlData['scheme'] != $relativeUrlData['scheme'])
    return $relativeUrl;
  else if($urlData['port'] != $relativeUrlData['port'])
    return $relativeUrl;
  
  $urlPath = $urlData['path'];
  $relativeUrlPath = $relativeUrlData['path'];

  $urlPath = substr($urlPath, 0, strrpos($urlPath, '/'));

  $relativeUrlFile = substr($relativeUrlPath, strrpos($relativeUrlPath, '/') + 1);
  $relativeUrlPath = substr($relativeUrlPath, 0, strrpos($relativeUrlPath, '/'));

  $urlPath = preg_split('/[\/]+/', $urlPath, -1, PREG_SPLIT_NO_EMPTY);
  $relativeUrlPath = preg_split('/[\/]+/', $relativeUrlPath, -1, PREG_SPLIT_NO_EMPTY);

  $numberItems = max(count($urlPath), count($relativeUrlPath));

  for($i = 0; $i < $numberItems; $i++)
  {
    if(!array_key_exists($i, $relativeUrlPath))
      $relativePath .= '../';
    else if(!array_key_exists($i, $urlPath))
      $relativePath .= $relativeUrlPath[$i].'/';
  }

  return $relativePath.$relativeUrlFile;
}

$url = 'http://gathering.tweakers.net/';
$relativeUrl = 'http://gathering.tweakers.net/forum/insert_topic/14';

echo determineRelativeURL($url, $relativeUrl);


Bovenstaande zal forum/insert_topic/14 weergeven in de browser, wat voor zover ik weet juist is. Ik heb de functie nog niet uitgebreid getest, maar hij doet tot nu wel wat ik wil. Toch maak ik het liefst gebruik van standaard functies...
  1. Is er een standaard functies bekend binnen PHP die iets vergelijkbaars doet?
  2. Zijn er PHP extensies / pakketten (ZEND, PEAR, etc.) die hier functies voor hebben?
  3. Wat kan er eventueel beter aan mij functie?

[ Voor 5% gewijzigd door remcotolsma op 25-04-2006 16:00 ]


Acties:
  • 0 Henk 'm!

  • prototype
  • Registratie: Juni 2001
  • Niet online

prototype

Cheer Bear

1. Nee, afaik niet.
2. Zie 1.
3. Ongetwijfeld. Te beginnen bij het feit dat je ook rekening moet houden met '?'. Stel de volgende URL: http://www.bla.com/asdasdas?sadas/asdasdas/dsadsa/. Je moet dus niet lukraak enkel checken op '/', maar ook of er niet een ? aan vooraf gaat, want dan hoort het gewoon bij de querystring ipv bij de baseurl.

Acties:
  • 0 Henk 'm!

  • remcotolsma
  • Registratie: December 2005
  • Laatst online: 08-09 11:11
Volgens mij zorgt de functie parse_url er wel voor dat ik niet te maken krijg met de query gedeelte van een URL. Ik pak immers alleen het path gedeelte van een URL.

Zie regel 6 en 7 en 14 en 15 van bovenstaande code fragment.

[ Voor 7% gewijzigd door remcotolsma op 25-04-2006 16:16 ]


Acties:
  • 0 Henk 'm!

  • prototype
  • Registratie: Juni 2001
  • Niet online

prototype

Cheer Bear

remcotolsma schreef op dinsdag 25 april 2006 @ 16:15:
Volgens mij zorgt de functie parse_url er wel voor dat ik niet te maken krijg met de query gedeelte van een URL. Ik pak immers alleen het path gedeelte van een URL.

Zie regel 6 en 7 en 14 en 15 van bovenstaande code fragment.
Ah mea culpa, wederom over het hoofd gezien :o Even kijken of ik er nog wat op kan vinden :)
Check je btw wel uberhaupt op 'host' ? Volgens mij niet, of middels rare constructies.
En wat nou als je het volgende hebt? http://www.tweakers.net/a en http://www.tweakers.net/b ? Moet hij dan ../b returnen?

[ Voor 23% gewijzigd door prototype op 25-04-2006 16:42 ]


Acties:
  • 0 Henk 'm!

  • T-MOB
  • Registratie: Maart 2001
  • Nu online
Het tweede geval kan natuurlijk heel eenvoudig met substr:
PHP:
1
2
$relUri = substr($url1, strlen($url2));
$basesDoMatch = ($url2 == substr($url1,0,strlen($url2));


Voor het eerste geval kun je explode()'en op '/' en dan met sizeof bepalen hoevaak je een dir omhoog moet.
PHP:
1
2
3
4
5
6
7
8
$parts1 = explode('/', $url1);
$parts2 = explode('/' $url2);
$dirsUp = sizeof($parts1) - sizeof($parts2);
$relPath = '';
for ($i = 0; $i<$dirsUp; $i++) {
  $relPath .= '../';
}
$basesDoMatch = ($url2 == substr($url1,0,strlen($url2));

finetune door de juiste zaken voor 1 en 2 te pakken en door mbhv trim() slashes goed af te handelen.

Regeren is vooruitschuiven


Acties:
  • 0 Henk 'm!

  • remcotolsma
  • Registratie: December 2005
  • Laatst online: 08-09 11:11
T-MOB schreef op dinsdag 25 april 2006 @ 18:11:
Het tweede geval kan natuurlijk heel eenvoudig met substr:
PHP:
1
2
$relUri = substr($url1, strlen($url2));
$basesDoMatch = ($url2 == substr($url1,0,strlen($url2));


Voor het eerste geval kun je explode()'en op '/' en dan met sizeof bepalen hoevaak je een dir omhoog moet.
PHP:
1
2
3
4
5
6
7
8
$parts1 = explode('/', $url1);
$parts2 = explode('/' $url2);
$dirsUp = sizeof($parts1) - sizeof($parts2);
$relPath = '';
for ($i = 0; $i<$dirsUp; $i++) {
  $relPath .= '../';
}
$basesDoMatch = ($url2 == substr($url1,0,strlen($url2));

finetune door de juiste zaken voor 1 en 2 te pakken en door mbhv trim() slashes goed af te handelen.
Op die manier ga je er van uit dat $url2 altijd korter is als $url1. Dit hoeft niet altijd zo te zijn, het kan ook zijn dat je directories 'omhoog' moet.

Maar iig bedankt, je hebt me wel op wat ideetjes gebracht waardoor de functie misschien beter / efficiënter gemaakt kan worden.
prototype schreef op dinsdag 25 april 2006 @ 16:20:
[...]

Ah mea culpa, wederom over het hoofd gezien :o Even kijken of ik er nog wat op kan vinden :)
Check je btw wel uberhaupt op 'host' ? Volgens mij niet, of middels rare constructies.
En wat nou als je het volgende hebt? http://www.tweakers.net/a en http://www.tweakers.net/b ? Moet hij dan ../b returnen?
Nee dan moet ie b of ./b returnen, dat doet ie wel goed.

[ Voor 4% gewijzigd door remcotolsma op 25-04-2006 19:23 ]


Acties:
  • 0 Henk 'm!

  • T-MOB
  • Registratie: Maart 2001
  • Nu online
remcotolsma schreef op dinsdag 25 april 2006 @ 19:22:

Op die manier ga je er van uit dat $url2 altijd korter is als $url1. Dit hoeft niet altijd zo te zijn, het kan ook zijn dat je directories 'omhoog' moet.
Daarom zei ik ook "voor het tweede geval...", waarmee ik op je tweede voorbeeld doelde. Anyway, de functie zoals ik hem ongeveer voor ogen zag:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function uriCompare($base, $derived) {
    $baseLength = strlen($base);
    $derivedLength = strlen($derived);
    
    if(substr($derived,0,$baseLength) == $base) {
        return substr($derived, $baseLength);
    
    } elseif (substr($base,0,$derivedLength) == $derived) {
        $partdiff = substr_count($base, '/') - substr_count($derived, '/');
        return str_repeat('../', $partdiff);
    }       
    
    return $derived;
}

Regeren is vooruitschuiven


Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 02:21

Janoz

Moderator Devschuur®

!litemod

Je functie gaat de mist in als je http://blaat/foo/ en http://blaat/bar/ hebt.

een handiger algoritme lijkt mij om bij beide strings vanaf het begin te beginnen. Je splitst ze op in onderdelen (domein, losse directories en opgevraagd bestand met parameters). vervolgens kijk je of de verschillende elementen gelijk zijn. Als ze verschillend of afgelopen zijn begin je met ../ te schrijven en een mapnamen over te nemen.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • T-MOB
  • Registratie: Maart 2001
  • Nu online
Janoz schreef op dinsdag 25 april 2006 @ 21:30:
Je functie gaat de mist in als je http://blaat/foo/ en http://blaat/bar/ hebt.
Nou ja, de mist in. Hij is er niet voor gemaakt en uit de TS kon ik ook niet opmaken dat het de bedoeling was dat het ondersteund moest worden.
een handiger algoritme lijkt mij om bij beide strings vanaf het begin te beginnen. Je splitst ze op in onderdelen (domein, losse directories en opgevraagd bestand met parameters). vervolgens kijk je of de verschillende elementen gelijk zijn. Als ze verschillend of afgelopen zijn begin je met ../ te schrijven en een mapnamen over te nemen.
Wat de preciese elementen van het URL zijn (host, file, parameters) lijkt me niet zo relevant voor het probleem. Het enige dat je hoeft te weten is welk teken de delen onderscheidt in een url. Van daar uit kun je bepalen in hoeverre de URL's gelijk zijn en of je dirs omhoog moet. Een werkende implementatie:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function uriCompare($base, $derived) {

    $offset=0;
    while( ($pos = strpos($base, '/', $offset)) !== FALSE) {
        $offset = $pos+1;
        if ( substr($base,0,$offset) == substr($derived,0,$offset)) {
            $sharedBaseLength = $offset;
        } else {
            break;
        }
    }

    if (empty($sharedBaseLength)) { return $derived; }

    $baseResidu = substr($base, $sharedBaseLength);
    $dirsUp = substr_count($baseResidu, '/');

    $derivedResidu = substr($derived, $sharedBaseLength);

    return str_repeat('../', $dirsUp) .$derivedResidu;
}

(of bedoelde je zoiets?)

Regeren is vooruitschuiven


Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 02:21

Janoz

Moderator Devschuur®

!litemod

De drie delen zijn wel degelijk relevant. Als het domein verschillend is moet het complete domein blijven bestaan. Van de overige 3 onderdelen mogen alleen de directories behandeld worden. Een bestand kan worden genegeerd en de parameters moeten altijd over genomen worden. Alle 4 de onderdelen moeten dus op een andere manier worden behandeld.

Je tweede stukje code gaat volgens mij de mist in bij de eerste / in http:// en wanneer er een / na een ? staat.

[ Voor 16% gewijzigd door Janoz op 26-04-2006 09:11 ]

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • remcotolsma
  • Registratie: December 2005
  • Laatst online: 08-09 11:11
En als je bijvoorbeeld de volgende 2 URL's hebt:

URL 1: http://www.test.nl/test123/hoi/test4/test5/test6/hallo/
URL 2: http://www.test.nl/test123/hallo/test4/test5/test6/hoi/

Als je in deze URL's alleen naar de scheiding tekens gaat kijken gaat het niet goed lijkt mij. Wat hij moet returnen is ../../../../../hallo/test4/test5/test6/hoi/. De functie die ik als eerste geplaatst had doet dit ook (nog) niet goed.

[ Voor 13% gewijzigd door remcotolsma op 26-04-2006 11:35 ]


Acties:
  • 0 Henk 'm!

  • T-MOB
  • Registratie: Maart 2001
  • Nu online
Janoz schreef op woensdag 26 april 2006 @ 08:58:
Alle 4 de onderdelen moeten dus op een andere manier worden behandeld.

Je tweede stukje code gaat volgens mij de mist in bij de eerste / in http:// en wanneer er een / na een ? staat.
Ach natuurlijk, je hebt gelijk.
Hij gaat inderdaad de mist in op het scheme. Op http://tweakers.net en http://nu.nl vindt ie inderdaad http:// bij beiden gelijk en geeft ie dus "../tweakers.net" terug :(.
remcotolsma schreef op woensdag 26 april 2006 @ 09:30:
URL 1: http://www.test.nl/test123/hoi/test4/test5/test6/hallo/
URL 2: http://www.test.nl/test123/hallo/test4/test5/test6/hoi/
Als je in deze URL's alleen naar de scheiding tekens gaat kijken gaat het niet goed lijkt mij. Wat hij moet returnen is ../../../../../hallo/test4/test5/test6/hoi/.
Nee, dat doet ie prima. De functie zoekt in het eerste blok naar welk deel de twee urls gelijk hebben. $sharedBase wordt dus "http://www.test.nl/test123/". In de rest van URL1 wordt vervolgens geteld hoevaak er een '/' voorkomt. Zoveel directories moeten we dus omhoog (../../../../../). De rest van URL 2 kun je daar achter plakken om het relatieve url compleet te maken. (hallo/test4/test5/test6/hoi/).

Afijn een aangepaste versie. Deze werkt wel volledig, querystring en dergelijk worden echter niet teruggeven. Wil je die ook dan kun je de uitvoer aanpassen door de parameters uit $derivedInfo op de juiste plaats weer in de uitvoer te plakken.
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function uriCompare($base, $derived) {

    $baseInfo = parse_url($base);
    $derivedInfo = parse_url($derived);
    
    if ($baseInfo['scheme'] != $derivedInfo['scheme'] ||
        $baseInfo['host'] != $derivedInfo['host']) {
        return $derived;
    }
    
    $base = (empty($baseInfo['path'])) ? '/' : $baseInfo['path'];
    $derived = (empty($derivedInfo['path'])) ? '/' : $derivedInfo['path'];
    
    while( ($pos = strpos($base, '/', $offset)) !== FALSE) {
        $pos++;
        if ( substr($base,0,$pos) == substr($derived,0,$pos)) {
            $sharedBaseLength = $pos;
            $offset = $pos;
        } else {
            break;
        }
    } 

    $baseResidu = substr($base, $sharedBaseLength);
    $dirsUp = substr_count($baseResidu, '/');
    $dirPart = ($dirsUp > 0) ? str_repeat('../', $dirsUp) : './';

    $derivedResidu = substr($derived, $sharedBaseLength);
    return $dirPart .$derivedResidu;
}

[ Voor 19% gewijzigd door T-MOB op 26-04-2006 12:42 . Reden: kleine verbetering toegevoegd ]

Regeren is vooruitschuiven


Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 02:21

Janoz

Moderator Devschuur®

!litemod

T-MOB schreef op woensdag 26 april 2006 @ 12:09:
[...]

Ach natuurlijk, je hebt gelijk.
Hij gaat inderdaad de mist in op het scheme. Op http://tweakers.net en http://nu.nl vindt ie inderdaad http:// bij beiden gelijk en geeft ie dus "../tweakers.net" terug :(.
Zo zie je maar weer dat de 4 onderdelen inderdaad appart behandeld moeten worden ;). Zoals je zelf al aangeeft worden de parameters niet meegenomen (op zich kun je het makkelijk toevoegen door dit aan het einde weer toe te voegen). Blijft van de 4 onderdelen die ik noemde nog 1 over, namelijk het bestand. Daar hield je al rekening mee omdat je in je algoritme een directory herkend door een / aan het einde. Een file wordt dus anders behandeld ;).

Maar inderdaad. Het script is verder inderdaad wat ik bedoelde en lijkt mij ook de meest efficiente manier om dit probleem aan te pakken.

[ Voor 5% gewijzigd door Janoz op 26-04-2006 13:40 ]

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'

Pagina: 1