1
0
mirror of https://github.com/e107inc/e107.git synced 2025-01-16 20:28:28 +01:00

Replaced JavaScript minifier with one that works with PHP 8.0

(Looks like tedivm/jshrink is abandoned)
This commit is contained in:
Nick Liu 2020-11-27 17:11:10 +01:00
parent f256b924ce
commit 52ecd679e6
No known key found for this signature in database
GPG Key ID: 1167C5F9C9897637
46 changed files with 3507 additions and 1072 deletions

View File

@ -17,7 +17,8 @@
"platform": {
"php": "5.6"
},
"vendor-dir": "e107_handlers/vendor"
"vendor-dir": "e107_handlers/vendor",
"platform-check": false
},
"require": {
"php": ">=5.6",
@ -25,8 +26,8 @@
"ext-pdo": "*",
"hybridauth/hybridauth": "^3.1.1",
"phpmailer/phpmailer": "^6.1",
"tedivm/jshrink": "^1.3",
"ifsnop/mysqldump-php": "^2.8"
"ifsnop/mysqldump-php": "^2.8",
"matthiasmullie/minify": "^1.3"
},
"require-dev": {}
}

167
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "1c075824f15ab5d3b2b7f32dcf933e8a",
"content-hash": "e9ca525b9c7c5043623e8c4681120052",
"packages": [
{
"name": "hybridauth/hybridauth",
@ -113,6 +113,123 @@
],
"time": "2020-04-03T14:40:40+00:00"
},
{
"name": "matthiasmullie/minify",
"version": "1.3.63",
"source": {
"type": "git",
"url": "https://github.com/matthiasmullie/minify.git",
"reference": "9ba1b459828adc13430f4dd6c49dae4950dc4117"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/matthiasmullie/minify/zipball/9ba1b459828adc13430f4dd6c49dae4950dc4117",
"reference": "9ba1b459828adc13430f4dd6c49dae4950dc4117",
"shasum": ""
},
"require": {
"ext-pcre": "*",
"matthiasmullie/path-converter": "~1.1",
"php": ">=5.3.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "~2.0",
"matthiasmullie/scrapbook": "~1.0",
"phpunit/phpunit": "~4.8"
},
"suggest": {
"psr/cache-implementation": "Cache implementation to use with Minify::cache"
},
"bin": [
"bin/minifycss",
"bin/minifyjs"
],
"type": "library",
"autoload": {
"psr-4": {
"MatthiasMullie\\Minify\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Matthias Mullie",
"email": "minify@mullie.eu",
"homepage": "http://www.mullie.eu",
"role": "Developer"
}
],
"description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.",
"homepage": "http://www.minifier.org",
"keywords": [
"JS",
"css",
"javascript",
"minifier",
"minify"
],
"support": {
"issues": "https://github.com/matthiasmullie/minify/issues",
"source": "https://github.com/matthiasmullie/minify/tree/1.3.63"
},
"time": "2020-01-21T20:21:08+00:00"
},
{
"name": "matthiasmullie/path-converter",
"version": "1.1.3",
"source": {
"type": "git",
"url": "https://github.com/matthiasmullie/path-converter.git",
"reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/e7d13b2c7e2f2268e1424aaed02085518afa02d9",
"reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9",
"shasum": ""
},
"require": {
"ext-pcre": "*",
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "~4.8"
},
"type": "library",
"autoload": {
"psr-4": {
"MatthiasMullie\\PathConverter\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Matthias Mullie",
"email": "pathconverter@mullie.eu",
"homepage": "http://www.mullie.eu",
"role": "Developer"
}
],
"description": "Relative path converter",
"homepage": "http://github.com/matthiasmullie/path-converter",
"keywords": [
"converter",
"path",
"paths",
"relative"
],
"support": {
"issues": "https://github.com/matthiasmullie/path-converter/issues",
"source": "https://github.com/matthiasmullie/path-converter/tree/1.1.3"
},
"time": "2019-02-05T23:41:09+00:00"
},
{
"name": "phpmailer/phpmailer",
"version": "v6.1.6",
@ -180,52 +297,6 @@
}
],
"time": "2020-05-27T12:24:03+00:00"
},
{
"name": "tedivm/jshrink",
"version": "v1.3.3",
"source": {
"type": "git",
"url": "https://github.com/tedious/JShrink.git",
"reference": "566e0c731ba4e372be2de429ef7d54f4faf4477a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/tedious/JShrink/zipball/566e0c731ba4e372be2de429ef7d54f4faf4477a",
"reference": "566e0c731ba4e372be2de429ef7d54f4faf4477a",
"shasum": ""
},
"require": {
"php": "^5.6|^7.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.8",
"php-coveralls/php-coveralls": "^1.1.0",
"phpunit/phpunit": "^6"
},
"type": "library",
"autoload": {
"psr-0": {
"JShrink": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Robert Hafner",
"email": "tedivm@tedivm.com"
}
],
"description": "Javascript Minifier built in PHP",
"homepage": "http://github.com/tedious/JShrink",
"keywords": [
"javascript",
"minifier"
],
"time": "2019-06-28T18:11:46+00:00"
}
],
"packages-dev": [],
@ -243,5 +314,5 @@
"platform-overrides": {
"php": "5.6"
},
"plugin-api-version": "1.1.0"
"plugin-api-version": "2.0.0"
}

View File

