Файл jsmin.php в Cotonti CMF

Файл jsmin.php в Cotonti CMF реализует PHP-версию библиотеки для минификации JavaScript-кода, которая является портом известного инструмента jsmin.c от Douglas Crockford, адаптированного для PHP. Этот файл служит для сжатия и оптимизации JavaScript-кода, удаляя ненужные пробелы, комментарии и другие избыточности, улучшая таким образом производительность веб-приложений, снижая время загрузки и ускоряя выполнение JavaScript в браузере.

Основное назначение:

Цель данного файла — минимизация (минификация) JavaScript-кода. Это процесс удаления лишних пробелов, комментариев, строк, не влияющих на работу программы, с целью уменьшения объема кода и ускорения его загрузки и выполнения в браузере. Этот процесс играет важную роль в веб-разработке, особенно для крупных проектов с большим количеством JavaScript-кода.

Место в системе Cotonti:

Файл находится в каталоге /lib/, что означает, что он представляет собой вспомогательную библиотеку, которая используется для минификации файлов JavaScript перед их отправкой в браузер клиента. Он может быть использован в рамках обработки и оптимизации файлов на серверной стороне перед их выводом пользователю или сохранением для дальнейшего использования.

Методы, функции и классы:

  1. Класс JSMin: Класс JSMin является основным элементом, который инкапсулирует логику минификации. Он содержит все необходимые функции для обработки и сжатия JavaScript-кода. Основной метод — это min(), который выполняет процесс минификации, принимая исходный JavaScript-код в виде строки и возвращая его оптимизированную версию.

  2. Метод minify(): Это публичный статический метод, который инициализирует объект класса JSMin с переданным JavaScript-кодом и вызывает метод min() для выполнения минификации. Результат возвращается как сжатая версия исходного кода.

  3. Методы работы с символами и строками:

    • get(): Извлекает следующий символ из строки, обрабатывая специальные случаи, такие как замена символа возврата каретки на новую строку и игнорирование символов, которые не важны для минификации.
    • next(): Обрабатывает комментарии (как однострочные, так и многострочные), и, если встречается комментарий, игнорирует его, возвращая следующий значимый символ.
    • peek(): Используется для просмотра следующего символа без его извлечения, что полезно для обработки комментариев и регулярных выражений.
    • action(): Основной метод, который выполняет разные действия в зависимости от текущего состояния и символов в потоке. В нем реализована логика обработки пробелов, новых строк, строковых литералов и регулярных выражений.
    • isAlphaNum(): Проверяет, является ли символ буквой, цифрой, подчеркиванием или символом доллара. Это необходимо для правильной обработки идентификаторов и переменных в JavaScript.
  4. Обработка ошибок: Для обработки ошибок используется класс JSMinException, который наследует стандартный класс исключений PHP. Это позволяет генерировать и ловить исключения в случае возникновения ошибок, таких как незавершенные строковые литералы или регулярные выражения.

Суть содержания кода:

Код включает несколько ключевых этапов для выполнения минификации:

  1. Чтение и обработка символов: Код обрабатывает входной JavaScript, анализируя его символы по одному, игнорируя лишние пробелы, символы новой строки и комментарии.
  2. Обработка строк и регулярных выражений: Строковые литералы и регулярные выражения обрабатываются отдельно, чтобы избежать их случайного удаления в процессе минификации.
  3. Удаление ненужных пробелов и комментариев: Пробелы, табуляции и комментарии, которые не влияют на выполнение кода, удаляются для уменьшения размера файла.
  4. Оптимизация кода: Строки, разделяющие элементы JavaScript (такие как запятые и точки с запятой), приводятся к единому виду без лишних пробелов, что уменьшает общий размер кода.

Задачи и условия, которые выполняет код:

  • Минификация JavaScript-кода: Главная задача файла — обработать JavaScript и вернуть его в минимизированном виде, что снижает объем передаваемых данных.
  • Оптимизация загрузки страниц: Сокращение размера скриптов улучшает скорость загрузки страниц, что является важным аспектом для повышения производительности веб-приложений.
  • Обработка различных синтаксических конструкций JavaScript: Код корректно обрабатывает строки, регулярные выражения и комментарии, чтобы они не влияли на минификацию. Это гарантирует, что функциональность кода сохраняется при его сжатии.

