MODX Revolution PHP Cross Reference Content Management Systems

Source: /manager/min/lib/Minify/CSS/UriRewriter.php - 296 lines - 9943 bytes - Summary - Text - Print

Description: Class Minify_CSS_UriRewriter

   1  <?php
   2  /**
   3   * Class Minify_CSS_UriRewriter  
   4   * @package Minify
   5   */
   6  
   7  /**
   8   * Rewrite file-relative URIs as root-relative in CSS files
   9   *
  10   * @package Minify
  11   * @author Stephen Clay <steve@mrclay.org>
  12   */
  13  class Minify_CSS_UriRewriter {
  14      
  15      /**
  16       * rewrite() and rewriteRelative() append debugging information here
  17       * @var string
  18       */
  19      public static $debugText = '';
  20      
  21      /**
  22       * In CSS content, rewrite file relative URIs as root relative
  23       * 
  24       * @param string $css
  25       * 
  26       * @param string $currentDir The directory of the current CSS file.
  27       * 
  28       * @param string $docRoot The document root of the web site in which 
  29       * the CSS file resides (default = $_SERVER['DOCUMENT_ROOT']).
  30       * 
  31       * @param array $symlinks (default = array()) If the CSS file is stored in 
  32       * a symlink-ed directory, provide an array of link paths to
  33       * target paths, where the link paths are within the document root. Because 
  34       * paths need to be normalized for this to work, use "//" to substitute 
  35       * the doc root in the link paths (the array keys). E.g.:
  36       * <code>
  37       * array('//symlink' => '/real/target/path') // unix
  38       * array('//static' => 'D:\\staticStorage')  // Windows
  39       * </code>
  40       * 
  41       * @return string
  42       */
  43      public static function rewrite($css, $currentDir, $docRoot = null, $symlinks = array(), $virtualDirs = array())
  44      {
  45          self::$_docRoot = self::_realpath(
  46              $docRoot ? $docRoot : $_SERVER['DOCUMENT_ROOT']
  47          );
  48          self::$_currentDir = self::_realpath($currentDir);
  49          self::$_symlinks = array();
  50          self::$_virtualDirs = $virtualDirs;
  51          
  52          // normalize symlinks
  53          foreach ($symlinks as $link => $target) {
  54              $link = ($link === '//')
  55                  ? self::$_docRoot
  56                  : str_replace('//', self::$_docRoot . '/', $link);
  57              $link = strtr($link, '/', DIRECTORY_SEPARATOR);
  58              self::$_symlinks[$link] = self::_realpath($target);
  59          }
  60          
  61          self::$debugText .= "docRoot    : " . self::$_docRoot . "\n"
  62                            . "currentDir : " . self::$_currentDir . "\n";
  63          if (self::$_symlinks) {
  64              self::$debugText .= "symlinks : " . var_export(self::$_symlinks, 1) . "\n";
  65          }
  66          self::$debugText .= "\n";
  67          
  68          $css = self::_trimUrls($css);
  69          
  70          // rewrite
  71          $css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
  72              ,array(self::$className, '_processUriCB'), $css);
  73          $css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
  74              ,array(self::$className, '_processUriCB'), $css);
  75  
  76          return $css;
  77      }
  78      
  79      /**
  80       * In CSS content, prepend a path to relative URIs
  81       * 
  82       * @param string $css
  83       * 
  84       * @param string $path The path to prepend.
  85       * 
  86       * @return string
  87       */
  88      public static function prepend($css, $path)
  89      {
  90          self::$_prependPath = $path;
  91          
  92          $css = self::_trimUrls($css);
  93          
  94          // append
  95          $css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
  96              ,array(self::$className, '_processUriCB'), $css);
  97          $css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
  98              ,array(self::$className, '_processUriCB'), $css);
  99  
 100          self::$_prependPath = null;
 101          return $css;
 102      }
 103      
 104      /**
 105       * Get a root relative URI from a file relative URI
 106       *
 107       * <code>
 108       * Minify_CSS_UriRewriter::rewriteRelative(
 109       *       '../img/hello.gif'
 110       *     , '/home/user/www/css'  // path of CSS file
 111       *     , '/home/user/www'      // doc root
 112       * );
 113       * // returns '/img/hello.gif'
 114       * 
 115       * // example where static files are stored in a symlinked directory
 116       * Minify_CSS_UriRewriter::rewriteRelative(
 117       *       'hello.gif'
 118       *     , '/var/staticFiles/theme'
 119       *     , '/home/user/www'
 120       *     , array('/home/user/www/static' => '/var/staticFiles')
 121       * );
 122       * // returns '/static/theme/hello.gif'
 123       * </code>
 124       * 
 125       * @param string $uri file relative URI
 126       * 
 127       * @param string $realCurrentDir realpath of the current file's directory.
 128       * 
 129       * @param string $realDocRoot realpath of the site document root.
 130       * 
 131       * @param array $symlinks (default = array()) If the file is stored in 
 132       * a symlink-ed directory, provide an array of link paths to
 133       * real target paths, where the link paths "appear" to be within the document 
 134       * root. E.g.:
 135       * <code>
 136       * array('/home/foo/www/not/real/path' => '/real/target/path') // unix
 137       * array('C:\\htdocs\\not\\real' => 'D:\\real\\target\\path')  // Windows
 138       * </code>
 139       * 
 140       * @return string
 141       */
 142      public static function rewriteRelative($uri, $realCurrentDir, $realDocRoot, $symlinks = array(), $virtualDirs = array())
 143      {
 144          // prepend path with current dir separator (OS-independent)
 145          $path = strtr($realCurrentDir, '/', DIRECTORY_SEPARATOR)  
 146              . DIRECTORY_SEPARATOR . strtr($uri, '/', DIRECTORY_SEPARATOR);
 147          
 148          self::$debugText .= "file-relative URI  : {$uri}\n"
 149                            . "path prepended     : {$path}\n";
 150          
 151          // "unresolve" a symlink back to doc root
 152          foreach ($symlinks as $link => $target) {
 153              if (0 === strpos($path, $target)) {
 154                  // replace $target with $link
 155                  $path = $link . substr($path, strlen($target));
 156                  
 157                  self::$debugText .= "symlink unresolved : {$path}\n";
 158                  
 159                  break;
 160              }
 161          }
 162          // strip doc root
 163          if (strpos($path, $realDocRoot) === 0) {
 164              $path = substr($path, strlen($realDocRoot));
 165          } elseif (!empty($virtualDirs)) {
 166              $path = str_replace(array_values($virtualDirs), array_keys($virtualDirs), $path);
 167          }
 168          
 169          self::$debugText .= "docroot stripped   : {$path}\n";
 170          
 171          // fix to root-relative URI
 172  
 173          $uri = strtr($path, '/\\', '//');
 174  
 175          $uri = self::removeDots($uri);
 176        
 177          self::$debugText .= "traversals removed : {$uri}\n\n";
 178          
 179          return $uri;
 180      }
 181  
 182      /**
 183       * Remove instances of "./" and "../" where possible from a root-relative URI
 184       * @param string $uri
 185       * @return string
 186       */
 187      public static function removeDots($uri)
 188      {
 189          $uri = str_replace('/./', '/', $uri);
 190          // inspired by patch from Oleg Cherniy
 191          do {
 192              $uri = preg_replace('@/[^/]+/\\.\\./@', '/', $uri, 1, $changed);
 193          } while ($changed);
 194          return $uri;
 195      }
 196      
 197      /**
 198       * Defines which class to call as part of callbacks, change this
 199       * if you extend Minify_CSS_UriRewriter
 200       * @var string
 201       */
 202      protected static $className = 'Minify_CSS_UriRewriter';
 203  
 204      /**
 205       * Get realpath with any trailing slash removed. If realpath() fails,
 206       * just remove the trailing slash.
 207       * 
 208       * @param string $path
 209       * 
 210       * @return mixed path with no trailing slash
 211       */
 212      protected static function _realpath($path)
 213      {
 214          $realPath = realpath($path);
 215          if ($realPath !== false) {
 216              $path = $realPath;
 217          }
 218          return rtrim($path, '/\\');
 219      }
 220  
 221      /**
 222       * @var string directory of this stylesheet
 223       */
 224      private static $_currentDir = '';
 225  
 226      /**
 227       * @var string DOC_ROOT
 228       */
 229      private static $_docRoot = '';
 230  
 231      /**
 232       * @var array directory replacements to map symlink targets back to their
 233       * source (within the document root) E.g. '/var/www/symlink' => '/var/realpath'
 234       */
 235      private static $_symlinks = array();
 236      private static $_virtualDirs = array();
 237  
 238      /**
 239       * @var string path to prepend
 240       */
 241      private static $_prependPath = null;
 242  
 243      private static function _trimUrls($css)
 244      {
 245          return preg_replace('/
 246              url\\(      # url(
 247              \\s*
 248              ([^\\)]+?)  # 1 = URI (assuming does not contain ")")
 249              \\s*
 250              \\)         # )
 251          /x', 'url($1)', $css);
 252      }
 253  
 254      private static function _processUriCB($m)
 255      {
 256          // $m matched either '/@import\\s+([\'"])(.*?)[\'"]/' or '/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
 257          $isImport = ($m[0][0] === '@');
 258          // determine URI and the quote character (if any)
 259          if ($isImport) {
 260              $quoteChar = $m[1];
 261              $uri = $m[2];
 262          } else {
 263              // $m[1] is either quoted or not
 264              $quoteChar = ($m[1][0] === "'" || $m[1][0] === '"')
 265                  ? $m[1][0]
 266                  : '';
 267              $uri = ($quoteChar === '')
 268                  ? $m[1]
 269                  : substr($m[1], 1, strlen($m[1]) - 2);
 270          }
 271          // analyze URI
 272          if ('/' !== $uri[0]                  // root-relative
 273              && false === strpos($uri, '//')  // protocol (non-data)
 274              && 0 !== strpos($uri, 'data:')   // data protocol
 275          ) {
 276              // URI is file-relative: rewrite depending on options
 277              if (self::$_prependPath === null) {
 278                  $uri = self::rewriteRelative($uri, self::$_currentDir, self::$_docRoot, self::$_symlinks, self::$_virtualDirs);
 279              } else {
 280                  $uri = self::$_prependPath . $uri;
 281                  if ($uri[0] === '/') {
 282                      $root = '';
 283                      $rootRelative = $uri;
 284                      $uri = $root . self::removeDots($rootRelative);
 285                  } elseif (preg_match('@^((https?\:)?//([^/]+))/@', $uri, $m) && (false !== strpos($m[3], '.'))) {
 286                      $root = $m[1];
 287                      $rootRelative = substr($uri, strlen($root));
 288                      $uri = $root . self::removeDots($rootRelative);
 289                  }
 290              }
 291          }
 292          return $isImport
 293              ? "@import {$quoteChar}{$uri}{$quoteChar}"
 294              : "url({$quoteChar}{$uri}{$quoteChar})";
 295      }
 296  }

title

Description

title

Description

title

Description

title

title

Body