[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/minify/lib/Minify/CSS/ -> UriRewriter.php (source)

   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       *
  18       * @var string
  19       */
  20      public static $debugText = '';
  21      
  22      /**
  23       * In CSS content, rewrite file relative URIs as root relative
  24       * 
  25       * @param string $css
  26       * 
  27       * @param string $currentDir The directory of the current CSS file.
  28       * 
  29       * @param string $docRoot The document root of the web site in which 
  30       * the CSS file resides (default = $_SERVER['DOCUMENT_ROOT']).
  31       * 
  32       * @param array $symlinks (default = array()) If the CSS file is stored in 
  33       * a symlink-ed directory, provide an array of link paths to
  34       * target paths, where the link paths are within the document root. Because 
  35       * paths need to be normalized for this to work, use "//" to substitute 
  36       * the doc root in the link paths (the array keys). E.g.:
  37       * <code>
  38       * array('//symlink' => '/real/target/path') // unix
  39       * array('//static' => 'D:\\staticStorage')  // Windows
  40       * </code>
  41       * 
  42       * @return string
  43       */
  44      public static function rewrite($css, $currentDir, $docRoot = null, $symlinks = array()) 
  45      {
  46          self::$_docRoot = self::_realpath(
  47              $docRoot ? $docRoot : $_SERVER['DOCUMENT_ROOT']
  48          );
  49          self::$_currentDir = self::_realpath($currentDir);
  50          self::$_symlinks = array();
  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())
 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          $path = substr($path, strlen($realDocRoot));
 164          
 165          self::$debugText .= "docroot stripped   : {$path}\n";
 166          
 167          // fix to root-relative URI
 168          $uri = strtr($path, '/\\', '//');
 169          $uri = self::removeDots($uri);
 170        
 171          self::$debugText .= "traversals removed : {$uri}\n\n";
 172          
 173          return $uri;
 174      }
 175  
 176      /**
 177       * Remove instances of "./" and "../" where possible from a root-relative URI
 178       *
 179       * @param string $uri
 180       *
 181       * @return string
 182       */
 183      public static function removeDots($uri)
 184      {
 185          $uri = str_replace('/./', '/', $uri);
 186          // inspired by patch from Oleg Cherniy
 187          do {
 188              $uri = preg_replace('@/[^/]+/\\.\\./@', '/', $uri, 1, $changed);
 189          } while ($changed);
 190          return $uri;
 191      }
 192      
 193      /**
 194       * Defines which class to call as part of callbacks, change this
 195       * if you extend Minify_CSS_UriRewriter
 196       *
 197       * @var string
 198       */
 199      protected static $className = 'Minify_CSS_UriRewriter';
 200  
 201      /**
 202       * Get realpath with any trailing slash removed. If realpath() fails,
 203       * just remove the trailing slash.
 204       * 
 205       * @param string $path
 206       * 
 207       * @return mixed path with no trailing slash
 208       */
 209      protected static function _realpath($path)
 210      {
 211          $realPath = realpath($path);
 212          if ($realPath !== false) {
 213              $path = $realPath;
 214          }
 215          return rtrim($path, '/\\');
 216      }
 217  
 218      /**
 219       * Directory of this stylesheet
 220       *
 221       * @var string
 222       */
 223      private static $_currentDir = '';
 224  
 225      /**
 226       * DOC_ROOT
 227       *
 228       * @var string
 229       */
 230      private static $_docRoot = '';
 231  
 232      /**
 233       * directory replacements to map symlink targets back to their
 234       * source (within the document root) E.g. '/var/www/symlink' => '/var/realpath'
 235       *
 236       * @var array
 237       */
 238      private static $_symlinks = array();
 239  
 240      /**
 241       * Path to prepend
 242       *
 243       * @var string
 244       */
 245      private static $_prependPath = null;
 246  
 247      /**
 248       * @param string $css
 249       *
 250       * @return string
 251       */
 252      private static function _trimUrls($css)
 253      {
 254          return preg_replace('/
 255              url\\(      # url(
 256              \\s*
 257              ([^\\)]+?)  # 1 = URI (assuming does not contain ")")
 258              \\s*
 259              \\)         # )
 260          /x', 'url($1)', $css);
 261      }
 262  
 263      /**
 264       * @param array $m
 265       *
 266       * @return string
 267       */
 268      private static function _processUriCB($m)
 269      {
 270          // $m matched either '/@import\\s+([\'"])(.*?)[\'"]/' or '/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
 271          $isImport = ($m[0][0] === '@');
 272          // determine URI and the quote character (if any)
 273          if ($isImport) {
 274              $quoteChar = $m[1];
 275              $uri = $m[2];
 276          } else {
 277              // $m[1] is either quoted or not
 278              $quoteChar = ($m[1][0] === "'" || $m[1][0] === '"')
 279                  ? $m[1][0]
 280                  : '';
 281              $uri = ($quoteChar === '')
 282                  ? $m[1]
 283                  : substr($m[1], 1, strlen($m[1]) - 2);
 284          }
 285          // if not root/scheme relative and not starts with scheme
 286          if (!preg_match('~^(/|[a-z]+\:)~', $uri)) {
 287              // URI is file-relative: rewrite depending on options
 288              if (self::$_prependPath === null) {
 289                  $uri = self::rewriteRelative($uri, self::$_currentDir, self::$_docRoot, self::$_symlinks);
 290              } else {
 291                  $uri = self::$_prependPath . $uri;
 292                  if ($uri[0] === '/') {
 293                      $root = '';
 294                      $rootRelative = $uri;
 295                      $uri = $root . self::removeDots($rootRelative);
 296                  } elseif (preg_match('@^((https?\:)?//([^/]+))/@', $uri, $m) && (false !== strpos($m[3], '.'))) {
 297                      $root = $m[1];
 298                      $rootRelative = substr($uri, strlen($root));
 299                      $uri = $root . self::removeDots($rootRelative);
 300                  }
 301              }
 302          }
 303          return $isImport
 304              ? "@import {$quoteChar}{$uri}{$quoteChar}"
 305              : "url({$quoteChar}{$uri}{$quoteChar})";
 306      }
 307  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1