You are here

minifyjs.module in Minify JS 8

Same filename and directory in other branches
  1. 8.2 minifyjs.module
  2. 7 minifyjs.module

File

minifyjs.module
View source
<?php

use Drupal\Core\Link;
use Drupal\Core\Url;
use Drupal\Core\Site\Settings;
use Drupal\file\Entity\File;
use Patchwork\JSqueeze;
use Symfony\Component\HttpFoundation;
define('MINIFYJS_CACHE_CID', 'minifyjs:all_files');

/**
 * Implements hook_FORM_ID_alter().
 *
 * @param array $form
 * @param FormStateInterface $form_state
 * @param int $form_id
 */
function minifyjs_form_system_performance_settings_alter(&$form, $form_state, $form_id) {
  if (\Drupal::currentUser()
    ->hasPermission('administer minifyjs')) {

    // Determine the number of files minified.
    $files = minifyjs_load_all_files();
    $files_minified = 0;
    foreach ($files as $file) {
      if (!empty($file->minified_uri)) {
        $files_minified++;
      }
    }
    $form['bandwidth_optimization']['use_minified_javascript'] = [
      '#type' => 'checkbox',
      '#title' => t('Use Minified JavaScript files.'),
      '#disabled' => $files_minified ? FALSE : TRUE,
      '#description' => t('Toggle minified javascript files on or off. There are @minified minified files out of @total total files available. <a href=":href">Click here to manage your javascript files</a>.', [
        '@minified' => $files_minified,
        '@total' => count($files),
        ':href' => Url::fromUri('base:/admin/config/development/performance/js')
          ->toString(),
      ]),
      '#default_value' => \Drupal::config('minifyjs.config')
        ->get('use_minified'),
    ];
    $form['#submit'][] = 'minifyjs_system_performance_settings_submit';
  }
}

/**
 * Helper function to handle submit hook in form alter.
 *
 * @param array $form
 * @param FormStateInterface $form_state
 */
function minifyjs_system_performance_settings_submit(&$form, $form_state) {
  \Drupal::service('config.factory')
    ->getEditable('minifyjs.config')
    ->set('use_minified', $form_state
    ->getValue('use_minified_javascript'))
    ->save();
}

/**
 * Implements hook_js_alter().
 *
 * @param array $javascript
 * @param AttachedAssetsInterface $assets
 */
function minifyjs_js_alter(&$scripts, $assets) {

  // Determine if the replacement needs to be performed.
  $do_replace = FALSE;
  if (\Drupal::config('minifyjs.config')
    ->get('use_minified')) {
    if (\Drupal::config('minifyjs.config')
      ->get('disable_admin')) {
      if (!\Drupal::service('router.admin_context')
        ->isAdminRoute()) {
        $do_replace = TRUE;
      }
    }
    else {
      $do_replace = TRUE;
    }
  }
  if ($do_replace) {

    // Files array is keyed by file id. To make this operation as fast as
    // possible, the array should be keyed by file uri. Also, shorten the
    // array by removing files that are not minified.
    $files = minifyjs_load_all_files();
    $minified_files = [];
    foreach ($files as $file) {
      if (!empty($file->minified_uri)) {
        $minified_files[$file->uri] = $file;
      }
    }

    // Update the scripts array with new file info.
    foreach ($scripts as $path => $file) {
      if (isset($minified_files[$path])) {
        $scripts[$path]['data'] = $minified_files[$path]->minified_uri;
        minifyjs_update_scripts($scripts, $path, $minified_files[$path]->minified_uri);
      }
    }
  }
}

/**
 * Helper function to change array key, without changing its order.
 *
 * @param array $scripts
 * @param string $existing
 * @param string $new
 */
function minifyjs_update_scripts(&$scripts, $existing, $new) {
  $keys = array_keys($scripts);
  $index = array_search($existing, $keys, TRUE);
  if ($index !== FALSE) {
    $keys[$index] = $new;
    $scripts = array_combine($keys, $scripts);
  }
}

/**
 * Helper function - load all of the minifyjs_file records from cache or
 * directly from the database.
 *
 * @return array $files
 */
