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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
<?php
/*
A spiritual port of Python's urlparse.urljoin() function to PHP. Why this isn't in the standard library is anyone's guess.
Author: fluffy, http://beesbuzz.biz/
Latest version at: https://github.com/plaidfluff/php-urljoin
*/
function urljoin($base, $rel) {
if (!$base) {
return $rel;
}
if (!$rel) {
return $base;
}
$uses_relative = array('', 'ftp', 'http', 'gopher', 'nntp', 'imap',
'wais', 'file', 'https', 'shttp', 'mms',
'prospero', 'rtsp', 'rtspu', 'sftp',
'svn', 'svn+ssh', 'ws', 'wss');
$pbase = parse_url($base);
$prel = parse_url($rel);
if ($prel === false || preg_match('/^[a-z0-9\-.]*[^a-z0-9\-.:][a-z0-9\-.]*:/i', $rel)) {
/*
Either parse_url couldn't parse this, or the original URL
fragment had an invalid scheme character before the first :,
which can confuse parse_url
*/
$prel = array('path' => $rel);
}
if (array_key_exists('path', $pbase) && $pbase['path'] === '/') {
unset($pbase['path']);
}
if (isset($prel['scheme'])) {
if (
$prel['scheme'] != ($pbase['scheme'] ?? null)
|| in_array($prel['scheme'], $uses_relative) == false
) {
return $rel;
}
}
$merged = array_merge($pbase, $prel);
// Handle relative paths:
// 'path/to/file.ext'
// './path/to/file.ext'
if (array_key_exists('path', $prel) && substr($prel['path'], 0, 1) != '/') {
// Normalize: './path/to/file.ext' => 'path/to/file.ext'
if (substr($prel['path'], 0, 2) === './') {
$prel['path'] = substr($prel['path'], 2);
}
if (array_key_exists('path', $pbase)) {
$dir = preg_replace('@/[^/]*$@', '', $pbase['path']);
$merged['path'] = $dir . '/' . $prel['path'];
} else {
$merged['path'] = '/' . $prel['path'];
}
}
if(array_key_exists('path', $merged)) {
// Get the path components, and remove the initial empty one
$pathParts = explode('/', $merged['path']);
array_shift($pathParts);
$path = [];
$prevPart = '';
foreach ($pathParts as $part) {
if ($part == '..' && count($path) > 0) {
// Cancel out the parent directory (if there's a parent to cancel)
$parent = array_pop($path);
// But if it was also a parent directory, leave it in
if ($parent == '..') {
array_push($path, $parent);
array_push($path, $part);
}
} else if ($prevPart != '' || ($part != '.' && $part != '')) {
// Don't include empty or current-directory components
if ($part == '.') {
$part = '';
}
array_push($path, $part);
}
$prevPart = $part;
}
$merged['path'] = '/' . implode('/', $path);
}
$ret = '';
if (isset($merged['scheme'])) {
$ret .= $merged['scheme'] . ':';
}
if (isset($merged['scheme']) || isset($merged['host'])) {
$ret .= '//';
}
if (isset($prel['host'])) {
$hostSource = $prel;
} else {
$hostSource = $pbase;
}
// username, password, and port are associated with the hostname, not merged
if (isset($hostSource['host'])) {
if (isset($hostSource['user'])) {
$ret .= $hostSource['user'];
if (isset($hostSource['pass'])) {
$ret .= ':' . $hostSource['pass'];
}
$ret .= '@';
}
$ret .= $hostSource['host'];
if (isset($hostSource['port'])) {
$ret .= ':' . $hostSource['port'];
}
}
if (isset($merged['path'])) {
$ret .= $merged['path'];
}
if (isset($prel['query'])) {
$ret .= '?' . $prel['query'];
}
if (isset($prel['fragment'])) {
$ret .= '#' . $prel['fragment'];
}
return $ret;
}
|