Заключение:

Файл jsmin.php является важным инструментом для разработки в Cotonti, предоставляя возможности для автоматической минификации JavaScript-кода. Это улучшает производительность веб-сайтов или приложений, снижая время загрузки и оптимизируя работу JavaScript на клиентской стороне.

 

/**
 * jsmin.php - PHP реализация JSMin от Douglas Crockford.
 *
 * Это практически прямой порт jsmin.c на PHP с небольшими
 * оптимизациями для PHP. В отличие от jsmin.c, который читает из stdin и
 * выводит в stdout, эта библиотека принимает строку в качестве входных данных
 * и возвращает другую строку в качестве выходных данных.
 *
 * Требуется PHP 5 или выше.
 *
 * Разрешается использовать эту версию библиотеки на тех же условиях, что и jsmin.c,
 * который имеет следующую лицензию:
 *
 *
 * @package JSMin  // Указывает, что этот код относится к пакету JSMin.
 * @author Ryan Grove <[email protected]>  // Автор порта на PHP.
 * @copyright 2002 Douglas Crockford <[email protected]> (jsmin.c) // Оригинальный автор jsmin.c.
 * @copyright 2008 Ryan Grove <[email protected]> (PHP port) // Автор PHP-версии.
 * @license http://opensource.org/licenses/mit-license.php MIT License // Указывает на лицензию MIT.
 * @version 1.1.1 (2008-03-02) // Версия и дата последнего обновления.
 * @link https://github.com/rgrove/jsmin-php/ // Ссылка на репозиторий.
 */

class JSMin { // Объявление класса JSMin, который выполняет минификацию JavaScript-кода.
  const ORD_LF    = 10;   // Константа, обозначающая ASCII-код символа перевода строки (LF).
  const ORD_SPACE = 32;   // Константа, обозначающая ASCII-код пробела.

 

  protected $a           = '';  // Текущий символ, обрабатываемый в коде.
  protected $b           = '';  // Следующий значимый символ в коде.
  protected $input       = '';  // Исходный JavaScript-код, переданный для минификации.
  protected $inputIndex  = 0;   // Текущая позиция (индекс) в строке кода.
  protected $inputLength = 0;   // Общая длина переданного JavaScript-кода.
  protected $lookAhead   = null; // Предварительно загруженный символ (если есть).
  protected $output      = '';  // Минифицированный (сжатый) JavaScript-код.

  // -- Общедоступные статические методы ---------------------------------------

  public static function minify($js) { // Статический метод для упрощенного вызова минификации.
    $jsmin = new JSMin($js); // Создаёт новый экземпляр JSMin с переданным JavaScript-кодом.
    return $jsmin->min(); // Вызывает метод min(), который выполняет минификацию и возвращает результат.
  }

  // -- Общедоступные методы экземпляра ----------------------------------------

  public function __construct($input) { // Конструктор класса, принимает строку JavaScript-кода.
    $this->input       = str_replace("\r\n", "\n", $input); // Преобразует символы переноса строк из формата Windows в Unix.
    $this->inputLength = strlen($this->input); // Определяет и сохраняет длину входного JavaScript-кода.
  }

 

  // -- Защищённые методы экземпляра --------------------------------------------

