LogoPhly, boy, phly
the weblog and site of Matthew Weier O'Phinney

Thursday, July 1. 2010

Creating Zend_Tool Providers

When I was at Symfony Live this past February, I assisted Stefan Koopmanschap in a full-day workshop on integrating Zend Framework in Symfony applications. During that workshop, Stefan demonstrated creating Symfony "tasks". These are classes that tie in to the Symfony command-line tooling -- basically allowing you to tie in to the CLI tool in order to create cronjobs, migration scripts, etc.

Of course, Zend Framework has an analogue to Symfony tasks in the Zend_Tool component's "providers". In this post, I'll demonstrate how you can create a simple provider that will return the most recent entry from an RSS or Atom feed.

First things first

Caveat: this entire post assumes you are using a unix-like operating system, such as a Linux distribution or Mac OSX. Most of the instructions should work in Windows, but I have not tested on that platform.

First, a little setup. Zend_Tool needs some configuration. To get started, you need to run the following command (if you haven't already):


% zf create config
 

This will create a configuration in $HOME/.zf.ini. If you pop that file open, you should see an entry for php.include_path. This is the include_path Zend_Tool will use, and should include your ZF installation; any providers you create should be on this path -- or you should modify it to add a path to your providers.

Create the provider

Providers are incredibly simple. The easiest way to create one is to create a class extending Zend_Tool_Framework_Provider_Abstract, and then to simply start creating methods.

A few rules are good to know, however:

  • If you need to throw an exception, throw a Zend_Tool_Project_Exception. This integrates with the CLI tooling to provide nice, colorful error messages.
  • While you can echo directly from your methods, the suggested practice is to use the response object and append content to it. This will ensure that if we later write an XML-RPC, SOAP, or web frontend to Zend_Tool, you will not need to make any changes to your code. This is as easy as:

    $response = $this->_registry->getResponse();
    $response->appendContent($content);
     

In my provider, I'm wanting to grab the first entry of a given feed. Instead of needing to remember the feed URL, I'd like to use a mnemonic; this will be my sole argument to the provider. I'll have it default to my own feed. The code ends up looking like this:


class Phly_Tool_Feed extends Zend_Tool_Framework_Provider_Abstract
{
    protected $_feeds = array(
        'weierophinney' => 'http://weierophinney.net/matthew/feeds/index.rss1',
        'planetphp'     => 'http://www.planet-php.net/rdf/',
    );

    /**
     * Read the first item of a feed
     *
     * @param  string $feed Named identifier for a feed
     * @return bool
     */

    public function read($feed = 'weierophinney')
    {
        if (!array_key_exists($feed, $this->_feeds)) {
            throw new Zend_Tool_Project_Exception(sprintf(
                'Unknown feed "%s"',
                $feed
            ));
        }

        $feed = Zend_Feed_Reader::import($this->_feeds[$feed]);
        $title = $desc = $link = '';
        foreach ($feed as $entry) {
            $title = $entry->getTitle();
            $desc  = $entry->getDescription();
            $link  = $entry->getLink();
            break;
        }
        $content = sprintf("%s\n%s\n\n%s\n", $title, strip_tags($desc), $link);

        $response = $this->_registry->getResponse();
        $response->appendContent($content);
        return true;
    }
}
 

I'm leveraging Zend_Feed_Reader here, and simply creating some formatted text output.

Now that the provider is created, I need to put it in the file Phly/Tool/Feed.php, relative to a directory in the include_path configured by Zend_Tool.

Tying the provider to the tool

Now that we've got the provider written and somewhere Zend_Tool can potentially find it, we need to tell Zend_Tool about it. Open up the $HOME/.zf.ini file again, and add the following line:


basicloader.classes.1 = "Phly_Tool_Feed"
 

This tells Zend_Tool that there's an additional provider it should be aware of. Note in particular the ".1" portion of the key; "basicloader.classes" is an array. One gotcha I discovered is that, unlike Zend_Config, you cannot use the "[]" notation. In other words, the following does not work:


basicloader.classes[] = "Phly_Tool_Feed"
 

You need to specify keys manually, and they need to be unique.

Getting help

Now, time to test out if it all works. If you've done the above steps, you can now execute the following:


% zf \? feed
 

Note: I use zsh, and need to escape the question mark; you may not need to in other shells.

If all is well, you'll get the following:

Actions supported by provider "Feed"
  Feed
    zf read feed feed[=weierophinney]

If you're not seeing this, check to make sure that your provider is on an include_path as defined in your .zf.ini file; if you still have issues, ask on the fw-general mailing list or in the #zftalk IRC channel on Freenode.

Using the provider

Once your provider is working, fire it up:


% zf read feed
 

or


% zf read feed planetphp
 

You should get something that looks like this (the actual entry will vary):

State of Zend Framework 2.0

    
    The past few months have kept myself and my team quite busy, as we've turned
    our attentions from maintenance of the Zend Framework 1.X series to Zend
    Framework 2.0. I've been fielding questions regularly about ZF2 lately, and
    felt it was time to talk about the roadmap for ZF2, what we've done so far,
    and how the community can help.

 Continue reading "State of Zend Framework 2.0"
    

http://weierophinney.net/matthew/archives/241-State-of-Zend-Framework-2.0.html

Closing notes

One "gotcha" you may experience is that there is currently no support for specifying project-specific providers within applications created with Zend_Tool -- a feature that would be quite useful for creating project-specific tasks.*

That said, Zend_Tool providers are an incredibly useful and easy way to write CLI tools based on Zend Framework. Hopefully this post will help demystify the component and its usage, and get you thinking about what tasks you would like to write.

* You can fake it by creating an alternate configuration file in your project, informing the environment of it, and calling the zf commandline tool -- something that can be done in a single line:


% ZF_CONFIG_FILE=./zf.ini; zf <action> <provider> ...
 
Posted by Matthew Weier O'Phinney in PHP at 09:05 | Comments (7) | Trackbacks (0)
Defined tags for this entry: php, zend framework
Related entries by tags:
Autoloading Benchmarks
Applying FilterIterator to Directory Iteration
Running mod_php and FastCGI side-by-side
State of Zend Framework 2.0
Writing Gearman Workers in PHP

Trackbacks
Trackback specific URI for this entry

No Trackbacks

Comments
Display comments as (Linear | Threaded)

I really like Zend Tool but the gotcha you mentioned at the end of the article is really quite a big limitation. I hope this is something that something that can be improved in a future ZF release.

In my opinion the simplest solution would be for the command line tool to check for the existence of a .zfproject.xml file in the directory the command is run from. If found, it would then use values from this instead of .zf.ini. This way, users can use project specific providers as long as they run the commands from the root directory of their ZF project.

Up until now I've been using the ZF_INCLUDE_PATH_PREPEND variable to add my app's application/models to Zend Tool's path, but your ZF_CONFIG_FILE solution is much nicer - thanks!
#1 Tim Fountain (Link) on 2010-07-01 11:07 (Reply)
Hey Tim,

I agree, Zend_Tool_Project should be more aware of project specific providers. This is something I hope to address in 1.11. The currently limitation is one of a chicken and egg problem that needs to be solved. Providers are found and loaded before a project is traversed, which specifies where project specific providers are located, but projects are only loaded by a dispatched provider, (.. so, you can see the conundrum).

I'll keep the mailing list and this post posted on my developments.

-ralph
#1.1 Ralph Schindler (Link) on 2010-07-01 11:51 (Reply)
Ha ha ZF 1.11 ? ^^
You think about what specific providers in ZF ?
#1.1.1 Intiilapa (Link) on 2010-07-07 16:31 (Reply)
Matthew, this is awesome! Thanks for doing this one. :-D
#2 Rob Zienert (Link) on 2010-07-01 12:45 (Reply)
Isn't the ? quite unconventional? Escaping issues aside, I think most people are used to either the svn way 'svn help command' or the general --help or -h flags?
#3 Ivo (Link) on 2010-07-02 04:04 (Reply)
Is there any manual about generating code by Zend_Tool? Currently I'm occupied in code-generation of a wrapper for Oracle stored procedures and named datatypes. I see, in generating ZF-project blank there used some more abstractions like Resources, Context, etc.; it works with .zfproject.xml and so on. It wold be great to read about it somewhere else that in ZF source code :-) Thank you.
#4 Sergei (Link) on 2010-07-13 01:58 (Reply)
Most providers in Zend_Tool do code generation using Zend_CodeGenerator. The resources and contexts used in Zend_Tool_Project help to indicate a) where the code should go, and b) how it should be named.

