Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
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
}