Как написать задачу для Phing

Тема работы с Phing уже не однократно поднималась в этом блоге. Переоценить значение этого инструмента очень сложно. Но всегда наступает момент, когда стандартных возможностей становиться недостаточно. В такой ситуации вы можете сами написать нужные функции и оформить их в виде задач.
В этой статье я покажу пример создания такой задачи.
Примечание. Я предполагаю, что вы хотя бы в общих чертах знакомы с Phing и знаете как его установить и написать простейший build файл. Если нет, советую сначала почитать эти статьи: Программирование на PHP. Избавляемся от рутинных операций с помощью Phing и Использование Phing для сборки web приложений.
Допустим, нам нужно изменять кодировку файлов. На PHP сделать это можно, например, с помощью функции mb_convert_encoding. В качестве параметров она принимает:
- исходный текст;
- нужную кодировку;
- исходную кодировку.
А после выполнения возвращает перекодированный текст.
Т.е. можно написать элементарный PHP скрипт, который будет изменять кодировки указанных файлов.
Но если нужно выполнять эту операцию на каком-то промежуточном этапе при построении проекта, то пользоваться таким скриптом будет не очень удобно. Придется либо запускать его каждый раз вручную, либо писать еще один скрипт, который будет запускать перекодировщик.
С другой стороны, с помощью Phing мы можем запустить несколько операций нажатием одной кнопки. Почему бы не добавить сюда и перекодировку?
Итак, приступим.
Шаг 1. Составляем перечень исходных данных.
Исходя из перечня параметров функции mb_convert_encoding нам потребуются:
- название исходной кодировки файлов;
- название кодировки, в которую мы хотим преобразовать файлы;
- список файлов;
- папку, в которую будем складывать перекодированные файл.
Теперь взгляните на пример использования нашей задачи.
-
<taskdef name="convencoding" classname="phing.tasks.my.convEncodingTask" />
-
-
<target name="convertencoding">
-
<convencoding fromencoding="windows-1251" toencoding="utf-8"
-
targetdir="new_dir">
-
<fileset dir="src_dir" />
-
</convencoding>
-
</target>
Разберем его подробнее.
В первой строке мы подключаем нашу задачу. Конечно, на данный момент мы её еще не написали, но уже сейчас нужно определиться с её размещением и названием (задается в атрибуте classname).
В данном случае, она должна находиться в папке phing.tasks.my. Тут phing указывает на папку в которую он установлен, а все точки нужно заменить на «\» (или «/»).
После этого мы создаем новую операцию (target) и в неё добавляем нашу задачу (convencoding) (строки 4-7).
В её атрибутах указываем три параметра:
- fromencoding – исходная кодировка;
- toencoding – кодировка, которую нужно получить;
- targetdir – папка, в которую будем складывать перекодированные файлы.
И добавляем тег fileset, в котором определяем список исходных файлов.
Шаг 2. Создаем скрипт задачи.
Для этого заходим в папку с phing/tasks/, создаем в ней папку my, а в ней файл convEncodingTask.php. Этот файл будет содержать весь код нашей задачи. Его название составляется из названия задачи и слова Task.
Этот файл должен содержать класс, название которого совпадает с именем файла и, кроме того, он должен быть потомком класса Task.
Объявляем класс.
-
require_once 'phing/Task.php';
-
-
class convEncodingTask extends Task {
-
-
}
Шаг 3. Работа с атрибутами.
Как я уже говорил, все исходные данные для задач Phing мы передаем в атрибутах. Поэтому объявляем свойства для каждого атрибута (область видимости – protected).
-
protected $targetDir;
-
protected $fromEncoding;
-
protected $toEncoding;
-
protected $filesets = array();
-
protected $failonerror = false;
Названия свойств должны совпадать с названиями атрибутов.
Обратите внимание на свойство $filesets, которому мы присвоили пустой массив. В нем будет храниться список файлов, который мы задали с помощью задачи fileset (строка 7 в первом листинге).
Последнее свойство используется для того, чтобы разработчик build файла мог указать нужно ли продолжать выполнение при возникновении ошибки в этой задаче.
Каждый атрибут с исходными данными должен иметь соответствующее свойство и метод, который его устанавливает.
Тут важно понимать, что Phing вызывает эти методы сам, т.е. нам нужно их только объявить.
-
public function setTargetDir($targetDir) {
-
$this->targetDir = $targetDir;
-
}
-
-
public function setFromEncoding($fromEncoding) {
-
$this->fromEncoding = $fromEncoding;
-
}
-
-
public function setToEncoding($toEncoding) {
-
$this->toEncoding = $toEncoding;
-
}
-
-
public function setFailonerror($value) {
-
$this->failonerror = $value;
-
}
Как видите, эти методы просто устанавливают значения соответствующих свойств. Их имена образуются добавлением приставки set к имени свойства.
Шаг 4. Добавляем поддержку списка файлов.
Отдельных пояснений требует метод установки списка файлов. Называется он createFileSet.
В нём мы создаем объект типа FileSet, сохраняем его в массиве $this->filesets. Этот метод должен возвратить сохраненный объект.
-
public function createFileSet() {
-
$num = array_push($this->filesets, new FileSet());
-
return $this->filesets[$num – 1];
-
}
Еще раз хочу напомнить. Напрямую к этим методам мы обращаться не будем. Это сделает Phing без нашего участия. Поэтому очень важно правильно записать имена методов и проследить за типом возвращаемых значений.
Шаг 5. Инициализация
Тут нам нужно просто объявить метод init
-
public function init() {
-
return true;
-
}
Шаг 6. Пишем основной метод.
В нём мы будем выполнять изменение кодировок файлов. На момент его запуска Phing установит значения всех свойств, т.е. мы можем ими спокойно пользоваться (внутри этого метода).
Теперь взгляните на код метода.
-
public function main() {
-
//проверяем, установлен ли модуль Multibyte String
-
if (function_exists("mb_convert_encoding")) {
-
//начинаем обработку
-
//(у нас может быть несколько наборов файлов FileSet)
-
foreach ($this->filesets as $fs) {
-
try {
-
//получаем массив со списком исходных файлов
-
$files = $fs->getDirectoryScanner($this->project)->getIncludedFiles();
-
$fullPath = realpath($fs->getDir($this->project));
-
//изменяем кодировку каждого файла
-
foreach ($files as $file) {
-
$this->log('Converting file ' . $file);
-
//формируем путь к конвертированному файлу
-
$target = $this->targetDir . '/' . str_replace($fullPath, ", $file);
-
if (file_exists(dirname($target)) == false) {
-
mkdir(dirname($target), 0700, true);
-
}
-
//конвертируем файл
-
file_put_contents($target, mb_convert_encoding(file_get_contents($fullPath . '/' . $file), $this->toEncoding, $this->fromEncoding));
-
}
-
} catch (BuildException $be) {
-
// папка не существует или доступ к ней закрыт
-
if ($this->failonerror) {
-
throw $be;
-
} else {
-
$this->log($be->getMessage(), $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN);
-
}
-
}
-
}
-
}
-
else {
-
//ошибка (недоступна функция mb_convert_encoding)
-
$be = new BuildException('No Multibyte String support');
-
if ($this->failonerror) {
-
throw $be;
-
}
-
else {
-
//если нужно продолжать выполнение build файла, несмотрая на ошибку, просто пишем в лог
-
$this->log($be->getMessage(), $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN);
-
}
-
}
-
}
Больше чем вы ожидали? Давайте посмотрим внимательнее. Большую часть здесь занимают: работа со списком файлов, различные проверки и обработка ошибок.
Рассмотрим все по порядку.
В строке 3 мы проверяем, доступна ли функция mb_convert_encoding. Если нет, то создаем исключение (BuildException) и в зависимости от значения свойства failonerror либо отправляем его, либо пишем сообщение в лог.
Если функция доступна, начинаем обработку файлов.
Как вы помните, список файлов хранится в свойстве filesets, точнее в нем хранится массив объектов FileSet, каждый из которых содержит список файлов.
Поэтому для обработки всех файлов нам нужно использовать два вложенных цикла. Внешний (строка 6) проходит по всем объектам FileSet, а внутренний (строка 12) – по всем файлам в этих объектах.
Вы, наверное, заметили, что здесь мы используем свойство, которого не объявляли ($this->project). Это свойство содержит объект с данными проекта и создается автоматически. В данном случае мы его используем при получении имен файлов.
Кроме того, мы используем несколько методов объекта FileSet. Например, getDirectoryScanner (объявлен в файле phing\types\AbstractFileSet.php). Подробно я на них останавливаться не буду, названия говорят сами за себя. В любом случае, они нам нужны только для того, чтобы получить список исходных файлов. И в большинстве задач этот код (строки 9-10) меняться не будет.
Во внутреннем цикле (строки 12-21) мы формируем пути к перекодированным файлам и изменяем кодировку (строка 20).
Обратите внимание на строку 13. В ней мы записываем в лог имена всех переименованных файлов. Эти записи очень помогают при поиске ошибок в build файлах.
В строках 22-29 мы перехватываем и обрабатываем возможные исключения.
Заключение.
Как видите, создание собственно задачи для Phing не намного сложнее разработки обычного PHP скрипта. Нужно только знать несколько правил и особенностей.
По большому счету, написать скрипт с аналогичной функциональностью было бы сложнее, т.к. пришлось бы писать парсер для xml файла или придумывать другой способ ввода информации.
Скачать.
Если вы хотите поэкспериментировать – качайте архив с этой задачей. В нем же находится небольшая инструкция по установке.
Интересно почитать.
Учимся профессионально водить с авто инструктором.
Рассказать о чем-то всему миру можно с помощью бесплатные объявления казахстан.
Позаботиться о своем здоровье вам помогут в клиника Марыныч.
Понравилась статья? Подписывайтесь на продолжение
!
Опубликовано в PHP, Phing, Web разработка 1 Комментарий »
Один комментарий
Вы можете отслеживать обсуждение записи с помощью RSS 2.0 ![]()
Вы также можете оставить комментарий, или трекбек с Вашего сайта.









Спасибо за информацию. очень полезно