web-dev-qa-db-fra.com

Comment minifier JS dans PHP facilement ... ou autre chose

J'ai fait quelques recherches, mais je suis toujours un peu confus.

J'ai essayé le JSMin de Crockford, mais Win XP ne peut pas décompresser le fichier exécutable pour une raison quelconque.

Ce que je veux vraiment, c’est un minificateur JS simple et facile à utiliser qui utilise PHP pour minifier le code JS - et renvoie le résultat.

La raison en est que: J'ai 2 fichiers (par exemple) sur lesquels je travaille: scripts.js et scripts_template.js

scripts_template est un code normal que j'écris - je dois ensuite le minifier et coller le script minifié dans scripts.js - celui que j'utilise réellement sur mon site Web.

Je veux éliminer l'homme du milieu en faisant simplement quelque chose comme ceci sur ma page:

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

Et puis pour le contenu de scripts.php:

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

Ainsi, chaque fois que je mets à jour mon JS, je n'ai pas à aller constamment sur un site Web pour le réduire et le coller à nouveau dans scripts.js - tout est mis à jour automatiquement.

Oui, j'ai aussi essayé le modificateur PHP de Crockford et j'ai jeté un coup d'œil à PHP Speedy, mais je ne comprends pas encore les classes PHP. Y at-il quelque chose là-bas qu'un singe pourrait comprendre, peut-être quelque chose avec RegExp?

Pourquoi ne pas simplifier davantage les choses?

Je veux juste supprimer les espaces de tabulation - je veux toujours que mon code soit lisible. 

Ce n’est pas comme si le script faisait que mon site se traînait de manière épique, c’est juste que tout vaut mieux que rien.

Enlèvement des onglets Et si possible, que diriez-vous de supprimer complètement les lignes blanches?

12
RickyAYoder

J'ai utilisé une PHP implémentation de JSMin par Douglas Crockford depuis un certain temps. Cela peut être un peu risqué lors de la concaténation de fichiers, car il peut manquer un point-virgule à la fin d'une fermeture.

Ce serait une bonne idée de mettre en cache la sortie réduite et de faire écho à ce qui est mis en cache tant qu'il est plus récent que le fichier source.

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;
}

Vous pouvez aussi essayer JShrink . Je ne l'avais jamais utilisé auparavant, car je n'avais jamais eu de problèmes avec JSMin auparavant, mais le code ci-dessous devrait faire l'affaire. Je n'avais pas compris cela, mais JShrink nécessite PHP 5.3 et des espaces de noms.

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;
}
22
Robert K

Jetez un coup d’œil à Assetic, une excellente bibliothèque de gestion d’actifs en PHP. Il est bien intégré à Symfony2 et largement utilisé.

https://github.com/kriswallsmith/assetic

En fonction des restrictions de votre serveur (par exemple, ne pas utiliser le mode safe ), vous pouvez également rechercher un minifier au-delà de PHP et l'exécuter à l'aide de Shell_exec(). Par exemple, si vous pouvez exécuter Java sur votre serveur, placez une copie de YUI Compressor sur le serveur et utilisez-la directement.

Puis scripts.php serait quelque chose comme:

<?php 

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

  echo(Shell_exec($cmd));

?>

Autre suggestion: intégrez l’étape de la minification dans votre flux de travail de développement, avant de procéder au déploiement sur le serveur. Par exemple, j'ai configuré mes projets Eclipse PHP pour compresser les fichiers JS et CSS dans un dossier "build". Fonctionne comme un charme.

2
Kevin Bray

En utilisant "PHPWee": https://github.com/searchturbine/phpwee-php-minifier (Qui utilise également JSmin), j'ai un peu poussé la solution @Robert K Un peu plus loin. 

Cette solution permet de minifier les fichiers CSS et JS. Si le fichier non minifié est introuvable, il retournera une chaîne vide. Si le fichier minifié est plus ancien que le fichier non minifié, il essaiera de le créer. Il créera un sous-dossier pour le fichier minifié s'il n'existe pas. Si la méthode réussit à réduire le fichier, elle le renvoie dans une balise <script> (javascript) ou <link> (CSS). Sinon, la méthode renverra la version non minifiée dans la balise appropriée.

Note : testé avec 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>'
    );
}

Dans un modèle (Smarty), vous pouvez utiliser les méthodes suivantes:

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

{$PageController->getCSSMin("/gem-mechanic/view/css/gem_mechanic.css")}
//Output: <link rel="stylesheet" href="/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>

Tests unitaires:

/**
* 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="/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="/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
        )
    );
}

Notes complémentaires :

  1. Dans phpwee.php je devais remplacer <? par <?php.
  2. J'ai eu des problèmes avec l'espace de noms (la fonction class_exists() n'a pas été en mesure de trouver les classes même si elles se trouvaient dans le même fichier). J'ai résolu ce problème en supprimant l'espace de nom dans chaque fichier.