wenpai.org/wp-content/plugins/http-concat/ngx-http-concat.php
2024-12-09 23:55:39 +08:00

232 lines
6.3 KiB
PHP

<?php
/*
* Concatenation script inspired by Nginx's ngx_http_concat and Apache's modconcat modules.
*
* It follows the same pattern for enabling the concatenation. It uses two ?, like this:
* http://example.com/??style1.css,style2.css,foo/style3.css
*
* If a third ? is present it's treated as version string. Like this:
* http://example.com/??style1.css,style2.css,foo/style3.css?v=102234
*
* It will also replace the relative paths in CSS files with absolute paths.
*/
require_once( __DIR__ . '/cssmin/cssmin.php' );
require_once( __DIR__ . '/concat-utils.php' );
/* Config */
$concat_max_files = 150;
$concat_unique = true;
$concat_types = array(
'css' => 'text/css',
'js' => 'application/javascript'
);
/* Constants */
// By default determine the document root from this scripts path in the plugins dir (you can hardcode this define)
define( 'CONCAT_FILES_ROOT', substr( dirname( __DIR__ ), 0, strpos( dirname( __DIR__ ), '/wp-content' ) ) );
function concat_http_status_exit( $status ) {
switch ( $status ) {
case 200:
$text = 'OK';
break;
case 400:
$text = 'Bad Request';
break;
case 403:
$text = 'Forbidden';
break;
case 404:
$text = 'Not found';
break;
case 500:
$text = 'Internal Server Error';
break;
default:
$text = '';
}
$protocol = $_SERVER['SERVER_PROTOCOL'];
if ( 'HTTP/1.1' != $protocol && 'HTTP/1.0' != $protocol )
$protocol = 'HTTP/1.0';
@header( "$protocol $status $text", true, $status );
exit();
}
function concat_get_mtype( $file ) {
global $concat_types;
$lastdot_pos = strrpos( $file, '.' );
if ( false === $lastdot_pos )
return false;
$ext = substr( $file, $lastdot_pos + 1 );
return isset( $concat_types[$ext] ) ? $concat_types[$ext] : false;
}
function concat_get_path( $uri ) {
if ( ! strlen( $uri ) )
concat_http_status_exit( 400 );
if ( false !== strpos( $uri, '..' ) || false !== strpos( $uri, "\0" ) )
concat_http_status_exit( 400 );
return CONCAT_FILES_ROOT . ( '/' != $uri[0] ? '/' : '' ) . $uri;
}
/* Main() */
if ( !in_array( $_SERVER['REQUEST_METHOD'], array( 'GET', 'HEAD' ) ) )
concat_http_status_exit( 400 );
// /_static/??/foo/bar.css,/foo1/bar/baz.css?m=293847g
// or
// /_static/??-eJzTT8vP109KLNJLLi7W0QdyDEE8IK4CiVjn2hpZGluYmKcDABRMDPM=
$args = parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY );
if ( ! $args )
concat_http_status_exit( 400 );
// if args start with a question mark, remove it
if ( '?' == $args[0] )
$args = substr( $args, 1 );
// /foo/bar.css,/foo1/bar/baz.css?m=293847g
// or
// -eJzTT8vP109KLNJLLi7W0QdyDEE8IK4CiVjn2hpZGluYmKcDABRMDPM=
if ( '-' == $args[0] ) {
$args = @gzuncompress( base64_decode( substr( $args, 1 ) ) );
// Invalid data, abort!
if ( false === $args ) {
concat_http_status_exit( 400 );
}
}
// /foo/bar.css,/foo1/bar/baz.css?m=293847g
$version_string_pos = strpos( $args, '?' );
if ( false !== $version_string_pos )
$args = substr( $args, 0, $version_string_pos );
// /foo/bar.css,/foo1/bar/baz.css
$args = explode( ',', $args );
if ( ! $args )
concat_http_status_exit( 400 );
// array( '/foo/bar.css', '/foo1/bar/baz.css' )
if ( 0 == count( $args ) || count( $args ) > $concat_max_files )
concat_http_status_exit( 400 );
// If we're in a subdirectory context, use that as the root.
// We can't assume that the root serves the same content as the subdir.
$subdir_path_prefix = '';
$request_path = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );
$_static_index = strpos( $request_path, '/_static/' );
if ( $_static_index > 0 ) {
$subdir_path_prefix = substr( $request_path, 0, $_static_index );
}
unset( $request_path, $_static_index );
$last_modified = 0;
$pre_output = '';
$output = '';
$css_minify = new tubalmartin\CssMin\Minifier;
foreach ( $args as $uri ) {
$fullpath = concat_get_path( $uri );
if ( ! file_exists( $fullpath ) )
concat_http_status_exit( 404 );
$mime_type = concat_get_mtype( $fullpath );
if ( ! in_array( $mime_type, $concat_types ) )
concat_http_status_exit( 400 );
if ( $concat_unique ) {
if ( ! isset( $last_mime_type ) )
$last_mime_type = $mime_type;
if ( $last_mime_type != $mime_type )
concat_http_status_exit( 400 );
}
$stat = stat( $fullpath );
if ( false === $stat )
concat_http_status_exit( 500 );
if ( $stat['mtime'] > $last_modified )
$last_modified = $stat['mtime'];
$buf = file_get_contents( $fullpath );
if ( false === $buf )
concat_http_status_exit( 500 );
if ( 'text/css' == $mime_type ) {
$dirpath = $subdir_path_prefix . dirname( $uri );
// url(relative/path/to/file) -> url(/absolute/and/not/relative/path/to/file)
$buf = WPCOM_Concat_Utils::relative_path_replace( $buf, $dirpath );
// AlphaImageLoader(...src='relative/path/to/file'...) -> AlphaImageLoader(...src='/absolute/path/to/file'...)
$buf = preg_replace(
'/(Microsoft.AlphaImageLoader\s*\([^\)]*src=(?:\'|")?)([^\/\'"\s\)](?:(?<!http:|https:).)*)\)/isU',
'$1' . ( $dirpath == '/' ? '/' : $dirpath . '/' ) . '$2)',
$buf
);
// The @charset rules must be on top of the output
if ( 0 === strpos( $buf, '@charset' ) ) {
preg_replace_callback(
'/(?P<charset_rule>@charset\s+[\'"][^\'"]+[\'"];)/i',
function ( $match ) {
global $pre_output;
if ( 0 === strpos( $pre_output, '@charset' ) )
return '';
$pre_output = $match[0] . "\n" . $pre_output;
return '';
},
$buf
);
}
// Move the @import rules on top of the concatenated output.
// Only @charset rule are allowed before them.
if ( false !== strpos( $buf, '@import' ) ) {
$buf = preg_replace_callback(
'/(?P<pre_path>@import\b\s{0,}(?:url\s*\()?[\'"\s]*)(?P<path>[^\'"\s](?:https?:\/\/.+\/?)?.+?)(?P<post_path>[\'"\s\)]*(?:\W|screen|print|all);)/i',
function ( $match ) use ( $dirpath ) {
global $pre_output;
if ( 0 !== strpos( $match['path'], 'http' ) && '/' != $match['path'][0] )
$pre_output .= $match['pre_path'] . ( $dirpath == '/' ? '/' : $dirpath . '/' ) .
$match['path'] . $match['post_path'] . "\n";
else
$pre_output .= $match[0] . "\n";
return '';
},
$buf
);
}
$buf = $css_minify->run( $buf );
}
if ( 'application/javascript' == $mime_type )
$output .= "$buf;\n";
else
$output .= "$buf";
}
header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $last_modified ) . ' GMT' );
header( 'Content-Length: ' . ( strlen( $pre_output ) + strlen( $output ) ) );
header( "Content-Type: $mime_type" );
echo $pre_output . $output;