function minifyjs_load_all_files() {

  // load files from cache
  if ($cache = \Drupal::cache()
    ->get(MINIFYJS_CACHE_CID)) {
    return $cache->data;
  }

  // re-build cache
  $result = \Drupal::database()
    ->select('minifyjs_file', 'f')
    ->fields('f')
    ->orderBy('uri')
    ->execute();
  $exclusions = \Drupal::config('minifyjs.config')
    ->get('exclusion_list');
  $files = [];
  while ($file = $result
    ->fetchObject()) {

    // ignore the exclusions
    if (!\Drupal::service('path.matcher')
      ->matchPath($file->uri, $exclusions)) {
      $files[$file->fid] = $file;
    }
  }

  // cache for 1 day
  \Drupal::cache()
    ->set(MINIFYJS_CACHE_CID, $files, strtotime('+1 day', \Drupal::time()
    ->getRequestTime()));
  return $files;
}

/**
 * Helper function that sends the JS off to be minified, handles the response,
 * stores the file in the filesystem and stores the file info in the managed
 * file tables.
 *
 * @param int $fid
 * @param bool $reset
 *
 * @return mixed
 */
function minifyjs_minify_file($fid, $reset = FALSE) {

  // Load the file by fid.
  $files = minifyjs_load_all_files();
  $file = $files[$fid];
  $js = file_get_contents(DRUPAL_ROOT . '/' . $file->uri);

  // Minify the JS, if it has a length. 0 byte files should pass by the
  // minification process.
  $minified = $js;
  if (strlen($js)) {
    $minifier = new JSqueeze();
    $minified = $minifier
      ->squeeze($js, TRUE, FALSE);
  }

  // Create the directory tree if it doesn't exist.
  $minifyjs_folder = 'public://minifyjs/' . dirname($file->uri);
  if (!is_dir($minifyjs_folder)) {
    mkdir($minifyjs_folder, Settings::get('file_chmod_directory', 0775), TRUE);
  }

  // Save the file first to the temp folder and then copy to the
  // public filesystem.
  $file_name = str_replace('.js', '.min.js', basename($file->uri));
  $tmp_file = file_directory_temp() . '/' . $file_name;
  $file_uri = 'public://minifyjs/' . dirname($file->uri) . '/' . $file_name;
  if (file_put_contents($tmp_file, $minified) !== FALSE) {
    if (copy($tmp_file, $file_uri)) {

      // save the file in the managed file table.
      if (empty($file->minified_uri)) {
        $file = File::create([
          'uid' => \Drupal::currentUser()
            ->id(),
          'uri' => $file_uri,
          'filename' => $file_name,
          'filemime' => \Drupal::service('file.mime_type.guesser')
            ->guess($file->uri),
          'status' => FILE_STATUS_PERMANENT,
        ]);
        $file
          ->save();
        \Drupal::service('file.usage')
          ->add($file, 'minifyjs', 'node', 1);
      }
      $filesize = filesize($file_uri);

      // update the minifyjs table
      \Drupal::database()
        ->update('minifyjs_file')
        ->fields([
        'minified_uri' => $file_uri,
        'minified_size' => $filesize ? $filesize : 0,
        'minified_modified' => REQUEST_TIME,
      ])
        ->condition('fid', $fid)
        ->execute();

      // Clean up temp folder
      unlink($tmp_file);

      // Clear the cache so this change will be reflected in
      // minifyjs_load_all_files()
      if ($reset) {
        \Drupal::cache()
          ->delete(MINIFYJS_CACHE_CID);
      }
      return TRUE;
    }
    else {
      return t('Could not copy the file from the %tmp folder.', [
        '%tmp' => file_directory_temp(),
      ]);
    }
  }
  else {
    return t('Could not save the file - %file', [
      '%file' => file_directory_temp() . '/' . $file_name,
    ]);
  }
}

/**
 * Helper function removes the file, the entry in the file_managed table and
 * sets the file status as unminified.
 *
 * @param int $fid
 * @param bool $reset
 *
 * @return mixed
 */
