Пишем свой провайдер для Zend_Tool

Zend_Tool – пакет Zend Framework’a, который позволяет писать свои консольные мини-приложения.

Написать своё подобное приложение достаточно просто, для этого нам потребуется сделать следующее:

  1. Написать класс провайдера (в нем будет реализована логика приложения)
  2. Написать класс манифеста (информационный класс, необходим для организации вывода информации о провайдерах)
  3. “Рассказать” где они ZF’у

Пример реализации провайдера

Начнем с написания простого провайдера:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
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!";
    }
}

И еще более простого манифеста:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
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):

Создаем новый, используя консоль (будет создан в вашей домашней директории):

1
zf --setup config-file

Вносим изменения:

1
2
basicloader.classes.0 = "My_Tool_Project_Provider_MyProvider"
basicloader.classes.1 = "My_Tool_Project_Provider_Manifest"

Расположение файла zf.ini может быть следующим:

  1. Берется из переменной окружения ZF_CONFIG_FILE
  2. Загружается файл $homeDirectory/.zf.ini, если таковой имеется
  3. Загружается файл $storageDirectory/zf.ini, если таковой имеется

Так же необходимо, чтобы эти наши классы лежали в include_path, если это не приемлемо (ну мне уж точно не нравится) – то для этой цели лучше написать утилиту враппер и положить в директорию с zf.sh, вот мой файл zfc.sh:

01
02
03
04
05
06
07
08
09
10
11
#!/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’у):

1
2
3
4
5
@ECHO off
SET ZF_CONFIG_FILE=./../.zf.ini
SET ZEND_TOOL_INCLUDE_PATH_PREPEND=./../library
 
./zf.bat -- %*

Теперь пробуем вызвать наш провайдер:

1
2
3
cd project/bin
./zfc.sh say myth
>> Hello Russian!

В результате всех вышеописанных манипуляций у меня получилась следующая структура проекта:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
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 проект. Начну пожалуй с изменений в структуре проекта:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
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 – он необходим для регистрации собственных элементов:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
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_'
        ); 
    }
}

Пишем свои элементы – свою директорию:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
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';
    }
}

И некие мифические классы:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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();
}

Пробуем сие творение:

1
./zfc.sh create myth Bigfoot

Результатом будем создание директории myth с классом Myth_Bigfoot:

1
2
3
4
5
6
7
class Myth_Bigfoot
{
    public function __construct()
    {
        /* Myth Here ... */
    }
}

Скачать пример можно по ссылке: My_Tool

Полезные ссылки

8 thoughts on “Пишем свой провайдер для Zend_Tool”

  1. Очень круто!
    Так можно сделать мега-генератор админки, например, и жить долго и счастливо

  2. Спасибо за статью , нужно будет на досуге написать генератор CRUD так как это сделанно в руби на рельсах.

  3. У меня так и не хватило терпения найти причину по которой эта фича не работает “из коробки”.

    1. Так и в чем она оказалась?
      В этой статье Антона есть решение этой проблемы?

      1. Проблема, насколько у меня хватило терпения, оказалась в том что Tool, не сканит директорию указанную в .xml как контейнер для провайдеров, возможно проблема в самом деле и не в этом, в багтрекере есть уже упоминание проблемы этой (http://framework.zend.com/issues/browse/ZF-7747 Created: 01/Sep/09 07:57 AM) возможно эта проблема снова платформозавимима, изначально Zend_Tool “из коробки” под виндой вообще не работал, но нет времени все детально проверить.

        В статье Антона есть решение но не в том виде в котором мне бы хотелось: в виде .patch для Zend.

  4. Привет!

    Решил написать мало ли кому пригодится.
    у кого ZF из портов FreeBSD развернут
    zfc.sh может так быть

    1
    2
    3
    4
    5
    6
    7
    #!/bin/sh
     
    PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin
     
    тра-та-та
     
    zf $@

    естественно в бин каталоге только zfc.sh

    а также используйте класс Zend_Tool_Framework_Client_Console_ResponseDecorator_Colorizer

    1
    2
    3
    $console = new Zend_Tool_Framework_Client_Console_ResponseDecorator_Colorizer();
            $color = 'hiGreen';
            echo $console->decorate('Hello Russian!', $color);

    и маленькая приятная новость, если у функции say указать параметр ($text)

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    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>

Leave a Reply to Zh0rzh Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.