Cómo minificar JS en PHP fácilmente... O algo más

He hecho un poco de búsqueda, pero todavía estoy un poco confundido.

Probé JSMin de Crockford, pero Win XP no puede descomprimir el archivo ejecutable por algún motivo.

Sin embargo, lo que realmente quiero es un minificador JS simple y fácil de usar que use PHP para minificar el código JS y devolver el resultado.

El motivo es porque: tengo 2 archivos (por ejemplo) entre los que estoy trabajando: scripts.js y scripts_template.js

scripts_template es un código normal que escribo, luego tengo que minimizarlo y pegar el script minimizado en scripts.js, el que realmente USO en mi sitio web.

Quiero erradicar al intermediario simplemente haciendo algo como esto en mi página:

<script type="text/javascript" src="scripts.php"></script>

Y luego para el contenido de scripts.php:

<?php include("include.inc"); header("Content-type:text/javascript"); echo(minify_js(file_get_contents("scripts_template.js")));

De esta manera, cada vez que actualizo mi JS, no tengo que ir constantemente a un sitio web para minimizarlo y volver a pegarlo en scripts.js; todo se actualiza automáticamente.

Sí, también probé PHP Minifier de Crockford y eché un vistazo a PHP Speedy, pero todavía no entiendo las clases de PHP... ¿Hay algo por ahí que un mono pueda entender, tal vez algo con RegExp?

¿Qué tal si hacemos esto aún más simple?

Solo quiero eliminar los espacios de tabulación; todavía quiero que mi código sea legible.

No es que el script haga que mi sitio se retrase épicamente, es simplemente que cualquier cosa es mejor que nada.

Eliminación de pestañas, ¿alguien? Y si es posible, ¿qué hay de eliminar completamente las líneas EN BLANCO?

preguntado el 12 de junio de 12 a las 16:06

una expresión regular no va a minimizar el código JS en el corto plazo. -

Si su código es lo suficientemente corto, puede intentar UglifyJS.php, pero se vuelve muy lento para códigos largos. -

5 Respuestas

He usado un Implementación PHP de JSMin por douglas crockford por algun tiempo. Puede ser un poco arriesgado al concatenar archivos, ya que puede faltar un punto y coma al final de un cierre.

Sería una buena idea almacenar en caché la salida minimizada y hacer eco de lo que se almacena en caché, siempre que sea más nuevo que el archivo de origen.

require 'jsmin.php';

if(filemtime('scripts_template.js') < filemtime('scripts_template.min.js')) {
  read_file('scripts_template.min.js');
} else {
  $output = JSMin::minify(file_get_contents('scripts_template.js'));
  file_put_contents('scripts_template.min.js', $output);
  echo $output;
}

También puedes probar JReducir. Nunca lo he usado antes, ya que no he tenido problemas con JSMin antes, pero este código a continuación debería funcionar. No me había dado cuenta de esto, pero JShrink requiere PHP 5.3 y espacios de nombres.

require 'JShrink/Minifier.php';

if(filemtime('scripts_template.js') < filemtime('scripts_template.min.js')) {
  read_file('scripts_template.min.js');
} else {
  $output = \JShrink\Minifier::minify(file_get_contents('scripts_template.js'));
  file_put_contents('scripts_template.min.js', $output);
  echo $output;
}

Respondido 03 ago 15, 14:08

Intenté usar esto, pero no minimiza todo el código. De hecho, CORTA el código a la mitad de la secuencia de comandos, por lo que estoy justo en medio de un bucle for(), lo que provocaría que la secuencia de comandos se corrompa de todos modos. - RickyAYoder

@RickyAYoder ¿Se generaron avisos o errores? Podría ser un error de sintaxis en tu Javascript. - Robert k

No. Cuando ejecuto el script hecho a mano y sin minimizar, no hay errores que reportar. - RickyAYoder

También tenga en cuenta que estos paquetes no suelen admitir captadores/establecedores a menos que así lo indiquen. Si tiene Node.js en su entorno, sugiero usar UglifyJS en lugar de. - Robert k

Probé ambos en uno de mis archivos fuente. jsmin: (46,385 => 26,031 bytes). JShrink: (463,85->26,027 bytes). Rendimiento muy similar. Sin embargo, javascript-minifier.com lo minimizó a 19,526 XNUMX bytes porque reemplaza los nombres largos de variables con versiones más cortas. - EricP

Eche un vistazo a Assetic, una gran biblioteca de gestión de activos en PHP. Está bien integrado con Symfony2 y es ampliamente utilizado.

https://github.com/kriswallsmith/assetic

Respondido el 12 de junio de 12 a las 19:06

Dependiendo de las restricciones de su servidor (por ejemplo, no ejecutar en modo seguro), tal vez también pueda buscar un minificador más allá de PHP y ejecutarlo usando shell_exec(). Por ejemplo, si puede ejecutar Java en su servidor, coloque una copia de Compresor YUI en el servidor y usarlo directamente.

Entonces scripts.php sería algo como:

