使用PHP解决URL中的相对路径问题

4

例子1:domain.com/dir_1/dir_2/dir_3/./../../../
应该在浏览器中自然地解析为 = domain.com/

例子2:domain.com/dir_1/dir_2/dir_3/./../../../test/././../new_dir/
应该解析为domain.com/new_dir/

例子3:domain.com/dir_1/dir_2/dir_3/./../../../test/dir4/../final
应该解析为domain.com/test/final

我如何迭代字符串来完成这个操作?我觉得for()循环在这个点会混淆。

使用PHP将相对路径转换为绝对URLPHP:如何解析相对URL中提供的答案对我在这种情况下不起作用。由于目标是清理我已经拥有的内容,因此我不应该需要一个参考点(基础)。

这不是PHP-打开流失败:没有此类文件或目录的重复问题。


你能详细说明一下你的具体情况吗?也许有更简单的方法来实现你想要做的事情。 - Pekka
可能是重复的问题:PHP - Failed to open stream : No such file or directory - Dan Is Fiddling By Firelight
丹·尼利,这不是重复。 - Jacob Cruz
2个回答

10

这个问题比你想象的要简单得多。你只需要在/字符上使用explode()函数,并使用堆栈解析所有的单独段落即可。当你从左到右遍历数组时,如果看到了.,就什么也不用做。如果看到了..,就从堆栈中弹出一个元素。否则,将一个元素推入堆栈。

$str = 'domain.com/dir_1/dir_2/dir_3/./../../../';
$array = explode( '/', $str);
$domain = array_shift( $array);

$parents = array();
foreach( $array as $dir) {
    switch( $dir) {
        case '.':
        // Don't need to do anything here
        break;
        case '..':
            array_pop( $parents);
        break;
        default:
            $parents[] = $dir;
        break;
    }
}

echo $domain . '/' . implode( '/', $parents);

这将正确地解析所有测试用例中的URL。
请注意,错误检查留给用户自行完成(即当$parents堆栈为空并尝试从其中弹出内容时)。

1
你需要的是一个“replaceDots”函数。它的工作原理是记住最后一个有效项目的位置,然后如果遇到点,则删除该项目。完整的描述在这里“Remove Dot Segments” https://www.rfc-editor.org/rfc/rfc3986。在RFC页面上搜索“Remove Dot Segments”。
你需要不止一个循环。内部循环向前扫描并查看下一个部分,然后如果是点,则跳过当前部分等,但这可能比较棘手。或者考虑将其分成几个部分,然后按照算法进行操作。
  1. 只要输入缓冲区不为空,按以下方式循环:

    A. 如果输入缓冲区以"../"或"./"的前缀开头,则从输入缓冲区中删除该前缀;否则,

    B. 如果输入缓冲区以"/./"或"/."的前缀开头,其中"."是完整的路径段,则将该前缀替换为"/"在输入缓冲区中;否则,

    C. 如果输入缓冲区以"/../"或"/.."的前缀开头,其中".. "是完整路径段,则将该前缀替换为"/"在输入缓冲区中,并从输出缓冲区中删除最后一个段及其前面的"/"(如果有);否则,

    D. 如果输入缓冲区仅包含"."或"..",则从输入缓冲区中删除该内容;否则,

    E. 将输入缓冲区中的第一个路径段移动到输出缓冲区的末尾,包括初始的"/"字符(如果有)和任何后续字符,但不包括下一个"/"字符或输入缓冲区的结尾。

  2. 最后,输出缓冲区作为remove_dot_segments函数的结果返回。

它的工作原理是记住最后一个有效项目的位置,然后如果您得到了点,则删除该项目。完整的描述在这里。
这是我用C++的版本...
ortl_funcimp(len_t) _str_remove_dots(char_t* s, len_t len) {
  len_t x,yy;
  /*
    Modifies the string in place by copying parts back. Not
    sure if this is the best way to do it since it involves
    many copies for deep relatives like ../../../../../myFile.cpp

    For each ../ it does one copy back. If the loop was implemented
    using writing into a buffer, you would have to do both, so this
    seems to be the best technique.
  */
  __checklenx(s,len);
  x = 0;
  while (x < len) {
    if (s[x] == _c('.')) {
      x++;
      if (x < len) {
        if (s[x] == _c('.')) {
          x++;
          if (x < len) {
            if (s[x] == _c('/')) { // ../
              mem_move(&s[x],&s[x-2],(len-x)*sizeof(char_t));
              len -= 2;
              x -= 2;
            }
            else x++;
          }
          else len -= 2;// .. only
        }
        else if (s[x] == _c('/')){ // ./
          mem_move(&s[x],&s[x-1],(len-x)*sizeof(char_t));
          len--;
          x--;
        }
      }
      else --len;// terminating '.', remove
    }
    else if (s[x] == _c('/')) {
      x++;
      if (x < len) {
        if (s[x] == _c('.')) {
          x++;
          if (x < len) {
            if (s[x] == _c('/')) { // /./
              mem_move(&s[x],&s[x-2],(len-x)*sizeof(char_t));
              len -= 2;
              x -= 2;
            }
            else if (s[x] == _c('.')) { // /..
              x++;
              if (x < len) { //
                if (s[x] == _c('/')) {// /../
                  yy = x;
                  x -= 3;
                  if (x > 0) x--;
                  while ((x > 0) && (s[x] != _c('/'))) x--;
                  mem_move(&s[yy],&s[x],(len-yy) * sizeof(char_t));
                  len -= (yy - x);
                }
                else {
                  x++;
                }
              }
              else {// ends with /..
                x -= 3;
                if (x > 0) x--;
                while (x > 0 && s[x] != _c('/')) x--;
                s[x] = _c('/');
                x++;
                len = x;
              }
            }
            else x++;
          }
          else len--;// ends with /.
        }
        else x++;
      }
    }
    else x++;
  }
  return len;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接