
Zend_Tool – пакет Zend Framework’a, который позволяет писать свои консольные мини-приложения.
Написать своё подобное приложение достаточно просто, для этого нам потребуется сделать следующее:
- Написать класс провайдера (в нем будет реализована логика приложения)
- Написать класс манифеста (информационный класс, необходим для организации вывода информации о провайдерах)
- “Рассказать” где они ZF’у
Пример реализации провайдера
Начнем с написания простого провайдера:
class My_Tool_Project_Provider_MyProvider
extends Zend_Tool_Project_Provider_Abstract
implements Zend_Tool_Framework_Provider_Pretendable
{
public function getName()
{
// кастомное имя провайдера
return 'Myth';
}
public function say()
{
// будет выводить сие приветствие
echo "Hello Russian!";
}
}
И еще более простого манифеста:
class My_Tool_Project_Provider_Manifest implements Zend_Tool_Framework_Manifest_ProviderManifestable
{
/**
* Возвращаю список всех доступных провайдеров
*
* @return array
*/
public function getProviders()
{
return array(
new My_Tool_Project_Provider_MyProvider
);
}
}
Теперь перед нами стоит задача инициализации данного манифеста и провайдера, для этой цели нам необходимо прописать информацию о них в конфигурационном файле Zend_Tool – .zf.ini (не путать с application.ini):
Создаем новый, используя консоль (будет создан в вашей домашней директории):
zf --setup config-file
Вносим изменения:
basicloader.classes.0 = "My_Tool_Project_Provider_MyProvider" basicloader.classes.1 = "My_Tool_Project_Provider_Manifest"
Расположение файла zf.ini может быть следующим:
- Берется из переменной окружения ZF_CONFIG_FILE
- Загружается файл $homeDirectory/.zf.ini, если таковой имеется
- Загружается файл $storageDirectory/zf.ini, если таковой имеется
Так же необходимо, чтобы эти наши классы лежали в include_path, если это не приемлемо (ну мне уж точно не нравится) – то для этой цели лучше написать утилиту враппер и положить в директорию с zf.sh, вот мой файл zfc.sh:
#!/bin/sh # указываю свой путь к конфигурационному файлу ZF_CONFIG_FILE="./../.zf.ini" export ZF_CONFIG_FILE # добавляю директорию к опции include_path ZEND_TOOL_INCLUDE_PATH_PREPEND="./../library" export ZEND_TOOL_INCLUDE_PATH_PREPEND # вызываю zf.sh с всеми входящими параметрами ./zf.sh $@
Файл zfc.bat для Windows (спасибо Hunter’у):
@ECHO off SET ZF_CONFIG_FILE=./../.zf.ini SET ZEND_TOOL_INCLUDE_PATH_PREPEND=./../library ./zf.bat -- %*
Теперь пробуем вызвать наш провайдер:
cd project/bin ./zfc.sh say myth >> Hello Russian!
В результате всех вышеописанных манипуляций у меня получилась следующая структура проекта:
project |-- application |-- bin | |-- zf.bat | |-- zf.sh | |-- zf.php | |-- zfс.bat | `-- zfс.sh |-- library | |-- My | | `-- Tool | | `-- Project | | `-- Provider | | |-- Manifest.php | | `-- MyProvider.php | `-- Zend |-- public `-- .zf.ini
Провайдер для ZF проекта
Далее стоит привести пример написания провайдеров для интеграции в ZF проект. Начну пожалуй с изменений в структуре проекта:
project |-- application |-- bin |-- library | |-- My | | `-- Tool | | |-- Context | | | `-- My | | | |-- MythDirectory.php (My_Tool_Project_Context_My_MythDirectory) | | | `-- MythFile.php (My_Tool_Project_Context_My_MythFile) | | `-- Project | | `-- Provider | | |-- Abstract.php (My_Tool_Project_Provider_Abstract) | | |-- Manifest.php | | `-- MyProvider.php | `-- Zend |-- public `-- .zf.ini
Теперь приведу листинг каждого файла с небольшими пояснениями. Начну с файла My_Tool_Project_Provider_Abstract – он необходим для регистрации собственных элементов:
require_once 'Zend/Tool/Project/Provider/Abstract.php';
class My_Tool_Project_Provider_Abstract
extends Zend_Tool_Project_Provider_Abstract
{
/**
* constructor
*/
public function __construct()
{
parent::__construct();
// load My Context elements
$contextRegistry = Zend_Tool_Project_Context_Repository::getInstance();
$contextRegistry->addContextsFromDirectory(
dirname(dirname(__FILE__)) . '/Context/My/', 'My_Tool_Project_Context_My_'
);
}
}
Пишем свои элементы – свою директорию:
require_once 'Zend/Tool/Project/Context/Filesystem/Directory.php';
class My_Tool_Project_Context_My_MythDirectory extends Zend_Tool_Project_Context_Filesystem_Directory
{
/**
* @var string
*/
protected $_filesystemName = 'myth';
public function getName()
{
return 'MythDirectory';
}
}
И некие мифические классы:
class My_Tool_Project_Context_My_MythFile extends Zend_Tool_Project_Context_Zf_AbstractClassFile
{
protected $_mythName = 'Base';
protected $_filesystemName = 'mythName';
public function init()
{
$this->_mythName = $this->_resource->getAttribute('mythName');
$this->_filesystemName = ucfirst($this->_mythName) . '.php';
parent::init();
}
public function getPersistentAttributes()
{
return array(
'mythName' => $this->getMythName()
);
}
public function getName()
{
return 'MythFile';
}
public function getMythName()
{
return $this->_mythName;
}
public function getContents()
{
$className = 'Myth_'.ucfirst(strtolower($this->_mythName));
$codeGenFile = new Zend_CodeGenerator_Php_File(array(
'fileName' => $this->getPath(),
'classes' => array(
new Zend_CodeGenerator_Php_Class(array(
'name' => $className,
'methods' => array(
new Zend_CodeGenerator_Php_Method(array(
'name' => '__construct',
'body' => '/* Myth Here ... */',
))
)
))
)
));
return $codeGenFile->generate();
}
}
Теперь надо бы научиться использовать эти элементы, для этого в файле MyProvider добавляем метод create:
public function create($name = "base")
{
// загружаем профайл проекта (это файл .zfproject.xml)
$profile = $this->_loadProfile(self::NO_PROFILE_THROW_EXCEPTION);
// ищем директиву projectDirectory
$profileSearchParams = array('projectDirectory');
$projectDirectory = $profile->search($profileSearchParams);
if (!($projectDirectory instanceof Zend_Tool_Project_Profile_Resource)) {
throw new Zend_Tool_Project_Provider_Exception(
'Не нашли - прерываем работу'
);
}
/* создаем мифическую директорию, и сохраняем данные о ней в профайле */
$mythDirectory = $projectDirectory->search(array('mythDirectory'));
if (!($mythDirectory instanceof Zend_Tool_Project_Profile_Resource)) {
$mythDirectory = $projectDirectory->createResource('mythDirectory', array('filesystemName' => 'myth'));
$mythDirectory -> create();
}
/* создаем мифический класс */
/*@var Zend_Tool_Project_Profile_Resource $newMyth*/
/* запись в профайле */
$newMyth = $mythDirectory->createResource(
'mythFile',
array('mythName' => $name)
);
/* файл в системе */
$newMyth->create();
/* сохраняем профайл */
$this->_storeProfile();
}
Пробуем сие творение:
./zfc.sh create myth Bigfoot
Результатом будем создание директории myth с классом Myth_Bigfoot:
class Myth_Bigfoot
{
public function __construct()
{
/* Myth Here ... */
}
}
Скачать пример можно по ссылке: My_Tool
Очень круто!
Так можно сделать мега-генератор админки, например, и жить долго и счастливо
Спасибо за статью , нужно будет на досуге написать генератор CRUD так как это сделанно в руби на рельсах.
Хорошая статья!
Может и юнит тесты за одно сгенерировать?
У меня так и не хватило терпения найти причину по которой эта фича не работает “из коробки”.
Так и в чем она оказалась?
В этой статье Антона есть решение этой проблемы?
Проблема, насколько у меня хватило терпения, оказалась в том что Tool, не сканит директорию указанную в .xml как контейнер для провайдеров, возможно проблема в самом деле и не в этом, в багтрекере есть уже упоминание проблемы этой (http://framework.zend.com/issues/browse/ZF-7747 Created: 01/Sep/09 07:57 AM) возможно эта проблема снова платформозавимима, изначально Zend_Tool “из коробки” под виндой вообще не работал, но нет времени все детально проверить.
В статье Антона есть решение но не в том виде в котором мне бы хотелось: в виде .patch для Zend.
Привет!
Решил написать мало ли кому пригодится.
у кого ZF из портов FreeBSD развернут
zfc.sh может так быть
естественно в бин каталоге только zfc.sh
а также используйте класс Zend_Tool_Framework_Client_Console_ResponseDecorator_Colorizer
$console = new Zend_Tool_Framework_Client_Console_ResponseDecorator_Colorizer(); $color = 'hiGreen'; echo $console->decorate('Hello Russian!', $color);и маленькая приятная новость, если у функции say указать параметр ($text)
class My_Tool_Project_Provider_MyProvider extends Zend_Tool_Project_Provider_Abstract implements Zend_Tool_Framework_Provider_Pretendable { public function getName() { return 'Myth'; } public function say($text) { echo "Hello Russian!"; } }то при вызове провайдера
./zfc.sh say myth
появится приглашение к вводу параметра
Please provide a value for $text
zf>