Unfortunately, the only documentation on this is towards the bottom of http://framework.zend.com/manual/en/zend.tool.extending.html, starting with the heading "Zend_Tool_Project Extensions". It isn't much information at this time; I hope we can rectify that in coming releases. Until then, though, the best guide truly is the ZF source code.
#4.1 Matthew Weier O'Phinney (Link) on 2010-07-13 08:44 (Reply)

Add Comment

Standard emoticons like :-) and ;-) are converted to images.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications

To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.
CAPTCHA

 
 
  • Home
  • Resume
  • Blog
  • Phly PEAR Channel
  • Twitter
  • Contact Me
  • About this site

ZCE

Zend Education Advisory Board Member

Add to Technorati Favorites

Calendar

Back September '10
Mon Tue Wed Thu Fri Sat Sun
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30      

Quicksearch

Links

  • PHLY - PHp LibrarY
  • Planet PHP
  • Zend Framework, where I'm project lead
  • Sebastian Bergmann
  • Cal Evans
  • Shahar Evron
  • Paul M. Jones
  • Bill Karwin
  • Mike Naberezny
  • Fabien Potencier
  • Ben Ramsey
  • Derick Rethans
  • Ralph Schindler
  • Marco Tabini

Archives

September 2010
August 2010
July 2010
Recent...
Older...

Categories

XML Linux
XML Personal
XML Aikido
XML Family
XML Programming
XML Dojo
XML Perl
XML PHP

All categories

Syndicate This Blog

XML RSS 0.91 feed
XML RSS 1.0 feed
XML RSS 2.0 feed
ATOM/XML ATOM 0.3 feed
ATOM/XML ATOM 1.0 feed
XML RSS 2.0 Comments

Show tagged entries

xml apache
xml best practices
xml books
xml conferences
xml cw09
xml decorators
xml dojo
xml dpc08
xml file_fortune
xml git
xml linux
xml mvc
xml oop
xml pear
xml perl
xml personal
xml php
xml phpworks08
xml programming
xml rest
xml ubuntu
xml vim
xml webinar
xml zendcon
xml zendcon08
xml zendcon09
xml zend framework
© 2004 - present, Matthew Weier O'Phinney
matthew-web <at> weierophinney.net