| Current Path : /home/ereika83/www/wp-content/plugins/wp-hummingbird/core/modules/minify/ |
| Current File : /home/ereika83/www/wp-content/plugins/wp-hummingbird/core/modules/minify/class-uri-rewriter.php |
<?php
/**
* Rewrite file-relative URIs as root-relative in CSS files
*
* @package Hummingbird\Core\Modules\Minify
* @author Stephen Clay <steve@mrclay.org>
*/
namespace Hummingbird\Core\Modules\Minify;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class URI_Rewriter
*/
class URI_Rewriter {
/**
* Defines which class to call as part of callbacks, change this
* if you extend URI_Rewriter
*
* @var string
*/
protected static $class_name = 'Hummingbird\\Core\\Modules\\Minify\\URI_Rewriter';
/**
* Directory of this stylesheet
*
* @var string
*/
private static $current_dir = '';
/**
* DOC_ROOT
*
* @var string
*/
private static $doc_root = '';
/**
* Directory replacements to map symlink targets back to their
* source (within the document root) E.g. '/var/www/symlink' => '/var/realpath'
*
* @var array
*/
private static $symlinks = array();
/**
* Path to prepend
*
* @var string
*/
private static $prepend_path = null;
/**
* Methods rewrite() and rewrite_relative() append debugging information here
*
* @var string $debug_text
*/
public static $debug_text = '';
/**
* Get realpath with any trailing slash removed. If realpath() fails,
* just remove the trailing slash.
*
* @param string $path Path to process.
*
* @return string path with no trailing slash
*/
protected static function realpath( $path ) {
$real_path = realpath( $path );
if ( false !== $real_path ) {
$path = $real_path;
}
return rtrim( $path, '/\\' );
}
/**
* Trim URLs
*
* @param string $css CSS URL.
*
* @return string
*/
private static function trim_urls( $css ) {
return preg_replace(
'/
url\\( # url(
\\s*
([^\\)]+?) # 1 = URI (assuming does not contain ")")
\\s*
\\) # )
/x',
'url($1)',
$css
);
}
/**
* Process callback function.
*
* @used-by URI_Rewriter::rewrite()
* @used-by URI_Rewriter::prepend()
*
* @param array $m Array of found links.
*
* @return string
*/
private static function process_uri_cb( $m ) {
// $m matched either '/@import\\s+([\'"])(.*?)[\'"]/' or '/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
$is_import = ( '@' === $m[0][0] );
// determine URI and the quote character (if any).
if ( $is_import ) {
$quote_char = $m[1];
$uri = $m[2];
} else {
// $m[1] is either quoted or not
$quote_char = ( "'" === $m[1][0] || '"' === $m[1][0] )
? $m[1][0]
: '';
$uri = ( '' === $quote_char )
? $m[1]
: substr( $m[1], 1, strlen( $m[1] ) - 2 );
}
// analyze URI (non root-relative).
if ( isset( $uri[0] ) && '/' !== $uri[0] // root-relative.
&& false === strpos( $uri, '//' ) // protocol (non-data).
&& 0 !== strpos( $uri, 'data:' ) // data protocol.
) {
// URI is file-relative: rewrite depending on options.
if ( null === self::$prepend_path ) {
$uri = self::rewrite_relative( $uri, self::$current_dir, self::$doc_root, self::$symlinks );
} else {
$uri = self::$prepend_path . $uri;
if ( '/' === $uri[0] ) {
$root = '';
$root_relative = $uri;
$uri = $root . self::remove_dots( $root_relative );
} elseif ( preg_match( '@^((https?\:)?//([^/]+))/@', $uri, $m ) && ( false !== strpos( $m[3], '.' ) ) ) {
$root = $m[1];
$root_relative = substr( $uri, strlen( $root ) );
$uri = $root . self::remove_dots( $root_relative );
}
}
} elseif ( isset( $uri[0] ) && '/' !== $uri[0] // root-relative.
&& false === strpos( $uri, '//' ) // protocol (non-data).
&& 0 !== strpos( $uri, 'data:' ) // data protocol.
&& null !== self::$prepend_path
) {
preg_match( '@^((https?\:)?//([^/]+))/@', self::$prepend_path, $m );
if ( preg_match( '@^((https?\:)?//([^/]+))/@', self::$prepend_path, $m ) && ( false !== strpos( $m[3], '.' ) ) ) {
$uri = $m[1] . self::remove_dots( $uri );
}
} else {
return $m[0];
}
return $is_import
? "@import {$quote_char}{$uri}{$quote_char}"
: "url({$quote_char}{$uri}{$quote_char})";
}
/**
* In CSS content, rewrite file relative URIs as root relative
*
* @param string $css The directory of the current CSS file.
*
* @param string $current_dir The directory of the current CSS file.
*
* @param string $doc_root The document root of the web site in which
* the CSS file resides (default = $_SERVER['DOCUMENT_ROOT']).
*
* @param array $symlinks (default = array()) If the CSS file is stored in
* a symlink-ed directory, provide an array of link paths to
* target paths, where the link paths are within the document root. Because
* paths need to be normalized for this to work, use "//" to substitute
* the doc root in the link paths (the array keys). E.g.:
* <code>
* array('//symlink' => '/real/target/path') // unix
* array('//static' => 'D:\\staticStorage') // Windows
* </code>.
*
* @return string
*/
public static function rewrite( $css, $current_dir, $doc_root = null, $symlinks = array() ) {
self::$doc_root = self::realpath(
$doc_root ? $doc_root : $_SERVER['DOCUMENT_ROOT'] // Input var ok.
);
self::$current_dir = self::realpath( $current_dir );
self::$symlinks = array();
// Normalize symlinks.
foreach ( $symlinks as $link => $target ) {
$link = ( '//' === $link )
? self::$doc_root
: str_replace( '//', self::$doc_root . '/', $link );
$link = strtr( $link, '/', DIRECTORY_SEPARATOR );
self::$symlinks[ $link ] = self::realpath( $target );
}
self::$debug_text .= 'docRoot : ' . self::$doc_root . "\n"
. 'currentDir : ' . self::$current_dir . "\n";
if ( self::$symlinks ) {
self::$debug_text .= 'symlinks : ' . var_export( self::$symlinks, 1 ) . "\n";
}
self::$debug_text .= "\n";
$css = self::trim_urls( $css );
// Rewrite.
$css = preg_replace_callback(
'/@import\\s+([\'"])(.*?)[\'"]/',
array( self::$class_name, 'process_uri_cb' ),
$css
);
$css = preg_replace_callback(
'/url\s*\\(\\s*([^\\)\\s]+)\\s*\\)/',
array( self::$class_name, 'process_uri_cb' ),
$css
);
return $css;
}
/**
* In CSS content, prepend a path to relative URIs
*
* @param string $css Content of CSS file.
*
* @param string $path The path to prepend.
*
* @return string
*/
public static function prepend( $css, $path ) {
self::$prepend_path = $path;
$css = self::trim_urls( $css );
// Append.
$css = preg_replace_callback(
'/@import\\s+([\'"])(.*?)[\'"]/',
array( self::$class_name, 'process_uri_cb' ),
$css
);
$css = preg_replace_callback(
'/url\\(\\s*([^\\)\\s]+)\\s*\\)/',
array( self::$class_name, 'process_uri_cb' ),
$css
);
self::$prepend_path = null;
return $css;
}
/**
* Get a root relative URI from a file relative URI
*
* <code>
* URI_Rewriter::rewrite_relative(
* '../img/hello.gif'
* , '/home/user/www/css' // path of CSS file
* , '/home/user/www' // doc root
* );
* // returns '/img/hello.gif'
*
* // example where static files are stored in a symlinked directory
* URI_Rewriter::rewrite_relative(
* 'hello.gif'
* , '/var/staticFiles/theme'
* , '/home/user/www'
* , array('/home/user/www/static' => '/var/staticFiles')
* );
* // returns '/static/theme/hello.gif'
* </code>
*
* @param string $uri file Relative URI.
*
* @param string $real_current_dir The realpath of the current file's directory.
*
* @param string $real_doc_root The realpath of the site document root.
*
* @param array $symlinks (default = array()) If the file is stored in
* a symlink-ed directory, provide an array of link paths to
* real target paths, where the link paths "appear" to be within the document
* root. E.g.:
* <code>
* array('/home/foo/www/not/real/path' => '/real/target/path') // unix
* array('C:\\htdocs\\not\\real' => 'D:\\real\\target\\path') // Windows
* </code>.
*
* @return string
*/
public static function rewrite_relative( $uri, $real_current_dir, $real_doc_root, $symlinks = array() ) {
// Prepend path with current dir separator (OS-independent).
$path = strtr( $real_current_dir, '/', DIRECTORY_SEPARATOR )
. DIRECTORY_SEPARATOR . strtr( $uri, '/', DIRECTORY_SEPARATOR );
self::$debug_text .= "file-relative URI : {$uri}\n"
. "path prepended : {$path}\n";
// "unresolve" a symlink back to doc root.
foreach ( $symlinks as $link => $target ) {
if ( 0 === strpos( $path, $target ) ) {
// Replace $target with $link.
$path = $link . substr( $path, strlen( $target ) );
self::$debug_text .= "symlink unresolved : {$path}\n";
break;
}
}
// Strip doc root.
$path = substr( $path, strlen( $real_doc_root ) );
self::$debug_text .= "docroot stripped : {$path}\n";
// Fix to root-relative URI.
$uri = strtr( $path, '/\\', '//' );
$uri = self::remove_dots( $uri );
self::$debug_text .= "traversals removed : {$uri}\n\n";
return $uri;
}
/**
* Remove instances of "./" and "../" where possible from a root-relative URI
*
* @param string $uri URI to parse.
*
* @return string
*/
public static function remove_dots( $uri ) {
$uri = str_replace( '/./', '/', $uri );
// Inspired by patch from Oleg Cherniy.
do {
$uri = preg_replace( '@/[^/]+/\\.\\./@', '/', $uri, 1, $changed );
} while ( $changed );
return $uri;
}
}