See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 39 and 401]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body