function minifyjs_remove_minified_file($fid, $reset = FALSE) {

  // Get minified uri from the minifyjs_file table.
  $query = \Drupal::database()
    ->select('minifyjs_file', 'm')
    ->fields('m', array(
    'minified_uri',
  ))
    ->condition('m.fid', $fid);

  // make sure that it exists
  if ($query
    ->countQuery()
    ->execute()
    ->fetchField() > 0) {
    $file = $query
      ->execute()
      ->fetchObject();

    // Get the fid of the minified table.
    $query = db_select('file_managed', 'f')
      ->fields('f', array(
      'fid',
    ))
      ->condition('f.uri', $file->minified_uri);

    // make sure that it exists
    if ($query
      ->countQuery()
      ->execute()
      ->fetchField() > 0) {
      $file = $query
        ->execute()
        ->fetchObject();

      // Remove the file from the file_managed table
      $file = File::load($file->fid);
      $file
        ->delete();

      // Set the file status to non-minfied.
      \Drupal::database()
        ->update('minifyjs_file')
        ->fields([
        'minified_uri' => '',
        'minified_size' => 0,
        'minified_modified' => 0,
      ])
        ->condition('fid', $fid)
        ->execute();

      // Clear the cache so this change will be reflected in
      // minifyjs_load_all_files()
      if ($reset) {
        \Drupal::cache()
          ->delete(MINIFYJS_CACHE_CID);
      }
      return TRUE;
    }
  }
  return t('File not found. Check that the file ID is correct.');
}

/**
 * Helper function for batch, minify file operation.
 *
 * @param int $fid
 * @param array $context
 */
function minifyjs_batch_minify_file_operation($fid, &$context) {

  // Set the context message
  $files = minifyjs_load_all_files();
  $file = $files[$fid];
  $context['message'] = t('Minifying file - %file', [
    '%file' => $file->uri,
  ]);

  // Perform the minify
  $result = minifyjs_minify_file($fid);
  if ($result === TRUE) {
    $context['results']['success'][] = t('File %file was minified successfully.', [
      '%file' => $file->uri,
    ]);
  }
  else {
    $context['results']['errors'][] = $result;
  }
}

/**
 * Helper function for batch, remove minified file operation.
 *
 * @param int $fid
 * @param array $context
 */
function minifyjs_batch_remove_minified_file_operation($fid, &$context) {

  // Set the context message
  $files = minifyjs_load_all_files();
  $file = $files[$fid];
  $context['message'] = t('Restoring un-minified file - %file', [
    '%file' => $file->uri,
  ]);

  // Perform the restore
  $result = minifyjs_remove_minified_file($fid);
  if ($result === TRUE) {
    $context['results']['success'][] = t('File %file was restored successfully.', [
      '%file' => $file->uri,
    ]);
  }
  else {
    $context['results']['errors'][] = $result;
  }
}

/**
 * Helper function for batch, batch finished.
 *
 * @param bool $success
 * @param array $results
 * @param array $operations
 */
function minifyjs_batch_finished($success, $results, $operations) {

  // Output successful operations.
  if (isset($results['success'])) {
    if (count($results['success'])) {
      foreach ($results['success'] as $success) {
        drupal_set_message($success);
      }
    }
  }

  // Output errors.
  if (isset($results['errors'])) {
    if (count($results['errors'])) {
      foreach ($results['errors'] as $error) {
        drupal_set_message($error, 'error');
      }
    }
  }

  // Clear the cache so this change will be reflected in
  // minifyjs_load_all_files()
  \Drupal::cache()
    ->delete(MINIFYJS_CACHE_CID);
}

Functions

Namesort descending Description
minifyjs_batch_finished Helper function for batch, batch finished.
minifyjs_batch_minify_file_operation Helper function for batch, minify file operation.
minifyjs_batch_remove_minified_file_operation Helper function for batch, remove minified file operation.
minifyjs_form_system_performance_settings_alter Implements hook_FORM_ID_alter().
minifyjs_js_alter Implements hook_js_alter().
minifyjs_load_all_files Helper function - load all of the minifyjs_file records from cache or directly from the database.
minifyjs_minify_file Helper function that sends the JS off to be minified, handles the response, stores the file in the filesystem and stores the file info in the managed file tables.
minifyjs_remove_minified_file Helper function removes the file, the entry in the file_managed table and sets the file status as unminified.
minifyjs_system_performance_settings_submit Helper function to handle submit hook in form alter.
minifyjs_update_scripts Helper function to change array key, without changing its order.

Constants

Namesort descending Description
MINIFYJS_CACHE_CID