  /* action — выполняет одно из трёх действий в зависимости от аргумента:
          1   Выводит `a`. Копирует `b` в `a`. Получает следующий `b`.
          2   Копирует `b` в `a`. Получает следующий `b` (удаляет `a`).
          3   Получает следующий `b` (удаляет `b`).
     action воспринимает строку как одиночный символ.
     action распознаёт регулярное выражение, если перед ним стоит ( или , или =.
  */
  protected function action($d) { // Метод выполняет одно из трёх действий в зависимости от переданного параметра.
    switch($d) { // Определяет, какой случай обработки выполнять.
      case 1: // Если $d равно 1.
        $this->output .= $this->a; // Добавляет текущий символ `a` в выходной код.

      case 2: // Если $d равно 2.
        $this->a = $this->b; // Переносит `b` в `a`.

        if ($this->a === "'" || $this->a === '"') { // Если `a` является кавычкой (начало строкового литерала).
          for (;;) { // Бесконечный цикл обработки строки.
            $this->output .= $this->a; // Добавляет `a` в выходной код.
            $this->a       = $this->get(); // Получает следующий символ.

            if ($this->a === $this->b) { // Если встретилась закрывающая кавычка, выходит из цикла.
              break; // Прерывает цикл.
            }

            if (ord($this->a) <= self::ORD_LF) { // Проверяет, не является ли символ управляющим символом новой строки.
              throw new JSMinException('Незавершённый строковый литерал.'); // Вызывает исключение, если строка не завершена.
            }

            if ($this->a === '\\') { // Если встретился символ экранирования.
              $this->output .= $this->a; // Добавляет `\` в выходной код.
              $this->a       = $this->get(); // Получает следующий символ после `\`.
            }
          }
        }

      case 3: // Если $d равно 3.
        $this->b = $this->next(); // Получает следующий символ в `b`.

        if ($this->b === '/' && ( // Проверяет, начинается ли `b` с `/` и является ли это началом регулярного выражения.
            $this->a === '(' || $this->a === ',' || $this->a === '=' ||
            $this->a === ':' || $this->a === '[' || $this->a === '!' ||
            $this->a === '&' || $this->a === '|' || $this->a === '?' ||
            $this->a === '{' || $this->a === '}' || $this->a === ';' ||
            $this->a === "\n" )) { // Если перед `/` стоит один из перечисленных символов, значит, это регулярное выражение.

          $this->output .= $this->a . $this->b; // Добавляет `a` и `b` (`/`) в выходной код.

          for (;;) { // Бесконечный цикл обработки регулярного выражения.
            $this->a = $this->get(); // Получает следующий символ.

            if ($this->a === '[') { // Если встретилась открывающая `[` внутри регулярного выражения.
              for (;;) { // Цикл обработки набора символов внутри `[...]`.
                $this->output .= $this->a; // Добавляет `a` в выходной код.
                $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 JSMinException('Незавершённый набор символов в регулярном выражении.'); // Вызывает исключение.
                }
              }
            } elseif ($this->a === '/') { // Если встретилась закрывающая `/`, завершает регулярное выражение.
              break; // Прерывает цикл.
            } elseif ($this->a === '\\') { // Если встретился `\`, обрабатывает экранированный символ.
              $this->output .= $this->a; // Добавляет `\` в выходной код.
              $this->a       = $this->get(); // Получает следующий символ после `\`.
            } elseif (ord($this->a) <= self::ORD_LF) { // Если достигнут конец строки без закрытия `/`.
              throw new JSMinException('Незавершённое регулярное выражение.'); // Вызывает исключение.
            }

            $this->output .= $this->a; // Добавляет текущий символ `a` в выходной код.
          }

          $this->b = $this->next(); // Получает следующий символ после завершения обработки регулярного выражения.
        }
    }
  }

 

  protected function get() { // Метод для получения следующего символа из входных данных.
    $c = $this->lookAhead; // Сохраняет значение предварительного просмотра символа в переменную `$c`.
    $this->lookAhead = null; // Обнуляет предварительный просмотр символа.

    if ($c === null) { // Если нет символа для предварительного просмотра (переменная `$c` пуста),
      if ($this->inputIndex < $this->inputLength) { // и если индекс ещё не достиг конца строки,
        $c = substr($this->input, $this->inputIndex, 1); // получает следующий символ из строки.
        $this->inputIndex += 1; // Увеличивает индекс, чтобы двигаться к следующему символу.
      } else { // Если достигнут конец строки,
        $c = null; // устанавливает `$c` в `null`, что означает конец ввода.
      }
    }

    if ($c === "\r") { // Если символ — это возврат каретки (Carriage Return),
      return "\n"; // возвращает символ новой строки (перевод строки), заменяя `\r` на `\n`.
    }

    if ($c === null || $c === "\n" || ord($c) >= self::ORD_SPACE) { // Если символ — это конец строки, `null`, или пробельный символ,
      return $c; // возвращает его без изменений.
    }

    return ' '; // В любом другом случае заменяет символ на пробел.
  }

 

  /* isAlphanum -- возвращает true, если символ является буквой, цифрой, подчеркиванием,
        знаком доллара или не-ASCII символом.
  */
  protected function isAlphaNum($c) { // Метод проверяет, является ли символ алфавитным или числовым.
    if ($c === null || $c === false) { // Если символ равен null или false,
        return false; // возвращает false.
    }
    return ord($c) > 126 || $c === '\\' || preg_match('/^[\w\$]$/', $c) === 1; // Если код символа больше 126 (не-ASCII символ), либо символ — это обратный слэш, либо символ является буквенно-цифровым или знаком доллара (проверяется с помощью регулярного выражения), возвращает true. В противном случае — false.
  }

 

  protected function min() { // Метод min() — основной процесс минификации.
    $this->a = "\n"; // Инициализация переменной $a значением новой строки, начинается с символа новой строки.
    $this->action(3); // Вызов метода action с аргументом 3, для выполнения действия с первым символом (инициализация).

    while ($this->a !== null) { // Цикл продолжается до тех пор, пока переменная $a не станет null.
      switch ($this->a) { // Проверка значения переменной $a, в зависимости от которого выполняется соответствующее действие.
        case ' ': // Если $a является пробелом,
          if ($this->isAlphaNum($this->b)) { // Если переменная $b является буквенно-цифровым символом,
            $this->action(1); // Выполнить действие 1, что значит добавить символ $a в вывод и передвинуть указатель.
          } else {
            $this->action(2); // В противном случае выполнить действие 2, что значит пропустить пробел.
          }
          break; // Выход из блока case пробела.

        case "\n": // Если $a является новой строкой,
          switch ($this->b) { // Проверка значения переменной $b, чтобы определить, как поступить с новой строкой.
            case '{': // Если $b — символ открывающей фигурной скобки,
            case '[': // или открывающей квадратной скобки,
            case '(': // или открывающей круглой скобки,
            case '+': // или знака плюса,
            case '-': // или знака минуса,
              $this->action(1); // Выполняем действие 1, то есть сохраняем $a и продолжаем обработку, добавляем в вывод.
              break; // Выход из блока case новой строки.

            case ' ': // Если $b — пробел,
              $this->action(3); // Выполняем действие 3, что означает просто пропуск пробела.
              break;

            default: // Если ни одно из предыдущих условий не выполнено,
              if ($this->isAlphaNum($this->b)) { // Если $b является буквенно-цифровым символом,
                $this->action(1); // Выполняем действие 1, добавляем $a в вывод.
              }
              else {
                $this->action(2); // В противном случае выполняем действие 2, пропускаем символ.
              }
          }
          break;

        default: // Если $a не является пробелом или новой строкой,
          switch ($this->b) { // Проверяем значение $b.
            case ' ': // Если $b — пробел,
              if ($this->isAlphaNum($this->a)) { // Если $a является буквенно-цифровым символом,
                $this->action(1); // Выполняем действие 1, добавляем символ $a в вывод.
                break;
              }

              $this->action(3); // Если $a не буквенно-цифровой символ, выполняем действие 3, пропускаем пробел.
              break;

            case "\n": // Если $b — новая строка,
              switch ($this->a) { // Проверяем значение $a.
                case '}': // Если $a — закрывающая фигурная скобка,
                case ']': // или закрывающая квадратная скобка,
                case ')': // или закрывающая круглая скобка,
                case '+': // или знак плюса,
                case '-': // или знак минуса,
                case '"': // или кавычка,
                case "'": // или апостроф,
                  $this->action(1); // Выполняем действие 1, добавляем символ $a в вывод.
                  break;

                default: // Если $a не соответствует ни одному из перечисленных случаев,
                  if ($this->isAlphaNum($this->a)) { // Если $a — буквенно-цифровой символ,
                    $this->action(1); // Выполняем действие 1, добавляем $a в вывод.
                  }
                  else {
                    $this->action(3); // В противном случае выполняем действие 3, пропускаем символ.
                  }
              }
              break;

            default: // Если $b не является пробелом или новой строкой,
              $this->action(1); // Выполняем действие 1, добавляем символ в вывод.
              break;
          }
      }
    }

    return $this->output; // Возвращаем результат минификации в виде строки.
  }

