| 1 |
efrain |
1 |
<?php
|
|
|
2 |
|
|
|
3 |
namespace MatthiasMullie\PathConverter;
|
|
|
4 |
|
|
|
5 |
/**
|
|
|
6 |
* Convert paths relative from 1 file to another.
|
|
|
7 |
*
|
|
|
8 |
* E.g.
|
|
|
9 |
* ../../images/icon.jpg relative to /css/imports/icons.css
|
|
|
10 |
* becomes
|
|
|
11 |
* ../images/icon.jpg relative to /css/minified.css
|
|
|
12 |
*
|
|
|
13 |
* Please report bugs on https://github.com/matthiasmullie/path-converter/issues
|
|
|
14 |
*
|
|
|
15 |
* @author Matthias Mullie <pathconverter@mullie.eu>
|
|
|
16 |
* @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
|
|
|
17 |
* @license MIT License
|
|
|
18 |
*/
|
|
|
19 |
class Converter implements ConverterInterface
|
|
|
20 |
{
|
|
|
21 |
/**
|
|
|
22 |
* @var string
|
|
|
23 |
*/
|
|
|
24 |
protected $from;
|
|
|
25 |
|
|
|
26 |
/**
|
|
|
27 |
* @var string
|
|
|
28 |
*/
|
|
|
29 |
protected $to;
|
|
|
30 |
|
|
|
31 |
/**
|
|
|
32 |
* @param string $from The original base path (directory, not file!)
|
|
|
33 |
* @param string $to The new base path (directory, not file!)
|
|
|
34 |
* @param string $root Root directory (defaults to `getcwd`)
|
|
|
35 |
*/
|
|
|
36 |
public function __construct($from, $to, $root = '')
|
|
|
37 |
{
|
|
|
38 |
$shared = $this->shared($from, $to);
|
|
|
39 |
if ($shared === '') {
|
|
|
40 |
// when both paths have nothing in common, one of them is probably
|
|
|
41 |
// absolute while the other is relative
|
|
|
42 |
$root = $root ?: getcwd();
|
|
|
43 |
$from = strpos($from, $root) === 0 ? $from : preg_replace('/\/+/', '/', $root.'/'.$from);
|
|
|
44 |
$to = strpos($to, $root) === 0 ? $to : preg_replace('/\/+/', '/', $root.'/'.$to);
|
|
|
45 |
|
|
|
46 |
// or traveling the tree via `..`
|
|
|
47 |
// attempt to resolve path, or assume it's fine if it doesn't exist
|
|
|
48 |
$from = @realpath($from) ?: $from;
|
|
|
49 |
$to = @realpath($to) ?: $to;
|
|
|
50 |
}
|
|
|
51 |
|
|
|
52 |
$from = $this->dirname($from);
|
|
|
53 |
$to = $this->dirname($to);
|
|
|
54 |
|
|
|
55 |
$from = $this->normalize($from);
|
|
|
56 |
$to = $this->normalize($to);
|
|
|
57 |
|
|
|
58 |
$this->from = $from;
|
|
|
59 |
$this->to = $to;
|
|
|
60 |
}
|
|
|
61 |
|
|
|
62 |
/**
|
|
|
63 |
* Normalize path.
|
|
|
64 |
*
|
|
|
65 |
* @param string $path
|
|
|
66 |
*
|
|
|
67 |
* @return string
|
|
|
68 |
*/
|
|
|
69 |
protected function normalize($path)
|
|
|
70 |
{
|
|
|
71 |
// deal with different operating systems' directory structure
|
|
|
72 |
$path = rtrim(str_replace(DIRECTORY_SEPARATOR, '/', $path), '/');
|
|
|
73 |
|
|
|
74 |
// remove leading current directory.
|
|
|
75 |
if (substr($path, 0, 2) === './') {
|
|
|
76 |
$path = substr($path, 2);
|
|
|
77 |
}
|
|
|
78 |
|
|
|
79 |
// remove references to current directory in the path.
|
|
|
80 |
$path = str_replace('/./', '/', $path);
|
|
|
81 |
|
|
|
82 |
/*
|
|
|
83 |
* Example:
|
|
|
84 |
* /home/forkcms/frontend/cache/compiled_templates/../../core/layout/css/../images/img.gif
|
|
|
85 |
* to
|
|
|
86 |
* /home/forkcms/frontend/core/layout/images/img.gif
|
|
|
87 |
*/
|
|
|
88 |
do {
|
|
|
89 |
$path = preg_replace('/[^\/]+(?<!\.\.)\/\.\.\//', '', $path, -1, $count);
|
|
|
90 |
} while ($count);
|
|
|
91 |
|
|
|
92 |
return $path;
|
|
|
93 |
}
|
|
|
94 |
|
|
|
95 |
/**
|
|
|
96 |
* Figure out the shared path of 2 locations.
|
|
|
97 |
*
|
|
|
98 |
* Example:
|
|
|
99 |
* /home/forkcms/frontend/core/layout/images/img.gif
|
|
|
100 |
* and
|
|
|
101 |
* /home/forkcms/frontend/cache/minified_css
|
|
|
102 |
* share
|
|
|
103 |
* /home/forkcms/frontend
|
|
|
104 |
*
|
|
|
105 |
* @param string $path1
|
|
|
106 |
* @param string $path2
|
|
|
107 |
*
|
|
|
108 |
* @return string
|
|
|
109 |
*/
|
|
|
110 |
protected function shared($path1, $path2)
|
|
|
111 |
{
|
|
|
112 |
// $path could theoretically be empty (e.g. no path is given), in which
|
|
|
113 |
// case it shouldn't expand to array(''), which would compare to one's
|
|
|
114 |
// root /
|
|
|
115 |
$path1 = $path1 ? explode('/', $path1) : array();
|
|
|
116 |
$path2 = $path2 ? explode('/', $path2) : array();
|
|
|
117 |
|
|
|
118 |
$shared = array();
|
|
|
119 |
|
|
|
120 |
// compare paths & strip identical ancestors
|
|
|
121 |
foreach ($path1 as $i => $chunk) {
|
|
|
122 |
if (isset($path2[$i]) && $path1[$i] == $path2[$i]) {
|
|
|
123 |
$shared[] = $chunk;
|
|
|
124 |
} else {
|
|
|
125 |
break;
|
|
|
126 |
}
|
|
|
127 |
}
|
|
|
128 |
|
|
|
129 |
return implode('/', $shared);
|
|
|
130 |
}
|
|
|
131 |
|
|
|
132 |
/**
|
|
|
133 |
* Convert paths relative from 1 file to another.
|
|
|
134 |
*
|
|
|
135 |
* E.g.
|
|
|
136 |
* ../images/img.gif relative to /home/forkcms/frontend/core/layout/css
|
|
|
137 |
* should become:
|
|
|
138 |
* ../../core/layout/images/img.gif relative to
|
|
|
139 |
* /home/forkcms/frontend/cache/minified_css
|
|
|
140 |
*
|
|
|
141 |
* @param string $path The relative path that needs to be converted
|
|
|
142 |
*
|
|
|
143 |
* @return string The new relative path
|
|
|
144 |
*/
|
|
|
145 |
public function convert($path)
|
|
|
146 |
{
|
|
|
147 |
// quit early if conversion makes no sense
|
|
|
148 |
if ($this->from === $this->to) {
|
|
|
149 |
return $path;
|
|
|
150 |
}
|
|
|
151 |
|
|
|
152 |
$path = $this->normalize($path);
|
|
|
153 |
// if we're not dealing with a relative path, just return absolute
|
|
|
154 |
if (strpos($path, '/') === 0) {
|
|
|
155 |
return $path;
|
|
|
156 |
}
|
|
|
157 |
|
|
|
158 |
// normalize paths
|
|
|
159 |
$path = $this->normalize($this->from.'/'.$path);
|
|
|
160 |
|
|
|
161 |
// strip shared ancestor paths
|
|
|
162 |
$shared = $this->shared($path, $this->to);
|
|
|
163 |
$path = mb_substr($path, mb_strlen($shared));
|
|
|
164 |
$to = mb_substr($this->to, mb_strlen($shared));
|
|
|
165 |
|
|
|
166 |
// add .. for every directory that needs to be traversed to new path
|
|
|
167 |
$to = str_repeat('../', count(array_filter(explode('/', $to))));
|
|
|
168 |
|
|
|
169 |
return $to.ltrim($path, '/');
|
|
|
170 |
}
|
|
|
171 |
|
|
|
172 |
/**
|
|
|
173 |
* Attempt to get the directory name from a path.
|
|
|
174 |
*
|
|
|
175 |
* @param string $path
|
|
|
176 |
*
|
|
|
177 |
* @return string
|
|
|
178 |
*/
|
|
|
179 |
protected function dirname($path)
|
|
|
180 |
{
|
|
|
181 |
if (@is_file($path)) {
|
|
|
182 |
return dirname($path);
|
|
|
183 |
}
|
|
|
184 |
|
|
|
185 |
if (@is_dir($path)) {
|
|
|
186 |
return rtrim($path, '/');
|
|
|
187 |
}
|
|
|
188 |
|
|
|
189 |
// no known file/dir, start making assumptions
|
|
|
190 |
|
|
|
191 |
// ends in / = dir
|
|
|
192 |
if (mb_substr($path, -1) === '/') {
|
|
|
193 |
return rtrim($path, '/');
|
|
|
194 |
}
|
|
|
195 |
|
|
|
196 |
// has a dot in the name, likely a file
|
|
|
197 |
if (preg_match('/.*\..*$/', basename($path)) !== 0) {
|
|
|
198 |
return dirname($path);
|
|
|
199 |
}
|
|
|
200 |
|
|
|
201 |
// you're on your own here!
|
|
|
202 |
return $path;
|
|
|
203 |
}
|
|
|
204 |
}
|