@ -3774,10 +3774,10 @@ class e107
return null;
}
// require_once(e_HANDLER."jsshrink/Minifier.php");
try
{
$minified = JShrink\Minifier::minify($js,$options);
$minifier = new MatthiasMullie\Minify\JS($js);
$minified = $minifier->minify();
}
catch(Exception $e)
{

View File

@ -4,4 +4,4 @@
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit1da798ad8058bf7b615c0a3d8f41e314::getLoader();
return ComposerAutoloaderInit4ce406ae486ac58c9aa71537459207ae::getLoader();

1
e107_handlers/vendor/bin/minifycss vendored Symbolic link
View File

@ -0,0 +1 @@
../matthiasmullie/minify/bin/minifycss

1
e107_handlers/vendor/bin/minifyjs vendored Symbolic link
View File

@ -0,0 +1 @@
../matthiasmullie/minify/bin/minifyjs

View File

@ -37,8 +37,8 @@ namespace Composer\Autoload;
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
@ -60,7 +60,7 @@ class ClassLoader
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();

View File

@ -0,0 +1,254 @@
<?php
namespace Composer;
use Composer\Semver\VersionParser;
class InstalledVersions
{
private static $installed = array (
'root' =>
array (
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'aliases' =>
array (
),
'reference' => 'c258b6a2806c591575415a7b93f5543473d04e15',
'name' => 'e107inc/e107',
),
'versions' =>
array (
'e107inc/e107' =>
array (
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'aliases' =>
array (
),
'reference' => 'c258b6a2806c591575415a7b93f5543473d04e15',
),
'hybridauth/hybridauth' =>
array (
'pretty_version' => '3.3.0',
'version' => '3.3.0.0',
'aliases' =>
array (
),
'reference' => '51cb2ad2f04d175d298b51e919868dec1d4d8b04',
),
'ifsnop/mysqldump-php' =>
array (
'pretty_version' => 'v2.9',
'version' => '2.9.0.0',
'aliases' =>
array (
),
'reference' => 'fc9c119fe5d70af9a685cad6a8ac612fd7589e25',
),
'matthiasmullie/minify' =>
array (
'pretty_version' => '1.3.63',
'version' => '1.3.63.0',
'aliases' =>
array (
),
'reference' => '9ba1b459828adc13430f4dd6c49dae4950dc4117',
),
'matthiasmullie/path-converter' =>
array (
'pretty_version' => '1.1.3',
'version' => '1.1.3.0',
'aliases' =>
array (
),
'reference' => 'e7d13b2c7e2f2268e1424aaed02085518afa02d9',
),
'phpmailer/phpmailer' =>
array (
'pretty_version' => 'v6.1.6',
'version' => '6.1.6.0',
'aliases' =>
array (
),
'reference' => 'c2796cb1cb99d7717290b48c4e6f32cb6c60b7b3',
),
),
);
public static function getInstalledPackages()
{
return array_keys(self::$installed['versions']);
}
public static function isInstalled($packageName)
{
return isset(self::$installed['versions'][$packageName]);
}
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints($constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
}
public static function getVersionRanges($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
$ranges = array();
if (isset(self::$installed['versions'][$packageName]['pretty_version'])) {
$ranges[] = self::$installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases', self::$installed['versions'][$packageName])) {
$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced', self::$installed['versions'][$packageName])) {
$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided', self::$installed['versions'][$packageName])) {
$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
public static function getVersion($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
if (!isset(self::$installed['versions'][$packageName]['version'])) {
return null;
}
return self::$installed['versions'][$packageName]['version'];
}
public static function getPrettyVersion($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
if (!isset(self::$installed['versions'][$packageName]['pretty_version'])) {
return null;
}
return self::$installed['versions'][$packageName]['pretty_version'];
}
public static function getReference($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
if (!isset(self::$installed['versions'][$packageName]['reference'])) {
return null;
}
return self::$installed['versions'][$packageName]['reference'];
}
public static function getRootPackage()
{
return self::$installed['root'];
}
public static function getRawData()
{
return self::$installed;
}
public static function reload($data)
{
self::$installed = $data;
}
}

View File

@ -6,4 +6,5 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname(dirname($vendorDir));
return array(
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
);

View File

@ -6,5 +6,4 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname(dirname($vendorDir));
return array(
'JShrink' => array($vendorDir . '/tedivm/jshrink/src'),
);

View File

@ -7,6 +7,8 @@ $baseDir = dirname(dirname($vendorDir));
return array(
'PHPMailer\\PHPMailer\\' => array($vendorDir . '/phpmailer/phpmailer/src'),
'MatthiasMullie\\PathConverter\\' => array($vendorDir . '/matthiasmullie/path-converter/src'),
'MatthiasMullie\\Minify\\' => array($vendorDir . '/matthiasmullie/minify/src'),
'Ifsnop\\' => array($vendorDir . '/ifsnop/mysqldump-php/src/Ifsnop'),
'Hybridauth\\' => array($vendorDir . '/hybridauth/hybridauth/src'),
);

View File

@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit1da798ad8058bf7b615c0a3d8f41e314
class ComposerAutoloaderInit4ce406ae486ac58c9aa71537459207ae
{
private static $loader;
@ -22,15 +22,15 @@ class ComposerAutoloaderInit1da798ad8058bf7b615c0a3d8f41e314
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit1da798ad8058bf7b615c0a3d8f41e314', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInit4ce406ae486ac58c9aa71537459207ae', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit1da798ad8058bf7b615c0a3d8f41e314', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInit4ce406ae486ac58c9aa71537459207ae', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit1da798ad8058bf7b615c0a3d8f41e314::getInitializer($loader));
call_user_func(\Composer\Autoload\ComposerStaticInit4ce406ae486ac58c9aa71537459207ae::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {

View File

@ -4,13 +4,18 @@
namespace Composer\Autoload;
class ComposerStaticInit1da798ad8058bf7b615c0a3d8f41e314
class ComposerStaticInit4ce406ae486ac58c9aa71537459207ae
{
public static $prefixLengthsPsr4 = array (
'P' =>
array (
'PHPMailer\\PHPMailer\\' => 20,
),
'M' =>
array (
'MatthiasMullie\\PathConverter\\' => 29,
'MatthiasMullie\\Minify\\' => 22,
),
'I' =>
array (
'Ifsnop\\' => 7,
@ -26,6 +31,14 @@ class ComposerStaticInit1da798ad8058bf7b615c0a3d8f41e314
array (
0 => __DIR__ . '/..' . '/phpmailer/phpmailer/src',
),
'MatthiasMullie\\PathConverter\\' =>
array (
0 => __DIR__ . '/..' . '/matthiasmullie/path-converter/src',
),
'MatthiasMullie\\Minify\\' =>
array (
0 => __DIR__ . '/..' . '/matthiasmullie/minify/src',
),
'Ifsnop\\' =>
array (
0 => __DIR__ . '/..' . '/ifsnop/mysqldump-php/src/Ifsnop',
@ -36,22 +49,16 @@ class ComposerStaticInit1da798ad8058bf7b615c0a3d8f41e314
),
);
public static $prefixesPsr0 = array (
'J' =>
array (
'JShrink' =>
array (
0 => __DIR__ . '/..' . '/tedivm/jshrink/src',
),
),
public static $classMap = array (
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit1da798ad8058bf7b615c0a3d8f41e314::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit1da798ad8058bf7b615c0a3d8f41e314::$prefixDirsPsr4;
$loader->prefixesPsr0 = ComposerStaticInit1da798ad8058bf7b615c0a3d8f41e314::$prefixesPsr0;
$loader->prefixLengthsPsr4 = ComposerStaticInit4ce406ae486ac58c9aa71537459207ae::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit4ce406ae486ac58c9aa71537459207ae::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit4ce406ae486ac58c9aa71537459207ae::$classMap;
}, null, ClassLoader::class);
}

View File

@ -1,231 +1,313 @@
[
{
"name": "hybridauth/hybridauth",
"version": "3.3.0",
"version_normalized": "3.3.0.0",
"source": {
"type": "git",
"url": "https://github.com/hybridauth/hybridauth.git",
"reference": "51cb2ad2f04d175d298b51e919868dec1d4d8b04"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hybridauth/hybridauth/zipball/51cb2ad2f04d175d298b51e919868dec1d4d8b04",
"reference": "51cb2ad2f04d175d298b51e919868dec1d4d8b04",
"shasum": ""
},
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"ext-curl": "*",
"phpunit/phpunit": "^4.8.35 || ^6.5 || ^8"
},
"time": "2020-04-16T08:04:26+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Hybridauth\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Miled",
"email": "hybridauth@gmail.com"
}
],
"description": "PHP Social Authentication Library",
"homepage": "https://hybridauth.github.io",
"keywords": [
"Authentication",
"OpenId",
"api",
"authorization",
"facebook",
"google",
"oauth",
"social",
"twitter"
]
},
{
"name": "ifsnop/mysqldump-php",
"version": "v2.9",
"version_normalized": "2.9.0.0",
"source": {
"type": "git",
"url": "https://github.com/ifsnop/mysqldump-php.git",
"reference": "fc9c119fe5d70af9a685cad6a8ac612fd7589e25"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ifsnop/mysqldump-php/zipball/fc9c119fe5d70af9a685cad6a8ac612fd7589e25",
"reference": "fc9c119fe5d70af9a685cad6a8ac612fd7589e25",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "4.8.36",
"squizlabs/php_codesniffer": "1.*"
},
"time": "2020-04-03T14:40:40+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Ifsnop\\": "src/Ifsnop/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-3.0-or-later"
],
"authors": [
{
"name": "Diego Torres",
"homepage": "https://github.com/ifsnop",
"role": "Developer"
}
],
"description": "PHP version of mysqldump cli that comes with MySQL",
"homepage": "https://github.com/ifsnop/mysqldump-php",
"keywords": [
"PHP7",
"database",
"hhvm",
"mariadb",
"mysql",
"mysql-backup",
"mysqldump",
"pdo",
"php",
"php5",
"sql"
]
},
{
"name": "phpmailer/phpmailer",
"version": "v6.1.6",
"version_normalized": "6.1.6.0",
"source": {
"type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "c2796cb1cb99d7717290b48c4e6f32cb6c60b7b3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/c2796cb1cb99d7717290b48c4e6f32cb6c60b7b3",
"reference": "c2796cb1cb99d7717290b48c4e6f32cb6c60b7b3",
"shasum": ""
},
"require": {
"ext-ctype": "*",
"ext-filter": "*",
"php": ">=5.5.0"
},
"require-dev": {
"doctrine/annotations": "^1.2",
"friendsofphp/php-cs-fixer": "^2.2",
"phpunit/phpunit": "^4.8 || ^5.7"
},
"suggest": {
"ext-mbstring": "Needed to send email in multibyte encoding charset",
"hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication",
"league/oauth2-google": "Needed for Google XOAUTH2 authentication",
"psr/log": "For optional PSR-3 debug logging",
"stevenmaguire/oauth2-microsoft": "Needed for Microsoft XOAUTH2 authentication",
"symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)"
},
"time": "2020-05-27T12:24:03+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"PHPMailer\\PHPMailer\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1-only"
],
"authors": [
{
"name": "Marcus Bointon",
"email": "phpmailer@synchromedia.co.uk"
{
"packages": [
{
"name": "hybridauth/hybridauth",
"version": "3.3.0",
"version_normalized": "3.3.0.0",
"source": {
"type": "git",
"url": "https://github.com/hybridauth/hybridauth.git",
"reference": "51cb2ad2f04d175d298b51e919868dec1d4d8b04"
},
{
"name": "Jim Jagielski",
"email": "jimjag@gmail.com"
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hybridauth/hybridauth/zipball/51cb2ad2f04d175d298b51e919868dec1d4d8b04",
"reference": "51cb2ad2f04d175d298b51e919868dec1d4d8b04",
"shasum": ""
},
{
"name": "Andy Prevost",
"email": "codeworxtech@users.sourceforge.net"
"require": {
"php": ">=5.4.0"
},
{
"name": "Brent R. Matzelle"
}
],
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
"funding": [
{
"url": "https://github.com/synchro",
"type": "github"
}
]
},
{
"name": "tedivm/jshrink",
"version": "v1.3.3",
"version_normalized": "1.3.3.0",
"source": {
"type": "git",
"url": "https://github.com/tedious/JShrink.git",
"reference": "566e0c731ba4e372be2de429ef7d54f4faf4477a"
"require-dev": {
"ext-curl": "*",
"phpunit/phpunit": "^4.8.35 || ^6.5 || ^8"
},
"time": "2020-04-16T08:04:26+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Hybridauth\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Miled",
"email": "hybridauth@gmail.com"
}
],
"description": "PHP Social Authentication Library",
"homepage": "https://hybridauth.github.io",
"keywords": [
"Authentication",
"OpenId",
"api",
"authorization",
"facebook",
"google",
"oauth",
"social",
"twitter"
],
"install-path": "../hybridauth/hybridauth"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/tedious/JShrink/zipball/566e0c731ba4e372be2de429ef7d54f4faf4477a",
"reference": "566e0c731ba4e372be2de429ef7d54f4faf4477a",
"shasum": ""
{
"name": "ifsnop/mysqldump-php",
"version": "v2.9",
"version_normalized": "2.9.0.0",
"source": {
"type": "git",
"url": "https://github.com/ifsnop/mysqldump-php.git",
"reference": "fc9c119fe5d70af9a685cad6a8ac612fd7589e25"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ifsnop/mysqldump-php/zipball/fc9c119fe5d70af9a685cad6a8ac612fd7589e25",
"reference": "fc9c119fe5d70af9a685cad6a8ac612fd7589e25",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "4.8.36",
"squizlabs/php_codesniffer": "1.*"
},
"time": "2020-04-03T14:40:40+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Ifsnop\\": "src/Ifsnop/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-3.0-or-later"
],
"authors": [
{
"name": "Diego Torres",
"homepage": "https://github.com/ifsnop",
"role": "Developer"
}
],
"description": "PHP version of mysqldump cli that comes with MySQL",
"homepage": "https://github.com/ifsnop/mysqldump-php",
"keywords": [
"PHP7",
"database",
"hhvm",
"mariadb",
"mysql",
"mysql-backup",
"mysqldump",
"pdo",
"php",
"php5",
"sql"
],
"install-path": "../ifsnop/mysqldump-php"
},
"require": {
"php": "^5.6|^7.0"
{
"name": "matthiasmullie/minify",
"version": "1.3.63",
"version_normalized": "1.3.63.0",
"source": {
"type": "git",
"url": "https://github.com/matthiasmullie/minify.git",
"reference": "9ba1b459828adc13430f4dd6c49dae4950dc4117"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/matthiasmullie/minify/zipball/9ba1b459828adc13430f4dd6c49dae4950dc4117",
"reference": "9ba1b459828adc13430f4dd6c49dae4950dc4117",
"shasum": ""
},
"require": {
"ext-pcre": "*",
"matthiasmullie/path-converter": "~1.1",
"php": ">=5.3.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "~2.0",
"matthiasmullie/scrapbook": "~1.0",
"phpunit/phpunit": "~4.8"
},
"suggest": {
"psr/cache-implementation": "Cache implementation to use with Minify::cache"
},
"time": "2020-01-21T20:21:08+00:00",
"bin": [
"bin/minifycss",
"bin/minifyjs"
],
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"MatthiasMullie\\Minify\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Matthias Mullie",
"email": "minify@mullie.eu",
"homepage": "http://www.mullie.eu",
"role": "Developer"
}
],
"description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.",
"homepage": "http://www.minifier.org",
"keywords": [
"JS",
"css",
"javascript",
"minifier",
"minify"
],
"support": {
"issues": "https://github.com/matthiasmullie/minify/issues",
"source": "https://github.com/matthiasmullie/minify/tree/1.3.63"
},
"install-path": "../matthiasmullie/minify"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.8",
"php-coveralls/php-coveralls": "^1.1.0",
"phpunit/phpunit": "^6"
{
"name": "matthiasmullie/path-converter",
"version": "1.1.3",
"version_normalized": "1.1.3.0",
"source": {
"type": "git",
"url": "https://github.com/matthiasmullie/path-converter.git",
"reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/e7d13b2c7e2f2268e1424aaed02085518afa02d9",
"reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9",
"shasum": ""
},
"require": {
"ext-pcre": "*",
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "~4.8"
},
"time": "2019-02-05T23:41:09+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"MatthiasMullie\\PathConverter\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Matthias Mullie",
"email": "pathconverter@mullie.eu",
"homepage": "http://www.mullie.eu",
"role": "Developer"
}
],
"description": "Relative path converter",
"homepage": "http://github.com/matthiasmullie/path-converter",
"keywords": [
"converter",
"path",
"paths",
"relative"
],
"support": {
"issues": "https://github.com/matthiasmullie/path-converter/issues",
"source": "https://github.com/matthiasmullie/path-converter/tree/1.1.3"
},
"install-path": "../matthiasmullie/path-converter"
},
"time": "2019-06-28T18:11:46+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"JShrink": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Robert Hafner",
"email": "tedivm@tedivm.com"
}
],
"description": "Javascript Minifier built in PHP",
"homepage": "http://github.com/tedious/JShrink",
"keywords": [
"javascript",
"minifier"
]
}
]
{
"name": "phpmailer/phpmailer",
"version": "v6.1.6",
"version_normalized": "6.1.6.0",
"source": {
"type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "c2796cb1cb99d7717290b48c4e6f32cb6c60b7b3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/c2796cb1cb99d7717290b48c4e6f32cb6c60b7b3",
"reference": "c2796cb1cb99d7717290b48c4e6f32cb6c60b7b3",
"shasum": ""
},
"require": {
"ext-ctype": "*",
"ext-filter": "*",
"php": ">=5.5.0"
},
"require-dev": {
"doctrine/annotations": "^1.2",
"friendsofphp/php-cs-fixer": "^2.2",
"phpunit/phpunit": "^4.8 || ^5.7"
},
"suggest": {
"ext-mbstring": "Needed to send email in multibyte encoding charset",
"hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication",
"league/oauth2-google": "Needed for Google XOAUTH2 authentication",
"psr/log": "For optional PSR-3 debug logging",
"stevenmaguire/oauth2-microsoft": "Needed for Microsoft XOAUTH2 authentication",
"symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)"
},
"time": "2020-05-27T12:24:03+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"PHPMailer\\PHPMailer\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1-only"
],
"authors": [
{
"name": "Marcus Bointon",
"email": "phpmailer@synchromedia.co.uk"
},
{
"name": "Jim Jagielski",
"email": "jimjag@gmail.com"
},
{
"name": "Andy Prevost",
"email": "codeworxtech@users.sourceforge.net"
},
{
"name": "Brent R. Matzelle"
}
],
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
"funding": [
{
"url": "https://github.com/synchro",
"type": "github"
}
],
"install-path": "../phpmailer/phpmailer"
}
],
"dev": true,
"dev-package-names": []
}