<?php 

  $cmd = "java -cp [path-to-yui-dir] -jar [path-to-yuicompressor.jar] [path-to-scripts_template.js]";

  echo(shell_exec($cmd));

?>

Otra sugerencia: incorpore el paso de minificación en su flujo de trabajo de desarrollo, antes de implementarlo en el servidor. Por ejemplo, configuré mis proyectos Eclipse PHP para comprimir archivos JS y CSS en una carpeta de "compilación". Funciona de maravilla.

Respondido el 12 de junio de 12 a las 21:06

Comenzar una aplicación JAVA para una pequeña utilidad me parece una gran inflacion. Sería imposible encajar esta solución en un flujo de solicitud-respuesta como quería el OP. - karatedog

Lo que dices no se aplica exclusivamente a una aplicación Java. Re-minificar en cada solicitud sería un gasto innecesario con cualquier utilidad. Nota El consejo anterior de Robert-K para almacenar en caché el resultado, o mi "otra sugerencia", para moverlo a un paso de compilación automatizado. Ahora, tres años después, hay mejores opciones para minimizar que YUI de todos modos. - kevin bray

Utilizando "PHPWee": https://github.com/searchturbine/phpwee-php-minifier (que también usa JSmin), empujé la solución @Robert K un poco más lejos.

Esta solución permite minimizar los archivos CSS y JS. Si no se puede encontrar el archivo no minimizado, devolverá una cadena vacía. Si el archivo minimizado es más antiguo que el no minimizado, intentará crearlo. Creará una subcarpeta para el archivo minimizado si no existe. Si el método puede minimizar el archivo con éxito, lo devuelve en un <script> (javascript) o un <link> (CSS) etiqueta. De lo contrario, el método devolverá la versión no minimizada en la etiqueta adecuada.

Nota:: probado con PHP 7.0.13

/**
* Try to minify the JS/CSS file. If we are not able to minify,
*   returns the path of the full file (if it exists).
*
* @param $matches Array
*   0 = Full partial path
*   1 = Path without the file
*   2 = File name and extension
*
* @param $fileType Boolean
*   FALSE: css file.
*   TRUE: js file
*
* @return String
*/
private static function createMinifiedFile(array $matches, bool $fileType)
{
    if (strpos($matches[1], 'shared_code') !== false) {

        $path = realpath(dirname(__FILE__)) . str_replace(
            'shared_code',
            '..',
            $matches[1]
        );

    } else {

        $path = realpath(dirname(__FILE__)) .
            "/../../" . $matches[1];
    }

    if (is_file($path . $matches[2])) {

        $filePath = $link = $matches[0];

        $min = 'min/' . str_replace(
            '.',
            '.min.',
            $matches[2]
        );

        if (!is_file($path . $min) or 
            filemtime($path . $matches[2]) > 
            filemtime($path . $min)
        ) {

            if (!is_dir($path . 'min')) {

                mkdir($path . 'min');   
            }

            if ($fileType) { // JS

                $minified = preg_replace(
                        array(
                            '/(\))\R({)/',
                            '/(})\R/'
                        ),
                        array(
                            '$1$2',
                            '$1'
                        ),
                        Minify::js(
                        (string) file_get_contents(
                            $path . $matches[2]
                        )
                    )
                );

            } else { // CSS

                $minified = preg_replace(
                    '@/\*(?:[\r\s\S](?!\*/))+\R?\*/@', //deal with multiline comments
                    '',
                    Minify::css(
                        (string) file_get_contents(
                            $path . $matches[2]
                        )
                    )
                );
            }

            if (!empty($minified) and file_put_contents(
                    $path . $min, 
                    $minified 
                )
            ) {

                $filePath = $matches[1] . $min;
            }

        } else { // up-to-date

            $filePath = $matches[1] . $min;
        }

    } else { // full file doesn't exists

        $filePath = "";
    }

    return $filePath;
}

/**
* Return the minified version of a CSS file (must end with the .css extension).
*   If the minified version of the file is older than the full CSS file,
*   the CSS file will be shrunk.
*
*   Note: An empty string will be return if the CSS file doesn't exist.
*
*   Note 2: If the file exists, but the minified file cannot be created, 
*       we will return the path of the full file.
*
* @link https://github.com/searchturbine/phpwee-php-minifier Source
*
* @param $path String name or full path to reach the CSS file.
*   If only the file name is specified, we assume that you refer to the shared path.
*
* @return String
*/
public static function getCSSMin(String $path)
{
    $link = "";
    $matches = array();

    if (preg_match(
            '@^(/[\w-]+/view/css/)?([\w-]+\.css)$@',
            $path,
            $matches
        )
    ) {

        if (empty($matches[1])) { // use the default path

            $matches[1] = self::getCssPath();

            $matches[0] = $matches[1] . $matches[2];
        }

        $link = self::createMinifiedFile($matches, false);

    } else {

        $link = "";
    }

    return (empty($link) ?
        '' :
        '<link rel="stylesheet" href="' . $link . '">'
    );
}

