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

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

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

  1. Написать класс провайдера (в нем будет реализована логика приложения)
  2. Написать класс манифеста (информационный класс, необходим для организации вывода информации о провайдерах)
  3. “Рассказать” где они 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 может быть следующим:

  1. Берется из переменной окружения ZF_CONFIG_FILE
  2. Загружается файл $homeDirectory/.zf.ini, если таковой имеется
  3. Загружается файл $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

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

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 может так быть

    #!/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

    $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>

Leave a 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.