View File

@ -0,0 +1,69 @@
<?php return array (
'root' =>
array (
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'aliases' =>
array (
),
'reference' => 'c258b6a2806c591575415a7b93f5543473d04e15',
'name' => 'e107inc/e107',
),
'versions' =>
array (
'e107inc/e107' =>
array (
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'aliases' =>
array (
),
'reference' => 'c258b6a2806c591575415a7b93f5543473d04e15',
),
'hybridauth/hybridauth' =>
array (
'pretty_version' => '3.3.0',
'version' => '3.3.0.0',
'aliases' =>
array (
),
'reference' => '51cb2ad2f04d175d298b51e919868dec1d4d8b04',
),
'ifsnop/mysqldump-php' =>
array (
'pretty_version' => 'v2.9',
'version' => '2.9.0.0',
'aliases' =>
array (
),
'reference' => 'fc9c119fe5d70af9a685cad6a8ac612fd7589e25',
),
'matthiasmullie/minify' =>
array (
'pretty_version' => '1.3.63',
'version' => '1.3.63.0',
'aliases' =>
array (
),
'reference' => '9ba1b459828adc13430f4dd6c49dae4950dc4117',
),
'matthiasmullie/path-converter' =>
array (
'pretty_version' => '1.1.3',
'version' => '1.1.3.0',
'aliases' =>
array (
),
'reference' => 'e7d13b2c7e2f2268e1424aaed02085518afa02d9',
),
'phpmailer/phpmailer' =>
array (
'pretty_version' => 'v6.1.6',
'version' => '6.1.6.0',
'aliases' =>
array (
),
'reference' => 'c2796cb1cb99d7717290b48c4e6f32cb6c60b7b3',
),
),
);

View File

