urljoin.php 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. <?php
  2. /*
  3. A spiritual port of Python's urlparse.urljoin() function to PHP. Why this isn't in the standard library is anyone's guess.
  4. Author: fluffy, http://beesbuzz.biz/
  5. Latest version at: https://github.com/plaidfluff/php-urljoin
  6. */
  7. function urljoin($base, $rel) {
  8. if (!$base) {
  9. return $rel;
  10. }
  11. if (!$rel) {
  12. return $base;
  13. }
  14. $uses_relative = array('', 'ftp', 'http', 'gopher', 'nntp', 'imap',
  15. 'wais', 'file', 'https', 'shttp', 'mms',
  16. 'prospero', 'rtsp', 'rtspu', 'sftp',
  17. 'svn', 'svn+ssh', 'ws', 'wss');
  18. $pbase = parse_url($base);
  19. $prel = parse_url($rel);
  20. if (array_key_exists('path', $pbase) && $pbase['path'] === '/') {
  21. unset($pbase['path']);
  22. }
  23. if (isset($prel['scheme'])) {
  24. if ($prel['scheme'] != $pbase['scheme'] || in_array($prel['scheme'], $uses_relative) == false) {
  25. return $rel;
  26. }
  27. }
  28. $merged = array_merge($pbase, $prel);
  29. // Handle relative paths:
  30. // 'path/to/file.ext'
  31. // './path/to/file.ext'
  32. if (array_key_exists('path', $prel) && substr($prel['path'], 0, 1) != '/') {
  33. // Normalize: './path/to/file.ext' => 'path/to/file.ext'
  34. if (substr($prel['path'], 0, 2) === './') {
  35. $prel['path'] = substr($prel['path'], 2);
  36. }
  37. if (array_key_exists('path', $pbase)) {
  38. $dir = preg_replace('@/[^/]*$@', '', $pbase['path']);
  39. $merged['path'] = $dir . '/' . $prel['path'];
  40. } else {
  41. $merged['path'] = '/' . $prel['path'];
  42. }
  43. }
  44. if(array_key_exists('path', $merged)) {
  45. // Get the path components, and remove the initial empty one
  46. $pathParts = explode('/', $merged['path']);
  47. array_shift($pathParts);
  48. $path = [];
  49. $prevPart = '';
  50. foreach ($pathParts as $part) {
  51. if ($part == '..' && count($path) > 0) {
  52. // Cancel out the parent directory (if there's a parent to cancel)
  53. $parent = array_pop($path);
  54. // But if it was also a parent directory, leave it in
  55. if ($parent == '..') {
  56. array_push($path, $parent);
  57. array_push($path, $part);
  58. }
  59. } else if ($prevPart != '' || ($part != '.' && $part != '')) {
  60. // Don't include empty or current-directory components
  61. if ($part == '.') {
  62. $part = '';
  63. }
  64. array_push($path, $part);
  65. }
  66. $prevPart = $part;
  67. }
  68. $merged['path'] = '/' . implode('/', $path);
  69. }
  70. $ret = '';
  71. if (isset($merged['scheme'])) {
  72. $ret .= $merged['scheme'] . ':';
  73. }
  74. if (isset($merged['scheme']) || isset($merged['host'])) {
  75. $ret .= '//';
  76. }
  77. if (isset($prel['host'])) {
  78. $hostSource = $prel;
  79. } else {
  80. $hostSource = $pbase;
  81. }
  82. // username, password, and port are associated with the hostname, not merged
  83. if (isset($hostSource['host'])) {
  84. if (isset($hostSource['user'])) {
  85. $ret .= $hostSource['user'];
  86. if (isset($hostSource['pass'])) {
  87. $ret .= ':' . $hostSource['pass'];
  88. }
  89. $ret .= '@';
  90. }
  91. $ret .= $hostSource['host'];
  92. if (isset($hostSource['port'])) {
  93. $ret .= ':' . $hostSource['port'];
  94. }
  95. }
  96. if (isset($merged['path'])) {
  97. $ret .= $merged['path'];
  98. }
  99. if (isset($prel['query'])) {
  100. $ret .= '?' . $prel['query'];
  101. }
  102. if (isset($prel['fragment'])) {
  103. $ret .= '#' . $prel['fragment'];
  104. }
  105. return $ret;
  106. }