/**
* Return the path to fetch CSS sheets.
* 
* @return String
*/
public static function getCssPath()
{
    return '/shared_code/css/' . self::getCurrentCSS() . "/";
}

/**
* Return the minified version of a JS file (must end with the .css extension).
*   If the minified version of the file is older than the full JS file,
*   the JS file will be shrunk.
*
*   Note: An empty string will be return if the JS file doesn't exist.
*
*   Note 2: If the file exists, but the minified file cannot be created, 
*       we will return the path of the full file.
*
* @link https://github.com/searchturbine/phpwee-php-minifier Source
*
* @param $path String name or full path to reach the js file.
*
* @return String
*/
public static function getJSMin(String $path)
{
    $matches = array();

    if (preg_match(
            '@^(/[\w-]+(?:/view)?/js/)([\w-]+\.js)$@',
            $path,
            $matches
        )
    ) {
        $script = self::createMinifiedFile($matches, true);

    } else {

        $script = "";
    }

    return (empty($script) ? 
        '' :
        '<script src="' . $script . '"></script>'
    );
}

En una plantilla (Smarty), puede usar esos métodos como este:

{$PageController->getCSSMin("main_frame.css")}
//Output: <link rel="stylesheet" href="/es/shared_code/css/default/min/main_frame.min.css">

{$PageController->getCSSMin("/gem-mechanic/view/css/gem_mechanic.css")}
//Output: <link rel="stylesheet" href="/es/gem-mechanic/view/css/min/gem_mechanic.min.css">

{$PageController->getJSMin("/shared_code/js/control_utilities.js")}
//Output: <script src="/shared_code/js/min/control_utilities.min.js"></script>

{$PageController->getJSMin("/PC_administration_interface/view/js/error_log.js")}
//Output: <script src="/PC_administration_interface/view/js/min/error_log.min.js"></script>

Pruebas unitarias:

/**
* Test that we can minify CSS files successfully.
*/
public function testGetCSSMin()
{
    //invalid style
    $this->assertEmpty(
        PageController::getCSSMin('doh!!!')
    );


    //shared style
    $path = realpath(dirname(__FILE__)) . '/../css/default/min/main_frame.min.css';

    if (is_file($path)) {

        unlink ($path);
    }

    $link = PageController::getCSSMin("main_frame.css");

    $this->assertNotEmpty($link);

    $this->assertEquals(
        '<link rel="stylesheet" href="/es/shared_code/css/default/min/main_frame.min.css">',
        $link
    );

    $this->validateMinifiedFile($path);


    //project style
    $path = realpath(dirname(__FILE__)) . '/../../gem-mechanic/view/css/min/gem_mechanic.min.css';

    if (is_file($path)) {

        unlink ($path);
    }

    $link = PageController::getCSSMin("/gem-mechanic/view/css/gem_mechanic.css");

    $this->assertNotEmpty($link);

    $this->assertEquals(
        '<link rel="stylesheet" href="/es/gem-mechanic/view/css/min/gem_mechanic.min.css">',
        $link
    );

    $this->validateMinifiedFile($path);
}

/**
* Test that we can minify JS files successfully.
*/
public function testGetJSMin()
{
    //invalid script
    $this->assertEmpty(
        PageController::getJSMin('doh!!!')
    );


    //shared script
    $path = realpath(dirname(__FILE__)) . '/../js/min/control_utilities.min.js';

    if (is_file($path)) {

        unlink ($path);
    }

    $script = PageController::getJSMin("/shared_code/js/control_utilities.js");

    $this->assertNotEmpty($script);

    $this->assertEquals(
        '<script src="/shared_code/js/min/control_utilities.min.js"></script>',
        $script
    );

    $this->validateMinifiedFile($path);


    //project script
    $path = realpath(dirname(__FILE__)) . '/../../PC_administration_interface/view/js/min/error_log.min.js';

    if (is_file($path)) {

        unlink ($path);
    }

    $script = PageController::getJSMin("/PC_administration_interface/view/js/error_log.js");

    $this->assertNotEmpty($script);

    $this->assertEquals(
        '<script src="/PC_administration_interface/view/js/min/error_log.min.js"></script>',
        $script
    );

    $this->validateMinifiedFile($path);
}

/**
* Make sure that the minified file exists and that its content is valid.
*
* @param $path String the path to reach the file
*/
private function validateMinifiedFile(string $path)
{
    $this->assertFileExists($path);

    $content = (string) file_get_contents($path);

    $this->assertNotEmpty($content);

    $this->assertNotContains('/*', $content);

    $this->assertEquals(
        0,
        preg_match(
            '/\R/',
            $content
        )
    );
}

Notas adicionales:

  1. In phpwee.php Tuve que reemplazar <? by <?php.
  2. Tuve problemas con el espacio de nombres (la función class_exists() no fue capaz de encontrar las clases a pesar de que estaban en el mismo archivo). Resolví este problema eliminando el espacio de nombres en cada archivo.

respondido 03 mar '17, 21:03

Empaquetador de JavaScript funciona desde 2008, y es bastante simple

Respondido 07 Feb 20, 16:02

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.