Объяснение:

  1. Основная цель метода min(): Минификация входного JavaScript-кода, удаляя лишние пробелы, переносы строк и ненужные символы. Это выполняется через несколько уровней вложенных условий, которые проверяют символы и в зависимости от контекста (например, пробел или новая строка) выполняется соответствующее действие.

  2. Цикл и проверка символов: Цикл while продолжается до тех пор, пока не обрабатываются все символы во входной строке, причем каждая итерация проверяет текущий символ и определяет, нужно ли его добавить в вывод, пропустить или выполнить другое действие.

  3. Использование метода action(): Этот метод обрабатывает различные действия в зависимости от текущего контекста (например, добавление символа в результат или его пропуск).

  4. Действия с символами: В коде рассматриваются различные случаи для пробела, новой строки и других символов. В каждом случае определяется, нужно ли продолжать минификацию или пропустить символ (например, если символ — пробел, то решение зависит от того, следует ли оставить его или удалить).

Метод использует проверку условий для адаптации минификации под разные контексты в коде, чтобы результат был корректным и эффективно минифицированным JavaScript-кодом.

 

  /* next -- get the next character, excluding comments. peek() is used to see
             if a '/' is followed by a '/' or '*'.
  */
  protected function next() { // Метод next() — получает следующий символ, исключая комментарии.
    $c = $this->get(); // Получаем следующий символ с помощью метода get(), который учитывает предыдущие символы.

    if ($c === '/') { // Если символ равен слэшу ("/"),
      switch($this->peek()) { // Проверяем следующий символ с помощью peek(), чтобы выяснить, что следует за "/".
        case '/': // Если следующий символ — слэш (начало однострочного комментария),
          for (;;) { // Запускаем бесконечный цикл для пропуска содержимого комментария.
            $c = (string) $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: // Если символ равен null (конец ввода),
                throw new JSMinException('Unterminated comment.'); // Бросаем исключение, так как комментарий не завершен.
            }
          }

        default: // Если следующий символ не является ни слэшем, ни звездочкой,
          return $c; // Возвращаем символ без изменений.
      }
    }

    return $c; // Если символ не является слэшем, просто возвращаем его.
  }

  protected function peek() { // Метод peek() — смотрит на следующий символ, не изменяя текущего положения указателя.
    $this->lookAhead = $this->get(); // Получаем следующий символ и сохраняем его в переменную lookAhead.
    return $this->lookAhead; // Возвращаем символ, который будет использоваться для дальнейшей проверки.
  }

