See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 and 403]
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 /* 75 * Example: 76 * /home/forkcms/frontend/cache/compiled_templates/../../core/layout/css/../images/img.gif 77 * to 78 * /home/forkcms/frontend/core/layout/images/img.gif 79 */ 80 do { 81 $path = preg_replace('/[^\/]+(?<!\.\.)\/\.\.\//', '', $path, -1, $count); 82 } while ($count); 83 84 return $path; 85 } 86 87 /** 88 * Figure out the shared path of 2 locations. 89 * 90 * Example: 91 * /home/forkcms/frontend/core/layout/images/img.gif 92 * and 93 * /home/forkcms/frontend/cache/minified_css 94 * share 95 * /home/forkcms/frontend 96 * 97 * @param string $path1 98 * @param string $path2 99 * 100 * @return string 101 */ 102 protected function shared($path1, $path2) 103 { 104 // $path could theoretically be empty (e.g. no path is given), in which 105 // case it shouldn't expand to array(''), which would compare to one's 106 // root / 107 $path1 = $path1 ? explode('/', $path1) : array(); 108 $path2 = $path2 ? explode('/', $path2) : array(); 109 110 $shared = array(); 111 112 // compare paths & strip identical ancestors 113 foreach ($path1 as $i => $chunk) { 114 if (isset($path2[$i]) && $path1[$i] == $path2[$i]) { 115 $shared[] = $chunk; 116 } else { 117 break; 118 } 119 } 120 121 return implode('/', $shared); 122 } 123 124 /** 125 * Convert paths relative from 1 file to another. 126 * 127 * E.g. 128 * ../images/img.gif relative to /home/forkcms/frontend/core/layout/css 129 * should become: 130 * ../../core/layout/images/img.gif relative to 131 * /home/forkcms/frontend/cache/minified_css 132 * 133 * @param string $path The relative path that needs to be converted 134 * 135 * @return string The new relative path 136 */ 137 public function convert($path) 138 { 139 // quit early if conversion makes no sense 140 if ($this->from === $this->to) { 141 return $path; 142 } 143 144 $path = $this->normalize($path); 145 // if we're not dealing with a relative path, just return absolute 146 if (strpos($path, '/') === 0) { 147 return $path; 148 } 149 150 // normalize paths 151 $path = $this->normalize($this->from.'/'.$path); 152 153 // strip shared ancestor paths 154 $shared = $this->shared($path, $this->to); 155 $path = mb_substr($path, mb_strlen($shared)); 156 $to = mb_substr($this->to, mb_strlen($shared)); 157 158 // add .. for every directory that needs to be traversed to new path 159 $to = str_repeat('../', count(array_filter(explode('/', $to)))); 160 161 return $to.ltrim($path, '/'); 162 } 163 164 /** 165 * Attempt to get the directory name from a path. 166 * 167 * @param string $path 168 * 169 * @return string 170 */ 171 protected function dirname($path) 172 { 173 if (@is_file($path)) { 174 return dirname($path); 175 } 176 177 if (@is_dir($path)) { 178 return rtrim($path, '/'); 179 } 180 181 // no known file/dir, start making assumptions 182 183 // ends in / = dir 184 if (mb_substr($path, -1) === '/') { 185 return rtrim($path, '/'); 186 } 187 188 // has a dot in the name, likely a file 189 if (preg_match('/.*\..*$/', basename($path)) !== 0) { 190 return dirname($path); 191 } 192 193 // you're on your own here! 194 return $path; 195 } 196 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body