1
0
mirror of https://github.com/mrclay/minify.git synced 2025-02-23 16:33:02 +01:00
minify/lib/Minify/Javascript.php
2008-02-28 18:42:56 +00:00

297 lines
8.0 KiB
PHP

<?php
/**
* Minify_Javascript - PHP implementation of Douglas Crockford's JSMin.
*
* This is pretty much a direct port of JSMin.c to PHP with just a few
* PHP-specific performance tweaks. Also, whereas JSMin.c reads from stdin and
* outputs to stdout, this library accepts a string as input and returns another
* string as output.
*
* PHP 5 or higher is required.
*
* Permission is hereby granted to use this version of the library under the
* same terms as JSMin.c, which has the following license:
*
* --
* Copyright (c) 2002 Douglas Crockford (www.crockford.com)
*
* 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 shall be used for Good, not Evil.
*
* 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.
* --
*
* @package Minify_Javascript
* @author Ryan Grove <ryan@wonko.com>
* @copyright 2002 Douglas Crockford <douglas@crockford.com> (JSMin.c)
* @copyright 2007 Ryan Grove <ryan@wonko.com> (PHP port)
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 1.1.0 (2007-06-01)
* @link http://code.google.com/p/jsmin-php/
*/
class Minify_Javascript {
const ORD_LF = 10;
const ORD_SPACE = 32;
private $a = '';
private $b = '';
private $input = '';
private $inputIndex = 0;
private $inputLength = 0;
private $lookAhead = null;
private $output = array();
// -- Public Static Methods --------------------------------------------------
public static function minify($js, $options = array()) {
$js = new Minify_Javascript($js);
return trim($js->min());
}
// -- Private Instance Methods ---------------------------------------------
private function __construct($input) {
$this->input = str_replace("\r\n", "\n", $input);
$this->inputLength = strlen($this->input);
}
private function action($d) {
switch($d) {
case 1:
$this->output[] = $this->a;
case 2:
$this->a = $this->b;
if ($this->a === "'" || $this->a === '"') {
for (;;) {
$this->output[] = $this->a;
$this->a = $this->get();
if ($this->a === $this->b) {
break;
}
if (ord($this->a) <= self::ORD_LF) {
throw new Minify_JavascriptException('Unterminated string literal.');
}
if ($this->a === '\\') {
$this->output[] = $this->a;
$this->a = $this->get();
}
}
}
case 3:
$this->b = $this->next();
if ($this->b === '/' && (
$this->a === '(' || $this->a === ',' || $this->a === '=' ||
$this->a === ':' || $this->a === '[' || $this->a === '!' ||
$this->a === '&' || $this->a === '|' || $this->a === '?')) {
$this->output[] = $this->a;
$this->output[] = $this->b;
for (;;) {
$this->a = $this->get();
if ($this->a === '/') {
break;
}
elseif ($this->a === '\\') {
$this->output[] = $this->a;
$this->a = $this->get();
}
elseif (ord($this->a) <= self::ORD_LF) {
throw new Minify_JavascriptException('Unterminated regular expression '.
'literal.');
}
$this->output[] = $this->a;
}
$this->b = $this->next();
}
}
}
private function get() {
$c = $this->lookAhead;
$this->lookAhead = null;
if ($c === null) {
if ($this->inputIndex < $this->inputLength) {
$c = $this->input[$this->inputIndex];
$this->inputIndex += 1;
}
else {
$c = null;
}
}
if ($c === "\r") {
return "\n";
}
if ($c === null || $c === "\n" || ord($c) >= self::ORD_SPACE) {
return $c;
}
return ' ';
}
private function min() {
$this->a = "\n";
$this->action(3);
while ($this->a !== null) {
switch ($this->a) {
case ' ':
if (self::isAlphaNum($this->b)) {
$this->action(1);
}
else {
$this->action(2);
}
break;
case "\n":
switch ($this->b) {
case '{':
case '[':
case '(':
case '+':
case '-':
$this->action(1);
break;
case ' ':
$this->action(3);
break;
default:
if (self::isAlphaNum($this->b)) {
$this->action(1);
}
else {
$this->action(2);
}
}
break;
default:
switch ($this->b) {
case ' ':
if (self::isAlphaNum($this->a)) {
$this->action(1);
break;
}
$this->action(3);
break;
case "\n":
switch ($this->a) {
case '}':
case ']':
case ')':
case '+':
case '-':
case '"':
case "'":
$this->action(1);
break;
default:
if (self::isAlphaNum($this->a)) {
$this->action(1);
}
else {
$this->action(3);
}
}
break;
default:
$this->action(1);
break;
}
}
}
return implode('', $this->output);
}
private function next() {
$c = $this->get();
if ($c === '/') {
switch($this->peek()) {
case '/':
for (;;) {
$c = $this->get();
if (ord($c) <= self::ORD_LF) {
return $c;
}
}
case '*':
$this->get();
for (;;) {
switch($this->get()) {
case '*':
if ($this->peek() === '/') {
$this->get();
return ' ';
}
break;
case null:
throw new Minify_JavascriptException('Unterminated comment.');
}
}
default:
return $c;
}
}
return $c;
}
private function peek() {
$this->lookAhead = $this->get();
return $this->lookAhead;
}
// Private static functions --------------------------------------------------
private static function isAlphaNum($c) {
return ord($c) > 126 || $c === '\\' || preg_match('/^[\w\$]$/', $c) === 1;
}
}
// -- Exceptions ---------------------------------------------------------------
class Minify_JavascriptException extends Exception {}