Объяснение:

  1. Метод next(): Этот метод получает следующий символ, игнорируя комментарии (как однострочные, так и многострочные). Если встречается комментарий, метод пропускает его содержимое и продолжает обработку.

  2. Обработка однострочных комментариев (//): Когда встречается символ /, метод использует peek() для проверки следующего символа. Если это второй /, это означает начало однострочного комментария, и метод будет продолжать считывать символы, пока не встретит символ новой строки.

  3. Обработка многострочных комментариев (/* */): Когда встречается символ /, метод проверяет, не является ли следующий символ звездочкой (*), что указывает на начало многострочного комментария. В таком случае метод пропускает все символы внутри комментария, пока не встретит закрывающую звездочку и слэш (*/).

  4. Метод peek(): Этот метод используется для просмотра следующего символа, не двигая указатель на ввод, что позволяет проверить, что идет после текущего символа. Этот метод важен для определения типа комментария (однострочный или многострочный).

 

// -- Exceptions ---------------------------------------------------------------
class JSMinException extends Exception {} // Создаем класс исключения JSMinException, который наследует стандартное исключение PHP (Exception).

Объяснение:

  • Этот фрагмент кода объявляет класс JSMinException, который расширяет стандартное исключение Exception в PHP. Класс не добавляет дополнительной логики или свойств, но он позволяет создавать специфические исключения для обработки ошибок в процессе минификации JavaScript.
  • Использование этого класса позволяет идентифицировать ошибки, связанные с минификатором JS, как отдельные исключения, которые можно обрабатывать отдельно от других типов ошибок в программе.

 

 

 

 

 

15 минут чтения Sodium Carbonate

Комментарии (0)

Комментарии отсутствуют
Добавление комментариев доступно только зарегистрированным пользователям

Автор контента

webitproff

Оффлайн

Sodium Carbonate

Последняя авторизация: 19.07.2026 01:57

  • Страница размещена: 30.01.2025 16:32
  • Последнее обновление: 30.01.2025 16:45