@ -0,0 +1,59 @@
# How to contribute
## Issues
When [filing bugs](https://github.com/matthiasmullie/minify/issues/new),
try to be as thorough as possible:
* What version did you use?
* What did you try to do? ***Please post the relevant parts of your code.***
* What went wrong? ***Please include error messages, if any.***
* What was the expected result?
## Pull requests
Bug fixes and general improvements to the existing codebase are always welcome.
New features are also welcome, but will be judged on an individual basis. If
you'd rather not risk wasting your time implementing a new feature only to see
it turned down, please start the discussion by
[opening an issue](https://github.com/matthiasmullie/minify/issues/new).
Don't forget to add your changes to the [changelog](CHANGELOG.md).
### Testing
Please include tests for every change or addition to the code.
To run the complete test suite:
```sh
vendor/bin/phpunit
```
When submitting a new pull request, please make sure that that the test suite
passes (Travis CI will run it & report back on your pull request.)
To run the tests on Windows, run `tests/convert_symlinks_to_windows_style.sh`
from the command line in order to convert Linux-style test symlinks to
Windows-style.
### Coding standards
All code must follow [PSR-2](http://www.php-fig.org/psr/psr-2/). Just make sure
to run php-cs-fixer before submitting the code, it'll take care of the
formatting for you:
```sh
vendor/bin/php-cs-fixer fix src
vendor/bin/php-cs-fixer fix tests
```
Document the code thoroughly!
## License
Note that minify is MIT-licensed, which basically allows anyone to do
anything they like with it, without restriction.

View File

@ -0,0 +1,13 @@
ARG version=cli
FROM php:$version
COPY . /var/www
WORKDIR /var/www
RUN apt-get update
RUN apt-get install -y zip unzip zlib1g-dev
RUN docker-php-ext-install zip
RUN docker-php-ext-install pcntl
RUN curl -sS https://getcomposer.org/installer | php
RUN mv composer.phar /usr/local/bin/composer
RUN composer install

View File

@ -0,0 +1,18 @@
Copyright (c) 2012 Matthias Mullie
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,45 @@
#!/usr/bin/env php
<?php
use MatthiasMullie\Minify;
// command line utility to minify CSS
if (file_exists(__DIR__ . '/../../../autoload.php')) {
// if composer install
require_once __DIR__ . '/../../../autoload.php';
} else {
require_once __DIR__ . '/../src/Minify.php';
require_once __DIR__ . '/../src/CSS.php';
require_once __DIR__ . '/../src/Exception.php';
}
error_reporting(E_ALL);
// check PHP setup for cli arguments
if (!isset($_SERVER['argv']) && !isset($argv)) {
fwrite(STDERR, 'Please enable the "register_argc_argv" directive in your php.ini' . PHP_EOL);
exit(1);
} elseif (!isset($argv)) {
$argv = $_SERVER['argv'];
}
// check if path to file given
if (!isset($argv[1])) {
fwrite(STDERR, 'Argument expected: path to file' . PHP_EOL);
exit(1);
}
// check if script run in cli environment
if ('cli' !== php_sapi_name()) {
fwrite(STDERR, $argv[1] . ' must be run in the command line' . PHP_EOL);
exit(1);
}
// check if source file exists
if (!file_exists($argv[1])) {
fwrite(STDERR, 'Source file "' . $argv[1] . '" not found' . PHP_EOL);
exit(1);
}
try {
$minifier = new Minify\CSS($argv[1]);
echo $minifier->minify();
} catch (Exception $e) {
fwrite(STDERR, $e->getMessage(), PHP_EOL);
exit(1);
}

View File

@ -0,0 +1,45 @@
#!/usr/bin/env php
<?php
use MatthiasMullie\Minify;
// command line utility to minify JS
if (file_exists(__DIR__ . '/../../../autoload.php')) {
// if composer install
require_once __DIR__ . '/../../../autoload.php';
} else {
require_once __DIR__ . '/../src/Minify.php';
require_once __DIR__ . '/../src/JS.php';
require_once __DIR__ . '/../src/Exception.php';
}
error_reporting(E_ALL);
// check PHP setup for cli arguments
if (!isset($_SERVER['argv']) && !isset($argv)) {
fwrite(STDERR, 'Please enable the "register_argc_argv" directive in your php.ini' . PHP_EOL);
exit(1);
} elseif (!isset($argv)) {
$argv = $_SERVER['argv'];
}
// check if path to file given
if (!isset($argv[1])) {
fwrite(STDERR, 'Argument expected: path to file' . PHP_EOL);
exit(1);
}
// check if script run in cli environment
if ('cli' !== php_sapi_name()) {
fwrite(STDERR, $argv[1] . ' must be run in the command line' . PHP_EOL);
exit(1);
}
// check if source file exists
if (!file_exists($argv[1])) {
fwrite(STDERR, 'Source file "' . $argv[1] . '" not found' . PHP_EOL);
exit(1);
}
try {
$minifier = new Minify\JS($argv[1]);
echo $minifier->minify();
} catch (Exception $e) {
fwrite(STDERR, $e->getMessage(), PHP_EOL);
exit(1);
}

View File

@ -0,0 +1,38 @@
{
"name": "matthiasmullie/minify",
"type": "library",
"description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.",
"keywords": ["minify", "minifier", "css", "js", "javascript"],
"homepage": "http://www.minifier.org",
"license": "MIT",
"authors": [
{
"name": "Matthias Mullie",
"homepage": "http://www.mullie.eu",
"email": "minify@mullie.eu",
"role": "Developer"
}
],
"require": {
"php": ">=5.3.0",
"ext-pcre": "*",
"matthiasmullie/path-converter": "~1.1"
},
"require-dev": {
"matthiasmullie/scrapbook": "~1.0",
"phpunit/phpunit": "~4.8",
"friendsofphp/php-cs-fixer": "~2.0"
},
"suggest": {
"psr/cache-implementation": "Cache implementation to use with Minify::cache"
},
"autoload": {
"psr-4": {
"MatthiasMullie\\Minify\\": "src/"
}
},
"bin": [
"bin/minifycss",
"bin/minifyjs"
]
}

View File

@ -0,0 +1,7 @@
in
public
extends
private
protected
implements
instanceof

View File

@ -0,0 +1,26 @@
do
in
let
new
var
case
else
enum
void
with
class
const
yield
delete
export
import
public
static
typeof
extends
package
private
function
protected
implements
instanceof

View File

@ -0,0 +1,63 @@
do
if
in
for
let
new
try
var
case
else
enum
eval
null
this
true
void
with
break
catch
class
const
false
super
throw
while
yield
delete
export
import
public
return
static
switch
typeof
default
extends
finally
package
private
continue
debugger
function
arguments
interface
protected
implements
instanceof
abstract
boolean
byte
char
double
final
float
goto
int
long
native
short
synchronized
throws
transient
volatile

View File

@ -0,0 +1,46 @@
+
-
*
/
%
=
+=
-=
*=
/=
%=
<<=
>>=
>>>=
&=
^=
|=
&
|
^
~
<<
>>
>>>
==
===
!=
!==
>
<
>=
<=
&&
||
!
.
[
]
?
:
,
;
(
)
{
}

View File

@ -0,0 +1,43 @@
+
-
*
/
%
=
+=
-=
*=
/=
%=
<<=
>>=
>>>=
&=
^=
|=
&
|
^
<<
>>
>>>
==
===
!=
!==
>
<
>=
<=
&&
||
.
[
]
?
:
,
;
(
)
}

View File

@ -0,0 +1,43 @@
+
-
*
/
%
=
+=
-=
*=
/=
%=
<<=
>>=
>>>=
&=
^=
|=
&
|
^
~
<<
>>
>>>
==
===
!=
!==
>
<
>=
<=
&&
||
!
.
[
?
:
,
;
(
{

View File

@ -0,0 +1,31 @@
version: '2.1'
services:
php:
build:
context: .
dockerfile: Dockerfile
volumes:
- ./src:/var/www/src
- ./data:/var/www/data
- ./tests:/var/www/tests
- ./phpunit.xml.dist:/var/www/phpunit.xml.dist
'7.2':
extends: php
build:
args:
version: 7.2-cli
'7.1':
extends: php
build:
args:
version: 7.1-cli
'7.0':
extends: php
build:
args:
version: 7.0-cli
'5.6':
extends: php
build:
args:
version: 5.6-cli

View File

@ -0,0 +1,752 @@
<?php
/**
* CSS Minifier
*
* Please report bugs on https://github.com/matthiasmullie/minify/issues
*
* @author Matthias Mullie <minify@mullie.eu>
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
* @license MIT License
*/
namespace MatthiasMullie\Minify;
use MatthiasMullie\Minify\Exceptions\FileImportException;
use MatthiasMullie\PathConverter\ConverterInterface;
use MatthiasMullie\PathConverter\Converter;
/**
* CSS minifier
*
* Please report bugs on https://github.com/matthiasmullie/minify/issues
*
* @package Minify
* @author Matthias Mullie <minify@mullie.eu>
* @author Tijs Verkoyen <minify@verkoyen.eu>
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
* @license MIT License
*/
class CSS extends Minify
{
/**
* @var int maximum inport size in kB
*/
protected $maxImportSize = 5;
/**
* @var string[] valid import extensions
*/
protected $importExtensions = array(
'gif' => 'data:image/gif',
'png' => 'data:image/png',
'jpe' => 'data:image/jpeg',
'jpg' => 'data:image/jpeg',
'jpeg' => 'data:image/jpeg',
'svg' => 'data:image/svg+xml',
'woff' => 'data:application/x-font-woff',
'tif' => 'image/tiff',
'tiff' => 'image/tiff',
'xbm' => 'image/x-xbitmap',
);
/**
* Set the maximum size if files to be imported.
*
* Files larger than this size (in kB) will not be imported into the CSS.
* Importing files into the CSS as data-uri will save you some connections,
* but we should only import relatively small decorative images so that our
* CSS file doesn't get too bulky.
*
* @param int $size Size in kB
*/
public function setMaxImportSize($size)
{
$this->maxImportSize = $size;
}
/**
* Set the type of extensions to be imported into the CSS (to save network
* connections).
* Keys of the array should be the file extensions & respective values
* should be the data type.
*
* @param string[] $extensions Array of file extensions
*/
public function setImportExtensions(array $extensions)
{
$this->importExtensions = $extensions;
}
/**
* Move any import statements to the top.
*
* @param string $content Nearly finished CSS content
*
* @return string
*/
protected function moveImportsToTop($content)
{
if (preg_match_all('/(;?)(@import (?<url>url\()?(?P<quotes>["\']?).+?(?P=quotes)(?(url)\)));?/', $content, $matches)) {
// remove from content
foreach ($matches[0] as $import) {
$content = str_replace($import, '', $content);
}
// add to top
$content = implode(';', $matches[2]).';'.trim($content, ';');
}
return $content;
}
/**
* Combine CSS from import statements.
*
* @import's will be loaded and their content merged into the original file,
* to save HTTP requests.
*
* @param string $source The file to combine imports for
* @param string $content The CSS content to combine imports for
* @param string[] $parents Parent paths, for circular reference checks
*
* @return string
*
* @throws FileImportException
*/
protected function combineImports($source, $content, $parents)
{
$importRegexes = array(
// @import url(xxx)
'/
# import statement
@import
# whitespace
\s+
# open url()
url\(
# (optional) open path enclosure
(?P<quotes>["\']?)
# fetch path
(?P<path>.+?)
# (optional) close path enclosure
(?P=quotes)
# close url()
\)
# (optional) trailing whitespace
\s*
# (optional) media statement(s)
(?P<media>[^;]*)
# (optional) trailing whitespace
\s*
# (optional) closing semi-colon
;?
/ix',
// @import 'xxx'
'/
# import statement
@import
# whitespace
\s+
# open path enclosure
(?P<quotes>["\'])
# fetch path
(?P<path>.+?)
# close path enclosure
(?P=quotes)
# (optional) trailing whitespace
\s*
# (optional) media statement(s)
(?P<media>[^;]*)
# (optional) trailing whitespace
\s*
# (optional) closing semi-colon
;?
/ix',
);
// find all relative imports in css
$matches = array();
foreach ($importRegexes as $importRegex) {
if (preg_match_all($importRegex, $content, $regexMatches, PREG_SET_ORDER)) {
$matches = array_merge($matches, $regexMatches);
}
}
$search = array();
$replace = array();
// loop the matches
foreach ($matches as $match) {
// get the path for the file that will be imported
$importPath = dirname($source).'/'.$match['path'];
// only replace the import with the content if we can grab the
// content of the file
if (!$this->canImportByPath($match['path']) || !$this->canImportFile($importPath)) {
continue;
}
// check if current file was not imported previously in the same
// import chain.
if (in_array($importPath, $parents)) {
throw new FileImportException('Failed to import file "'.$importPath.'": circular reference detected.');
}
// grab referenced file & minify it (which may include importing
// yet other @import statements recursively)
$minifier = new static($importPath);
$minifier->setMaxImportSize($this->maxImportSize);
$minifier->setImportExtensions($this->importExtensions);
$importContent = $minifier->execute($source, $parents);
// check if this is only valid for certain media
if (!empty($match['media'])) {
$importContent = '@media '.$match['media'].'{'.$importContent.'}';
}
// add to replacement array
$search[] = $match[0];
$replace[] = $importContent;
}
// replace the import statements
return str_replace($search, $replace, $content);
}
/**
* Import files into the CSS, base64-ized.
*
* @url(image.jpg) images will be loaded and their content merged into the
* original file, to save HTTP requests.
*
* @param string $source The file to import files for
* @param string $content The CSS content to import files for
*
* @return string
*/
protected function importFiles($source, $content)
{
$regex = '/url\((["\']?)(.+?)\\1\)/i';
if ($this->importExtensions && preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) {
$search = array();
$replace = array();
// loop the matches
foreach ($matches as $match) {
$extension = substr(strrchr($match[2], '.'), 1);
if ($extension && !array_key_exists($extension, $this->importExtensions)) {
continue;
}
// get the path for the file that will be imported
$path = $match[2];
$path = dirname($source).'/'.$path;
// only replace the import with the content if we're able to get
// the content of the file, and it's relatively small
if ($this->canImportFile($path) && $this->canImportBySize($path)) {
// grab content && base64-ize
$importContent = $this->load($path);
$importContent = base64_encode($importContent);
// build replacement
$search[] = $match[0];
$replace[] = 'url('.$this->importExtensions[$extension].';base64,'.$importContent.')';
}
}
// replace the import statements
$content = str_replace($search, $replace, $content);
}
return $content;
}
/**
* Minify the data.
* Perform CSS optimizations.
*
* @param string[optional] $path Path to write the data to
* @param string[] $parents Parent paths, for circular reference checks
*
* @return string The minified data
*/
public function execute($path = null, $parents = array())
{
$content = '';
// loop CSS data (raw data and files)
foreach ($this->data as $source => $css) {
/*
* Let's first take out strings & comments, since we can't just
* remove whitespace anywhere. If whitespace occurs inside a string,
* we should leave it alone. E.g.:
* p { content: "a test" }
*/
$this->extractStrings();
$this->stripComments();
$this->extractCalcs();
$css = $this->replace($css);
$css = $this->stripWhitespace($css);
$css = $this->shortenColors($css);
$css = $this->shortenZeroes($css);
$css = $this->shortenFontWeights($css);
$css = $this->stripEmptyTags($css);
// restore the string we've extracted earlier
$css = $this->restoreExtractedData($css);
$source = is_int($source) ? '' : $source;
$parents = $source ? array_merge($parents, array($source)) : $parents;
$css = $this->combineImports($source, $css, $parents);
$css = $this->importFiles($source, $css);
/*
* If we'll save to a new path, we'll have to fix the relative paths
* to be relative no longer to the source file, but to the new path.
* If we don't write to a file, fall back to same path so no
* conversion happens (because we still want it to go through most
* of the move code, which also addresses url() & @import syntax...)
*/
$converter = $this->getPathConverter($source, $path ?: $source);
$css = $this->move($converter, $css);
// combine css
$content .= $css;
}
$content = $this->moveImportsToTop($content);
return $content;
}
/**
* Moving a css file should update all relative urls.
* Relative references (e.g. ../images/image.gif) in a certain css file,
* will have to be updated when a file is being saved at another location
* (e.g. ../../images/image.gif, if the new CSS file is 1 folder deeper).
*
* @param ConverterInterface $converter Relative path converter
* @param string $content The CSS content to update relative urls for
*
* @return string
*/
protected function move(ConverterInterface $converter, $content)
{
/*
* Relative path references will usually be enclosed by url(). @import
* is an exception, where url() is not necessary around the path (but is
* allowed).
* This *could* be 1 regular expression, where both regular expressions
* in this array are on different sides of a |. But we're using named
* patterns in both regexes, the same name on both regexes. This is only
* possible with a (?J) modifier, but that only works after a fairly
* recent PCRE version. That's why I'm doing 2 separate regular
* expressions & combining the matches after executing of both.
*/
$relativeRegexes = array(
// url(xxx)
'/
# open url()
url\(
\s*
# open path enclosure
(?P<quotes>["\'])?
# fetch path
(?P<path>.+?)
# close path enclosure
(?(quotes)(?P=quotes))
\s*
# close url()
\)
/ix',
// @import "xxx"
'/
# import statement
@import
# whitespace
\s+
# we don\'t have to check for @import url(), because the
# condition above will already catch these
# open path enclosure
(?P<quotes>["\'])
# fetch path
(?P<path>.+?)
# close path enclosure
(?P=quotes)
/ix',
);
// find all relative urls in css
$matches = array();
foreach ($relativeRegexes as $relativeRegex) {
if (preg_match_all($relativeRegex, $content, $regexMatches, PREG_SET_ORDER)) {
$matches = array_merge($matches, $regexMatches);
}
}
$search = array();
$replace = array();
// loop all urls
foreach ($matches as $match) {
// determine if it's a url() or an @import match
$type = (strpos($match[0], '@import') === 0 ? 'import' : 'url');
$url = $match['path'];
if ($this->canImportByPath($url)) {
// attempting to interpret GET-params makes no sense, so let's discard them for awhile
$params = strrchr($url, '?');
$url = $params ? substr($url, 0, -strlen($params)) : $url;
// fix relative url
$url = $converter->convert($url);
// now that the path has been converted, re-apply GET-params
$url .= $params;
}
/*
* Urls with control characters above 0x7e should be quoted.
* According to Mozilla's parser, whitespace is only allowed at the
* end of unquoted urls.
* Urls with `)` (as could happen with data: uris) should also be
* quoted to avoid being confused for the url() closing parentheses.
* And urls with a # have also been reported to cause issues.
* Urls with quotes inside should also remain escaped.
*
* @see https://developer.mozilla.org/nl/docs/Web/CSS/url#The_url()_functional_notation
* @see https://hg.mozilla.org/mozilla-central/rev/14abca4e7378
* @see https://github.com/matthiasmullie/minify/issues/193
*/
$url = trim($url);
if (preg_match('/[\s\)\'"#\x{7f}-\x{9f}]/u', $url)) {
$url = $match['quotes'] . $url . $match['quotes'];
}
// build replacement
$search[] = $match[0];
if ($type === 'url') {
$replace[] = 'url('.$url.')';
} elseif ($type === 'import') {
$replace[] = '@import "'.$url.'"';
}
}
// replace urls
return str_replace($search, $replace, $content);
}
/**
* Shorthand hex color codes.
* #FF0000 -> #F00.
*
* @param string $content The CSS content to shorten the hex color codes for
*
* @return string
*/
protected function shortenColors($content)
{
$content = preg_replace('/(?<=[: ])#([0-9a-z])\\1([0-9a-z])\\2([0-9a-z])\\3(?:([0-9a-z])\\4)?(?=[; }])/i', '#$1$2$3$4', $content);
// remove alpha channel if it's pointless...
$content = preg_replace('/(?<=[: ])#([0-9a-z]{6})ff?(?=[; }])/i', '#$1', $content);
$content = preg_replace('/(?<=[: ])#([0-9a-z]{3})f?(?=[; }])/i', '#$1', $content);
$colors = array(
// we can shorten some even more by replacing them with their color name
'#F0FFFF' => 'azure',
'#F5F5DC' => 'beige',
'#A52A2A' => 'brown',
'#FF7F50' => 'coral',
'#FFD700' => 'gold',
'#808080' => 'gray',
'#008000' => 'green',
'#4B0082' => 'indigo',
'#FFFFF0' => 'ivory',
'#F0E68C' => 'khaki',
'#FAF0E6' => 'linen',
'#800000' => 'maroon',
'#000080' => 'navy',
'#808000' => 'olive',
'#CD853F' => 'peru',
'#FFC0CB' => 'pink',
'#DDA0DD' => 'plum',
'#800080' => 'purple',
'#F00' => 'red',
'#FA8072' => 'salmon',
'#A0522D' => 'sienna',
'#C0C0C0' => 'silver',
'#FFFAFA' => 'snow',
'#D2B48C' => 'tan',
'#FF6347' => 'tomato',
'#EE82EE' => 'violet',
'#F5DEB3' => 'wheat',
// or the other way around
'WHITE' => '#fff',
'BLACK' => '#000',
);
return preg_replace_callback(
'/(?<=[: ])('.implode('|', array_keys($colors)).')(?=[; }])/i',
function ($match) use ($colors) {
return $colors[strtoupper($match[0])];
},
$content
);
}
/**
* Shorten CSS font weights.
*
* @param string $content The CSS content to shorten the font weights for
*
* @return string
*/
protected function shortenFontWeights($content)
{
$weights = array(
'normal' => 400,
'bold' => 700,
);
$callback = function ($match) use ($weights) {
return $match[1].$weights[$match[2]];
};
return preg_replace_callback('/(font-weight\s*:\s*)('.implode('|', array_keys($weights)).')(?=[;}])/', $callback, $content);
}
/**
* Shorthand 0 values to plain 0, instead of e.g. -0em.
*
* @param string $content The CSS content to shorten the zero values for
*
* @return string
*/
protected function shortenZeroes($content)
{
// we don't want to strip units in `calc()` expressions:
// `5px - 0px` is valid, but `5px - 0` is not
// `10px * 0` is valid (equates to 0), and so is `10 * 0px`, but
// `10 * 0` is invalid
// we've extracted calcs earlier, so we don't need to worry about this
// reusable bits of code throughout these regexes:
// before & after are used to make sure we don't match lose unintended
// 0-like values (e.g. in #000, or in http://url/1.0)
// units can be stripped from 0 values, or used to recognize non 0
// values (where wa may be able to strip a .0 suffix)
$before = '(?<=[:(, ])';
$after = '(?=[ ,);}])';
$units = '(em|ex|%|px|cm|mm|in|pt|pc|ch|rem|vh|vw|vmin|vmax|vm)';
// strip units after zeroes (0px -> 0)
// NOTE: it should be safe to remove all units for a 0 value, but in
// practice, Webkit (especially Safari) seems to stumble over at least
// 0%, potentially other units as well. Only stripping 'px' for now.
// @see https://github.com/matthiasmullie/minify/issues/60
$content = preg_replace('/'.$before.'(-?0*(\.0+)?)(?<=0)px'.$after.'/', '\\1', $content);
// strip 0-digits (.0 -> 0)
$content = preg_replace('/'.$before.'\.0+'.$units.'?'.$after.'/', '0\\1', $content);
// strip trailing 0: 50.10 -> 50.1, 50.10px -> 50.1px
$content = preg_replace('/'.$before.'(-?[0-9]+\.[0-9]+)0+'.$units.'?'.$after.'/', '\\1\\2', $content);
// strip trailing 0: 50.00 -> 50, 50.00px -> 50px
$content = preg_replace('/'.$before.'(-?[0-9]+)\.0+'.$units.'?'.$after.'/', '\\1\\2', $content);
// strip leading 0: 0.1 -> .1, 01.1 -> 1.1
$content = preg_replace('/'.$before.'(-?)0+([0-9]*\.[0-9]+)'.$units.'?'.$after.'/', '\\1\\2\\3', $content);
// strip negative zeroes (-0 -> 0) & truncate zeroes (00 -> 0)
$content = preg_replace('/'.$before.'-?0+'.$units.'?'.$after.'/', '0\\1', $content);
// IE doesn't seem to understand a unitless flex-basis value (correct -
// it goes against the spec), so let's add it in again (make it `%`,
// which is only 1 char: 0%, 0px, 0 anything, it's all just the same)
// @see https://developer.mozilla.org/nl/docs/Web/CSS/flex
$content = preg_replace('/flex:([0-9]+\s[0-9]+\s)0([;\}])/', 'flex:${1}0%${2}', $content);
$content = preg_replace('/flex-basis:0([;\}])/', 'flex-basis:0%${1}', $content);
return $content;
}
/**
* Strip empty tags from source code.
*
* @param string $content
*
* @return string
*/
protected function stripEmptyTags($content)
{
$content = preg_replace('/(?<=^)[^\{\};]+\{\s*\}/', '', $content);
$content = preg_replace('/(?<=(\}|;))[^\{\};]+\{\s*\}/', '', $content);
return $content;
}
/**
* Strip comments from source code.
*/
protected function stripComments()
{
// PHP only supports $this inside anonymous functions since 5.4
$minifier = $this;
$callback = function ($match) use ($minifier) {
$count = count($minifier->extracted);
$placeholder = '/*'.$count.'*/';
$minifier->extracted[$placeholder] = $match[0];
return $placeholder;
};
$this->registerPattern('/\n?\/\*(!|.*?@license|.*?@preserve).*?\*\/\n?/s', $callback);
$this->registerPattern('/\/\*.*?\*\//s', '');
}
/**
* Strip whitespace.
*
* @param string $content The CSS content to strip the whitespace for
*
* @return string
*/
protected function stripWhitespace($content)
{
// remove leading & trailing whitespace
$content = preg_replace('/^\s*/m', '', $content);
$content = preg_replace('/\s*$/m', '', $content);
// replace newlines with a single space
$content = preg_replace('/\s+/', ' ', $content);
// remove whitespace around meta characters
// inspired by stackoverflow.com/questions/15195750/minify-compress-css-with-regex
$content = preg_replace('/\s*([\*$~^|]?+=|[{};,>~]|!important\b)\s*/', '$1', $content);
$content = preg_replace('/([\[(:>\+])\s+/', '$1', $content);
$content = preg_replace('/\s+([\]\)>\+])/', '$1', $content);
$content = preg_replace('/\s+(:)(?![^\}]*\{)/', '$1', $content);
// whitespace around + and - can only be stripped inside some pseudo-
// classes, like `:nth-child(3+2n)`
// not in things like `calc(3px + 2px)`, shorthands like `3px -2px`, or
// selectors like `div.weird- p`
$pseudos = array('nth-child', 'nth-last-child', 'nth-last-of-type', 'nth-of-type');
$content = preg_replace('/:('.implode('|', $pseudos).')\(\s*([+-]?)\s*(.+?)\s*([+-]?)\s*(.*?)\s*\)/', ':$1($2$3$4$5)', $content);
// remove semicolon/whitespace followed by closing bracket
$content = str_replace(';}', '}', $content);
return trim($content);
}
/**
* Replace all `calc()` occurrences.
*/
protected function extractCalcs()
{
// PHP only supports $this inside anonymous functions since 5.4
$minifier = $this;
$callback = function ($match) use ($minifier) {
$length = strlen($match[1]);
$expr = '';
$opened = 0;
for ($i = 0; $i < $length; $i++) {
$char = $match[1][$i];
$expr .= $char;
if ($char === '(') {
$opened++;
} elseif ($char === ')' && --$opened === 0) {
break;
}
}
$rest = str_replace($expr, '', $match[1]);
$expr = trim(substr($expr, 1, -1));
$count = count($minifier->extracted);
$placeholder = 'calc('.$count.')';
$minifier->extracted[$placeholder] = 'calc('.$expr.')';
return $placeholder.$rest;
};
$this->registerPattern('/calc(\(.+?)(?=$|;|}|calc\()/', $callback);
$this->registerPattern('/calc(\(.+?)(?=$|;|}|calc\()/m', $callback);
}
/**
* Check if file is small enough to be imported.
*
* @param string $path The path to the file
*
* @return bool
*/
protected function canImportBySize($path)
{
return ($size = @filesize($path)) && $size <= $this->maxImportSize * 1024;
}
/**
* Check if file a file can be imported, going by the path.
*
* @param string $path
*
* @return bool
*/
protected function canImportByPath($path)
{
return preg_match('/^(data:|https?:|\\/)/', $path) === 0;
}
/**
* Return a converter to update relative paths to be relative to the new
* destination.
*
* @param string $source
* @param string $target
*
* @return ConverterInterface
*/
protected function getPathConverter($source, $target)
{
return new Converter($source, $target);
}
}

View File

@ -0,0 +1,20 @@
<?php
/**
* Base Exception
*
* @deprecated Use Exceptions\BasicException instead
*
* @author Matthias Mullie <minify@mullie.eu>
*/
namespace MatthiasMullie\Minify;
/**
* Base Exception Class
* @deprecated Use Exceptions\BasicException instead
*
* @package Minify
* @author Matthias Mullie <minify@mullie.eu>
*/
abstract class Exception extends \Exception
{
}

View File

@ -0,0 +1,23 @@
<?php
/**
* Basic exception
*
* Please report bugs on https://github.com/matthiasmullie/minify/issues
*
* @author Matthias Mullie <minify@mullie.eu>
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
* @license MIT License
*/
namespace MatthiasMullie\Minify\Exceptions;
use MatthiasMullie\Minify\Exception;
/**
* Basic Exception Class
*
* @package Minify\Exception
* @author Matthias Mullie <minify@mullie.eu>
*/
abstract class BasicException extends Exception
{
}

View File

@ -0,0 +1,21 @@
<?php
/**
* File Import Exception
*
* Please report bugs on https://github.com/matthiasmullie/minify/issues
*
* @author Matthias Mullie <minify@mullie.eu>
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
* @license MIT License
*/
namespace MatthiasMullie\Minify\Exceptions;
/**
* File Import Exception Class
*
* @package Minify\Exception
* @author Matthias Mullie <minify@mullie.eu>
*/
class FileImportException extends BasicException
{
}

View File

@ -0,0 +1,21 @@
<?php
/**
* IO Exception
*
* Please report bugs on https://github.com/matthiasmullie/minify/issues
*
* @author Matthias Mullie <minify@mullie.eu>
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
* @license MIT License
*/
namespace MatthiasMullie\Minify\Exceptions;
/**
* IO Exception Class
*
* @package Minify\Exception
* @author Matthias Mullie <minify@mullie.eu>
*/
class IOException extends BasicException
{
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,497 @@
<?php
/**
* Abstract minifier class
*
* Please report bugs on https://github.com/matthiasmullie/minify/issues
*
* @author Matthias Mullie <minify@mullie.eu>
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
* @license MIT License
*/
namespace MatthiasMullie\Minify;
use MatthiasMullie\Minify\Exceptions\IOException;
use Psr\Cache\CacheItemInterface;
/**
* Abstract minifier class.
*
* Please report bugs on https://github.com/matthiasmullie/minify/issues
*
* @package Minify
* @author Matthias Mullie <minify@mullie.eu>
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
* @license MIT License
*/
abstract class Minify
{
/**
* The data to be minified.
*
* @var string[]
*/
protected $data = array();
/**
* Array of patterns to match.
*
* @var string[]
*/
protected $patterns = array();
/**
* This array will hold content of strings and regular expressions that have
* been extracted from the JS source code, so we can reliably match "code",
* without having to worry about potential "code-like" characters inside.
*
* @var string[]
*/
public $extracted = array();
/**
* Init the minify class - optionally, code may be passed along already.
*/
public function __construct(/* $data = null, ... */)
{
// it's possible to add the source through the constructor as well ;)
if (func_num_args()) {
call_user_func_array(array($this, 'add'), func_get_args());
}
}
/**
* Add a file or straight-up code to be minified.
*
* @param string|string[] $data
*
* @return static
*/
public function add($data /* $data = null, ... */)
{
// bogus "usage" of parameter $data: scrutinizer warns this variable is
// not used (we're using func_get_args instead to support overloading),
// but it still needs to be defined because it makes no sense to have
// this function without argument :)
$args = array($data) + func_get_args();
// this method can be overloaded
foreach ($args as $data) {
if (is_array($data)) {
call_user_func_array(array($this, 'add'), $data);
continue;
}
// redefine var
$data = (string) $data;
// load data
$value = $this->load($data);
$key = ($data != $value) ? $data : count($this->data);
// replace CR linefeeds etc.
// @see https://github.com/matthiasmullie/minify/pull/139
$value = str_replace(array("\r\n", "\r"), "\n", $value);
// store data
$this->data[$key] = $value;
}
return $this;
}
/**
* Add a file to be minified.
*
* @param string|string[] $data
*
* @return static
*
* @throws IOException
*/
public function addFile($data /* $data = null, ... */)
{
// bogus "usage" of parameter $data: scrutinizer warns this variable is
// not used (we're using func_get_args instead to support overloading),
// but it still needs to be defined because it makes no sense to have
// this function without argument :)
$args = array($data) + func_get_args();
// this method can be overloaded
foreach ($args as $path) {
if (is_array($path)) {
call_user_func_array(array($this, 'addFile'), $path);
continue;
}
// redefine var
$path = (string) $path;
// check if we can read the file
if (!$this->canImportFile($path)) {
throw new IOException('The file "'.$path.'" could not be opened for reading. Check if PHP has enough permissions.');
}
$this->add($path);
}
return $this;
}
/**
* Minify the data & (optionally) saves it to a file.
*
* @param string[optional] $path Path to write the data to
*
* @return string The minified data
*/
public function minify($path = null)
{
$content = $this->execute($path);
// save to path
if ($path !== null) {
$this->save($content, $path);
}
return $content;
}
/**
* Minify & gzip the data & (optionally) saves it to a file.
*
* @param string[optional] $path Path to write the data to
* @param int[optional] $level Compression level, from 0 to 9
*
* @return string The minified & gzipped data
*/
public function gzip($path = null, $level = 9)
{
$content = $this->execute($path);
$content = gzencode($content, $level, FORCE_GZIP);
// save to path
if ($path !== null) {
$this->save($content, $path);
}
return $content;
}
/**
* Minify the data & write it to a CacheItemInterface object.
*
* @param CacheItemInterface $item Cache item to write the data to
*
* @return CacheItemInterface Cache item with the minifier data
*/
public function cache(CacheItemInterface $item)
{
$content = $this->execute();
$item->set($content);
return $item;
}
/**
* Minify the data.
*
* @param string[optional] $path Path to write the data to
*
* @return string The minified data
*/
abstract public function execute($path = null);
/**
* Load data.
*
* @param string $data Either a path to a file or the content itself
*
* @return string
*/
protected function load($data)
{
// check if the data is a file
if ($this->canImportFile($data)) {
$data = file_get_contents($data);
// strip BOM, if any
if (substr($data, 0, 3) == "\xef\xbb\xbf") {
$data = substr($data, 3);
}
}
return $data;
}
/**
* Save to file.
*
* @param string $content The minified data
* @param string $path The path to save the minified data to
*
* @throws IOException
*/
protected function save($content, $path)
{
$handler = $this->openFileForWriting($path);
$this->writeToFile($handler, $content);
@fclose($handler);
}
/**
* Register a pattern to execute against the source content.
*
* @param string $pattern PCRE pattern
* @param string|callable $replacement Replacement value for matched pattern
*/
protected function registerPattern($pattern, $replacement = '')
{
// study the pattern, we'll execute it more than once
$pattern .= 'S';
$this->patterns[] = array($pattern, $replacement);
}
/**
* We can't "just" run some regular expressions against JavaScript: it's a
* complex language. E.g. having an occurrence of // xyz would be a comment,
* unless it's used within a string. Of you could have something that looks
* like a 'string', but inside a comment.
* The only way to accurately replace these pieces is to traverse the JS one
* character at a time and try to find whatever starts first.
*
* @param string $content The content to replace patterns in
*
* @return string The (manipulated) content
*/
protected function replace($content)
{
$processed = '';
$positions = array_fill(0, count($this->patterns), -1);
$matches = array();
while ($content) {
// find first match for all patterns
foreach ($this->patterns as $i => $pattern) {
list($pattern, $replacement) = $pattern;
// we can safely ignore patterns for positions we've unset earlier,
// because we know these won't show up anymore
if (array_key_exists($i, $positions) == false) {
continue;
}
// no need to re-run matches that are still in the part of the
// content that hasn't been processed
if ($positions[$i] >= 0) {
continue;
}
$match = null;
if (preg_match($pattern, $content, $match, PREG_OFFSET_CAPTURE)) {
$matches[$i] = $match;
// we'll store the match position as well; that way, we
// don't have to redo all preg_matches after changing only
// the first (we'll still know where those others are)
$positions[$i] = $match[0][1];
} else {
// if the pattern couldn't be matched, there's no point in
// executing it again in later runs on this same content;
// ignore this one until we reach end of content
unset($matches[$i], $positions[$i]);
}
}
// no more matches to find: everything's been processed, break out
if (!$matches) {
$processed .= $content;
break;
}
// see which of the patterns actually found the first thing (we'll
// only want to execute that one, since we're unsure if what the
// other found was not inside what the first found)
$discardLength = min($positions);
$firstPattern = array_search($discardLength, $positions);
$match = $matches[$firstPattern][0][0];
// execute the pattern that matches earliest in the content string
list($pattern, $replacement) = $this->patterns[$firstPattern];
$replacement = $this->replacePattern($pattern, $replacement, $content);
// figure out which part of the string was unmatched; that's the
// part we'll execute the patterns on again next
$content = (string) substr($content, $discardLength);
$unmatched = (string) substr($content, strpos($content, $match) + strlen($match));
// move the replaced part to $processed and prepare $content to
// again match batch of patterns against
$processed .= substr($replacement, 0, strlen($replacement) - strlen($unmatched));
$content = $unmatched;
// first match has been replaced & that content is to be left alone,
// the next matches will start after this replacement, so we should
// fix their offsets
foreach ($positions as $i => $position) {
$positions[$i] -= $discardLength + strlen($match);
}
}
return $processed;
}
/**
* This is where a pattern is matched against $content and the matches
* are replaced by their respective value.
* This function will be called plenty of times, where $content will always
* move up 1 character.
*
* @param string $pattern Pattern to match
* @param string|callable $replacement Replacement value
* @param string $content Content to match pattern against
*
* @return string
*/
protected function replacePattern($pattern, $replacement, $content)
{
if (is_callable($replacement)) {
return preg_replace_callback($pattern, $replacement, $content, 1, $count);
} else {
return preg_replace($pattern, $replacement, $content, 1, $count);
}
}
/**
* Strings are a pattern we need to match, in order to ignore potential
* code-like content inside them, but we just want all of the string
* content to remain untouched.
*
* This method will replace all string content with simple STRING#
* placeholder text, so we've rid all strings from characters that may be
* misinterpreted. Original string content will be saved in $this->extracted
* and after doing all other minifying, we can restore the original content
* via restoreStrings().
*
* @param string[optional] $chars
* @param string[optional] $placeholderPrefix
*/
protected function extractStrings($chars = '\'"', $placeholderPrefix = '')
{
// PHP only supports $this inside anonymous functions since 5.4
$minifier = $this;
$callback = function ($match) use ($minifier, $placeholderPrefix) {
// check the second index here, because the first always contains a quote
if ($match[2] === '') {
/*
* Empty strings need no placeholder; they can't be confused for
* anything else anyway.
* But we still needed to match them, for the extraction routine
* to skip over this particular string.
*/
return $match[0];
}
$count = count($minifier->extracted);
$placeholder = $match[1].$placeholderPrefix.$count.$match[1];
$minifier->extracted[$placeholder] = $match[1].$match[2].$match[1];
return $placeholder;
};
/*
* The \\ messiness explained:
* * Don't count ' or " as end-of-string if it's escaped (has backslash
* in front of it)
* * Unless... that backslash itself is escaped (another leading slash),
* in which case it's no longer escaping the ' or "
* * So there can be either no backslash, or an even number
* * multiply all of that times 4, to account for the escaping that has
* to be done to pass the backslash into the PHP string without it being
* considered as escape-char (times 2) and to get it in the regex,
* escaped (times 2)
*/
$this->registerPattern('/(['.$chars.'])(.*?(?<!\\\\)(\\\\\\\\)*+)\\1/s', $callback);
}
/**
* This method will restore all extracted data (strings, regexes) that were
* replaced with placeholder text in extract*(). The original content was
* saved in $this->extracted.
*
* @param string $content
*
* @return string
*/
protected function restoreExtractedData($content)
{
if (!$this->extracted) {
// nothing was extracted, nothing to restore
return $content;
}
$content = strtr($content, $this->extracted);
$this->extracted = array();
return $content;
}
/**
* Check if the path is a regular file and can be read.
*
* @param string $path
*
* @return bool
*/
protected function canImportFile($path)
{
$parsed = parse_url($path);
if (
// file is elsewhere
isset($parsed['host']) ||
// file responds to queries (may change, or need to bypass cache)
isset($parsed['query'])
) {
return false;
}
return strlen($path) < PHP_MAXPATHLEN && @is_file($path) && is_readable($path);
}
/**
* Attempts to open file specified by $path for writing.
*
* @param string $path The path to the file
*
* @return resource Specifier for the target file
*
* @throws IOException
*/
protected function openFileForWriting($path)
{
if (($handler = @fopen($path, 'w')) === false) {
throw new IOException('The file "'.$path.'" could not be opened for writing. Check if PHP has enough permissions.');
}
return $handler;
}
/**
* Attempts to write $content to the file specified by $handler. $path is used for printing exceptions.
*
* @param resource $handler The resource to write to
* @param string $content The content to write
* @param string $path The path to the file (for exception printing only)
*
* @throws IOException
*/
protected function writeToFile($handler, $content, $path = '')
{
if (($result = @fwrite($handler, $content)) === false || ($result < strlen($content))) {
throw new IOException('The file "'.$path.'" could not be written to. Check your disk space and file permissions.');
}
}
}

View File

@ -0,0 +1,18 @@
Copyright (c) 2015 Matthias Mullie
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,28 @@
{
"name": "matthiasmullie/path-converter",
"type": "library",
"description": "Relative path converter",
"keywords": ["relative", "path", "converter", "paths"],
"homepage": "http://github.com/matthiasmullie/path-converter",
"license": "MIT",
"authors": [
{
"name": "Matthias Mullie",
"homepage": "http://www.mullie.eu",
"email": "pathconverter@mullie.eu",
"role": "Developer"
}
],
"require": {
"php": ">=5.3.0",
"ext-pcre": "*"
},
"require-dev": {
"phpunit/phpunit": "~4.8"
},
"autoload": {
"psr-4": {
"MatthiasMullie\\PathConverter\\": "src/"
}
}
}

View File

@ -0,0 +1,204 @@
<?php
namespace MatthiasMullie\PathConverter;
/**
* Convert paths relative from 1 file to another.
*
* E.g.
* ../../images/icon.jpg relative to /css/imports/icons.css
* becomes
* ../images/icon.jpg relative to /css/minified.css
*
* Please report bugs on https://github.com/matthiasmullie/path-converter/issues
*
* @author Matthias Mullie <pathconverter@mullie.eu>
* @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
* @license MIT License
*/
class Converter implements ConverterInterface
{
/**
* @var string
*/
protected $from;
/**
* @var string
*/
protected $to;
/**
* @param string $from The original base path (directory, not file!)
* @param string $to The new base path (directory, not file!)
* @param string $root Root directory (defaults to `getcwd`)
*/
public function __construct($from, $to, $root = '')
{
$shared = $this->shared($from, $to);
if ($shared === '') {
// when both paths have nothing in common, one of them is probably
// absolute while the other is relative
$root = $root ?: getcwd();
$from = strpos($from, $root) === 0 ? $from : preg_replace('/\/+/', '/', $root.'/'.$from);
$to = strpos($to, $root) === 0 ? $to : preg_replace('/\/+/', '/', $root.'/'.$to);
// or traveling the tree via `..`
// attempt to resolve path, or assume it's fine if it doesn't exist
$from = @realpath($from) ?: $from;
$to = @realpath($to) ?: $to;
}
$from = $this->dirname($from);
$to = $this->dirname($to);
$from = $this->normalize($from);
$to = $this->normalize($to);
$this->from = $from;
$this->to = $to;
}
/**
* Normalize path.
*
* @param string $path
*
* @return string
*/
protected function normalize($path)
{
// deal with different operating systems' directory structure
$path = rtrim(str_replace(DIRECTORY_SEPARATOR, '/', $path), '/');
// remove leading current directory.
if (substr($path, 0, 2) === './') {
$path = substr($path, 2);
}
// remove references to current directory in the path.
$path = str_replace('/./', '/', $path);
/*
* Example:
* /home/forkcms/frontend/cache/compiled_templates/../../core/layout/css/../images/img.gif
* to
* /home/forkcms/frontend/core/layout/images/img.gif
*/
do {
$path = preg_replace('/[^\/]+(?<!\.\.)\/\.\.\//', '', $path, -1, $count);
} while ($count);
return $path;
}
/**
* Figure out the shared path of 2 locations.
*
* Example:
* /home/forkcms/frontend/core/layout/images/img.gif
* and
* /home/forkcms/frontend/cache/minified_css
* share
* /home/forkcms/frontend
*
* @param string $path1
* @param string $path2
*
* @return string
*/
protected function shared($path1, $path2)
{
// $path could theoretically be empty (e.g. no path is given), in which
// case it shouldn't expand to array(''), which would compare to one's
// root /
$path1 = $path1 ? explode('/', $path1) : array();
$path2 = $path2 ? explode('/', $path2) : array();
$shared = array();
// compare paths & strip identical ancestors
foreach ($path1 as $i => $chunk) {
if (isset($path2[$i]) && $path1[$i] == $path2[$i]) {
$shared[] = $chunk;
} else {
break;
}
}
return implode('/', $shared);
}
/**
* Convert paths relative from 1 file to another.
*
* E.g.
* ../images/img.gif relative to /home/forkcms/frontend/core/layout/css
* should become:
* ../../core/layout/images/img.gif relative to
* /home/forkcms/frontend/cache/minified_css
*
* @param string $path The relative path that needs to be converted
*
* @return string The new relative path
*/
public function convert($path)
{
// quit early if conversion makes no sense
if ($this->from === $this->to) {
return $path;
}
$path = $this->normalize($path);
// if we're not dealing with a relative path, just return absolute
if (strpos($path, '/') === 0) {
return $path;
}
// normalize paths
$path = $this->normalize($this->from.'/'.$path);
// strip shared ancestor paths
$shared = $this->shared($path, $this->to);
$path = mb_substr($path, mb_strlen($shared));
$to = mb_substr($this->to, mb_strlen($shared));
// add .. for every directory that needs to be traversed to new path
$to = str_repeat('../', count(array_filter(explode('/', $to))));
return $to.ltrim($path, '/');
}
/**
* Attempt to get the directory name from a path.
*
* @param string $path
*
* @return string
*/
protected function dirname($path)
{
if (@is_file($path)) {
return dirname($path);
}
if (@is_dir($path)) {
return rtrim($path, '/');
}
// no known file/dir, start making assumptions
// ends in / = dir
if (mb_substr($path, -1) === '/') {
return rtrim($path, '/');
}
// has a dot in the name, likely a file
if (preg_match('/.*\..*$/', basename($path)) !== 0) {
return dirname($path);
}
// you're on your own here!
return $path;
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace MatthiasMullie\PathConverter;
/**
* Convert file paths.
*
* Please report bugs on https://github.com/matthiasmullie/path-converter/issues
*
* @author Matthias Mullie <pathconverter@mullie.eu>
* @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
* @license MIT License
*/
interface ConverterInterface
{
/**
* Convert file paths.
*
* @param string $path The path to be converted
*
* @return string The new path
*/
public function convert($path);
}

View File

@ -0,0 +1,23 @@
<?php
namespace MatthiasMullie\PathConverter;
/**
* Don't convert paths.
*
* Please report bugs on https://github.com/matthiasmullie/path-converter/issues
*
* @author Matthias Mullie <pathconverter@mullie.eu>
* @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
* @license MIT License
*/
class NoConverter implements ConverterInterface
{
/**
* {@inheritdoc}
*/
public function convert($path)
{
return $path;
}
}

View File

@ -1 +0,0 @@
tidelift: "packagist/tedivm/jshrink"

View File

@ -1,54 +0,0 @@
# Contributions Welcome!
Pull Requests and Community Contributions are the bread and butter of open source software. Every contribution- from bug
reports to feature requests, typos to full new features- are greatly appreciated.
## Important Guidelines
* One Item Per Pull Request or Issue. This makes it much easier to review code and merge it back in, and prevents issues
with one request from blocking another.
* Code Coverage is extremely important, and pull requests are much more likely to be accepted if testing is also improved.
New code should be properly tested, and all tests must pass.
* Read the LICENSE document and make sure you understand it, because your code is going to be released under it.
* Be prepared to make revisions. Don't be discouraged if you're asked to make changes, as that is just another step
towards refining the code and getting it merged back in.
* Remember to add the relevant documentation, particular the docblock comments.
## Code Styling
This project follows the PSR standards set forth by the [PHP Framework Interop Group](http://www.php-fig.org/).
* [PSR-0: Class and file naming conventions](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md)
* [PSR-1: Basic coding standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md)
* [PSR-2: Coding style guide](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)
All code most follow these standards to be accepted. The easiest way to accomplish this is to run php-cs-fixer once the
new changes are finished. The php-cs-fixer package is installed as a development dependency of this project.
composer install --dev
vendor/bin/php-cs-fixer fix ./ --level="all" -vv
## Running the test suite
First install dependencies using Composer. It's important to include the dev packages:
composer install --dev
The "runTests.sh" script runs the full test suite- phpunit, php-cs-fixer, as well as any environmental setup:
tests/runTests.sh
To call phpunit directly:
vendor/bin/phpunit
To call php-cs-fixer directly:
vendor/bin/php-cs-fixer fix ./ --level="all" -vv --dry-run

View File

@ -1,29 +0,0 @@
BSD 3-Clause License
Copyright (c) 2009, Robert Hafner
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,72 +0,0 @@
# JShrink [![Build Status](https://travis-ci.org/tedious/JShrink.svg?branch=master)](https://travis-ci.org/tedivm/JShrink)
[![License](http://img.shields.io/packagist/l/tedivm/JShrink.svg)](https://github.com/tedivm/JShrink/blob/master/LICENSE)
[![Latest Stable Version](http://img.shields.io/github/release/tedious/JShrink.svg)](https://packagist.org/packages/tedivm/JShrink)
[![Coverage Status](https://coveralls.io/repos/tedious/JShrink/badge.png?branch=master)](https://coveralls.io/r/tedivm/JShrink?branch=master)
[![Total Downloads](http://img.shields.io/packagist/dt/tedivm/jshrink.svg)](https://packagist.org/packages/tedivm/JShrink)
JShrink is a php class that minifies javascript so that it can be delivered to the client quicker. This code can be used
by any product looking to minify their javascript on the fly (although caching the results is suggested for performance
reasons). Unlike many other products this is not a port into php but a native application, resulting in better
performance.
## Usage
Minifying your code is simple call to a static function-
```php
<?php
include('vendor/autoload.php');
// Basic (default) usage.
$minifiedCode = \JShrink\Minifier::minify($js);
// Disable YUI style comment preservation.
$minifiedCode = \JShrink\Minifier::minify($js, array('flaggedComments' => false));
```
## Results
* Raw - 586,990
* Gzip - 151,301
* JShrink - 371,982
* JShrink and Gzip - 93,507
## Installing
### Composer
Installing JShrink can be done through a variety of methods, although Composer is
recommended.
```yaml
"require": {
"tedivm/jshrink": "~1.0"
}
```
### Github
Releases of JShrink are available on [Github](https://github.com/tedious/JShrink/releases).
## License
JShrink is licensed under the BSD License. See the LICENSE file for details.
In the spirit of open source, use of this library for evil is discouraged but not prohibited.
## Reporting Security and Vulnerability Issues
This project utilizes the [Tidelift Security Reporting System](https://tidelift.com/security) for security and vulnerability reporting.
## Support
Issues can be opened directly in Github for issues that aren't related to security.
[Professionally supported JShrink is now available with Tidelift.](https://tidelift.com/subscription/pkg/packagist-tedivm-jshrink?utm_source=packagist-tedivm-jshrink&utm_medium=referral&utm_campaign=readme)

View File

@ -1,25 +0,0 @@
{
"name": "tedivm/jshrink",
"description": "Javascript Minifier built in PHP",
"keywords": ["minifier","javascript"],
"homepage": "http://github.com/tedious/JShrink",
"type": "library",
"license": "BSD-3-Clause",
"authors": [
{
"name": "Robert Hafner",
"email": "tedivm@tedivm.com"
}
],
"require": {
"php": "^5.6|^7.0"
},
"require-dev": {
"phpunit/phpunit": "^6",
"friendsofphp/php-cs-fixer": "^2.8",
"php-coveralls/php-coveralls": "^1.1.0"
},
"autoload": {
"psr-0": {"JShrink": "src/"}
}
}

View File

@ -1,592 +0,0 @@
<?php
/*
* This file is part of the JShrink package.
*
* (c) Robert Hafner <tedivm@tedivm.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* JShrink
*
*
* @package JShrink
* @author Robert Hafner <tedivm@tedivm.com>
*/
namespace JShrink;
/**
* Minifier
*
* Usage - Minifier::minify($js);
* Usage - Minifier::minify($js, $options);
* Usage - Minifier::minify($js, array('flaggedComments' => false));
*
* @package JShrink
* @author Robert Hafner <tedivm@tedivm.com>
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
class Minifier
{
/**
* The input javascript to be minified.
*
* @var string
*/
protected $input;
/**
* The location of the character (in the input string) that is next to be
* processed.
*
* @var int
*/
protected $index = 0;
/**
* The first of the characters currently being looked at.
*
* @var string
*/
protected $a = '';
/**
* The next character being looked at (after a);
*
* @var string
*/
protected $b = '';
/**
* This character is only active when certain look ahead actions take place.
*
* @var string
*/
protected $c;
/**
* Contains the options for the current minification process.
*
* @var array
*/
protected $options;
/**
* These characters are used to define strings.
*/
protected $stringDelimiters = ['\'', '"', '`'];
/**
* Contains the default options for minification. This array is merged with
* the one passed in by the user to create the request specific set of
* options (stored in the $options attribute).
*
* @var array
*/
protected static $defaultOptions = array('flaggedComments' => true);
/**
* Contains lock ids which are used to replace certain code patterns and
* prevent them from being minified
*
* @var array
*/
protected $locks = array();
/**
* Takes a string containing javascript and removes unneeded characters in
* order to shrink the code without altering it's functionality.
*
* @param string $js The raw javascript to be minified
* @param array $options Various runtime options in an associative array
* @throws \Exception
* @return bool|string
*/
public static function minify($js, $options = array())
{
try {
ob_start();
$jshrink = new Minifier();
$js = $jshrink->lock($js);
$jshrink->minifyDirectToOutput($js, $options);
// Sometimes there's a leading new line, so we trim that out here.
$js = ltrim(ob_get_clean());
$js = $jshrink->unlock($js);
unset($jshrink);
return $js;
} catch (\Exception $e) {
if (isset($jshrink)) {
// Since the breakdownScript function probably wasn't finished
// we clean it out before discarding it.
$jshrink->clean();
unset($jshrink);
}
// without this call things get weird, with partially outputted js.
ob_end_clean();
throw $e;
}
}
/**
* Processes a javascript string and outputs only the required characters,
* stripping out all unneeded characters.
*
* @param string $js The raw javascript to be minified
* @param array $options Various runtime options in an associative array
*/
protected function minifyDirectToOutput($js, $options)
{
$this->initialize($js, $options);
$this->loop();
$this->clean();
}
/**
* Initializes internal variables, normalizes new lines,
*
* @param string $js The raw javascript to be minified
* @param array $options Various runtime options in an associative array
*/
protected function initialize($js, $options)
{
$this->options = array_merge(static::$defaultOptions, $options);
$js = str_replace("\r\n", "\n", $js);
$js = str_replace('/**/', '', $js);
$this->input = str_replace("\r", "\n", $js);
// We add a newline to the end of the script to make it easier to deal
// with comments at the bottom of the script- this prevents the unclosed
// comment error that can otherwise occur.
$this->input .= PHP_EOL;
// Populate "a" with a new line, "b" with the first character, before
// entering the loop
$this->a = "\n";
$this->b = $this->getReal();
}
/**
* The primary action occurs here. This function loops through the input string,
* outputting anything that's relevant and discarding anything that is not.
*/
protected function loop()
{
while ($this->a !== false && !is_null($this->a) && $this->a !== '') {
switch ($this->a) {
// new lines
case "\n":
// if the next line is something that can't stand alone preserve the newline
if ($this->b !== false && strpos('(-+[@', $this->b) !== false) {
echo $this->a;
$this->saveString();
break;
}
// if B is a space we skip the rest of the switch block and go down to the
// string/regex check below, resetting $this->b with getReal
if ($this->b === ' ') {
break;
}
// otherwise we treat the newline like a space
// no break
case ' ':
if (static::isAlphaNumeric($this->b)) {
echo $this->a;
}
$this->saveString();
break;
default:
switch ($this->b) {
case "\n":
if (strpos('}])+-"\'', $this->a) !== false) {
echo $this->a;
$this->saveString();
break;
} else {
if (static::isAlphaNumeric($this->a)) {
echo $this->a;
$this->saveString();
}
}
break;
case ' ':
if (!static::isAlphaNumeric($this->a)) {
break;
}
// no break
default:
// check for some regex that breaks stuff
if ($this->a === '/' && ($this->b === '\'' || $this->b === '"')) {
$this->saveRegex();
continue 3;
}
echo $this->a;
$this->saveString();
break;
}
}
// do reg check of doom
$this->b = $this->getReal();
if (($this->b == '/' && strpos('(,=:[!&|?', $this->a) !== false)) {
$this->saveRegex();
}
}
}
/**
* Resets attributes that do not need to be stored between requests so that
* the next request is ready to go. Another reason for this is to make sure
* the variables are cleared and are not taking up memory.
*/
protected function clean()
{
unset($this->input);
$this->index = 0;
$this->a = $this->b = '';
unset($this->c);
unset($this->options);
}
/**
* Returns the next string for processing based off of the current index.
*
* @return string
*/
protected function getChar()
{
// Check to see if we had anything in the look ahead buffer and use that.
if (isset($this->c)) {
$char = $this->c;
unset($this->c);
} else {
// Otherwise we start pulling from the input.
$char = substr($this->input, $this->index, 1);
// If the next character doesn't exist return false.
if (isset($char) && $char === false) {
return false;
}
// Otherwise increment the pointer and use this char.
$this->index++;
}
// Normalize all whitespace except for the newline character into a
// standard space.
if ($char !== "\n" && ord($char) < 32) {
return ' ';
}
return $char;
}
/**
* This function gets the next "real" character. It is essentially a wrapper
* around the getChar function that skips comments. This has significant
* performance benefits as the skipping is done using native functions (ie,
* c code) rather than in script php.
*
*
* @return string Next 'real' character to be processed.
* @throws \RuntimeException
*/
protected function getReal()
{
$startIndex = $this->index;
$char = $this->getChar();
// Check to see if we're potentially in a comment
if ($char !== '/') {
return $char;
}
$this->c = $this->getChar();
if ($this->c === '/') {
$this->processOneLineComments($startIndex);
return $this->getReal();
} elseif ($this->c === '*') {
$this->processMultiLineComments($startIndex);
return $this->getReal();
}
return $char;
}
/**
* Removed one line comments, with the exception of some very specific types of
* conditional comments.
*
* @param int $startIndex The index point where "getReal" function started
* @return void
*/
protected function processOneLineComments($startIndex)
{
$thirdCommentString = substr($this->input, $this->index, 1);
// kill rest of line
$this->getNext("\n");
unset($this->c);
if ($thirdCommentString == '@') {
$endPoint = $this->index - $startIndex;
$this->c = "\n" . substr($this->input, $startIndex, $endPoint);
}
}
/**
* Skips multiline comments where appropriate, and includes them where needed.
* Conditional comments and "license" style blocks are preserved.
*
* @param int $startIndex The index point where "getReal" function started
* @return void
* @throws \RuntimeException Unclosed comments will throw an error
*/
protected function processMultiLineComments($startIndex)
{
$this->getChar(); // current C
$thirdCommentString = $this->getChar();
// kill everything up to the next */ if it's there
if ($this->getNext('*/')) {
$this->getChar(); // get *
$this->getChar(); // get /
$char = $this->getChar(); // get next real character
// Now we reinsert conditional comments and YUI-style licensing comments
if (($this->options['flaggedComments'] && $thirdCommentString === '!')
|| ($thirdCommentString === '@')) {
// If conditional comments or flagged comments are not the first thing in the script
// we need to echo a and fill it with a space before moving on.
if ($startIndex > 0) {
echo $this->a;
$this->a = " ";
// If the comment started on a new line we let it stay on the new line
if ($this->input[($startIndex - 1)] === "\n") {
echo "\n";
}
}
$endPoint = ($this->index - 1) - $startIndex;
echo substr($this->input, $startIndex, $endPoint);
$this->c = $char;
return;
}
} else {
$char = false;
}
if ($char === false) {
throw new \RuntimeException('Unclosed multiline comment at position: ' . ($this->index - 2));
}
// if we're here c is part of the comment and therefore tossed
$this->c = $char;
}
/**
* Pushes the index ahead to the next instance of the supplied string. If it
* is found the first character of the string is returned and the index is set
* to it's position.
*
* @param string $string
* @return string|false Returns the first character of the string or false.
*/
protected function getNext($string)
{
// Find the next occurrence of "string" after the current position.
$pos = strpos($this->input, $string, $this->index);
// If it's not there return false.
if ($pos === false) {
return false;
}
// Adjust position of index to jump ahead to the asked for string
$this->index = $pos;
// Return the first character of that string.
return substr($this->input, $this->index, 1);
}
/**
* When a javascript string is detected this function crawls for the end of
* it and saves the whole string.
*
* @throws \RuntimeException Unclosed strings will throw an error
*/
protected function saveString()
{
$startpos = $this->index;
// saveString is always called after a gets cleared, so we push b into
// that spot.
$this->a = $this->b;
// If this isn't a string we don't need to do anything.
if (!in_array($this->a, $this->stringDelimiters)) {
return;
}
// String type is the quote used, " or '
$stringType = $this->a;
// Echo out that starting quote
echo $this->a;
// Loop until the string is done
// Grab the very next character and load it into a
while (($this->a = $this->getChar()) !== false) {
switch ($this->a) {
// If the string opener (single or double quote) is used
// output it and break out of the while loop-
// The string is finished!
case $stringType:
break 2;
// New lines in strings without line delimiters are bad- actual
// new lines will be represented by the string \n and not the actual
// character, so those will be treated just fine using the switch
// block below.
case "\n":
if ($stringType === '`') {
echo $this->a;
} else {
throw new \RuntimeException('Unclosed string at position: ' . $startpos);
}
break;
// Escaped characters get picked up here. If it's an escaped new line it's not really needed
case '\\':
// a is a slash. We want to keep it, and the next character,
// unless it's a new line. New lines as actual strings will be
// preserved, but escaped new lines should be reduced.
$this->b = $this->getChar();
// If b is a new line we discard a and b and restart the loop.
if ($this->b === "\n") {
break;
}
// echo out the escaped character and restart the loop.
echo $this->a . $this->b;
break;
// Since we're not dealing with any special cases we simply
// output the character and continue our loop.
default:
echo $this->a;
}
}
}
/**
* When a regular expression is detected this function crawls for the end of
* it and saves the whole regex.
*
* @throws \RuntimeException Unclosed regex will throw an error
*/
protected function saveRegex()
{
echo $this->a . $this->b;
while (($this->a = $this->getChar()) !== false) {
if ($this->a === '/') {
break;
}
if ($this->a === '\\') {
echo $this->a;
$this->a = $this->getChar();
}
if ($this->a === "\n") {
throw new \RuntimeException('Unclosed regex pattern at position: ' . $this->index);
}
echo $this->a;
}
$this->b = $this->getReal();
}
/**
* Checks to see if a character is alphanumeric.
*
* @param string $char Just one character
* @return bool
*/
protected static function isAlphaNumeric($char)
{
return preg_match('/^[\w\$\pL]$/', $char) === 1 || $char == '/';
}
/**
* Replace patterns in the given string and store the replacement
*
* @param string $js The string to lock
* @return bool
*/
protected function lock($js)
{
/* lock things like <code>"asd" + ++x;</code> */
$lock = '"LOCK---' . crc32(time()) . '"';
$matches = array();
preg_match('/([+-])(\s+)([+-])/S', $js, $matches);
if (empty($matches)) {
return $js;
}
$this->locks[$lock] = $matches[2];
$js = preg_replace('/([+-])\s+([+-])/S', "$1{$lock}$2", $js);
/* -- */
return $js;
}
/**
* Replace "locks" with the original characters
*
* @param string $js The string to unlock
* @return bool
*/
protected function unlock($js)
{
if (empty($this->locks)) {
return $js;
}
foreach ($this->locks as $lock => $replacement) {
$js = str_replace($lock, $replacement, $js);
}
return $js;
}
}