Tag Archives: Ffh

Zend_Log with multiple writers

So I was sitting here thinking to myself “This is Friday and I’m not getting much of anything done.  Maybe I should write another Friday Framework Highlight.”  I figured that it was a good idea so I pondered what I should write.  I came up blank and so I asked Matthew Weier O’Phinney.  “Multiple writers for Zend_Log,” he said.  I agreed.

If you were not aware, Zend_Log provides facilities for writing to multiple logs through the same log instance.  Additionally, you can do this via configuration options when using a Zend_Applicatin resource plugin.  Together those make for very powerful logging mechanisms.  “How?” you ask?  It’s really easy.  Take your application.ini file, which you use to configure your Zend_Application instance, and make it look something like this.  I’ll highlight the pertinent parts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[production]
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
includePaths.library = APPLICATION_PATH "/../library"
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
resources.frontController.params.displayExceptions = 0
resources.log.stream.writerName = "Stream"
resources.log.stream.writerParams.stream = APPLICATION_PATH "/logs/application.log"
resources.log.stream.writerParams.mode = "a"
resources.log.stream.filterName = "Priority"
resources.log.stream.filterParams.priority = 4
 
[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
resources.log.stream.filterParams.priority = 7
resources.frontController.params.displayExceptions = 1
resources.log.firebug.writerName = "FireBug"
resources.log.firebug.filterName = "Priority"
resources.log.firebug.filterParams.priority = 7

 

What this does is say that “in production, log warnings and above to the log file, but in development, log debug to the log file AND send the log items to FirePHP.”  Then, in our index controller we put this:

1
2
3
4
5
6
7
8
9
class IndexController extends Zend_Controller_Action
{
 
    public function indexAction()
    {
        $this->getInvokeArg('bootstrap')->log->debug("I'm at indexAction");
    }
 
}

 

When we execute this code we get both the output in the application log

$ tail -f application.log
2010-09-10T16:27:25-05:00 DEBUG (7): I'm at indexAction

and in the Firebug log

X-Wf-Protocol-1            http://meta.wildfirehq.org/Protocol/JsonStream/0.2
X-Wf-1-Structure-1    http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1
X-Wf-1-Plugin-1            http://meta.firephp.org/Wildfire/Plugin/ZendFramework/FirePHP/1.6.2
X-Wf-1-1-1-1            122|[{"Type":"LOG","File":"C:\workspace\Test\application\controllers\IndexController.php","Line":8},"I'm at indexAction"]|

In production we wouldn’t get anything since this code would filter out debug logging, due to the resources.log.stream.filterParams.priority setting in the production section in application.ini.  Simple.  Done.

Zend_Server

Let’s take a quick look at something that’s kind of neat in Zend Framework.  I’ve been doing some work with Adobe on some articles and one of them was on working with mobile clients with Flash.  Well, me being the masochist I did more.  What I did was write an example that worked as a full website, an Ajax website, a Flash service and an XML-RPC service.

Setting the Stage

First I started with a Zend_Db row instance and it’s corresponding table instance.

1
2
3
4
5
6
7
8
9
10
11
12
// row
class Model_Peak extends Zend_Db_Table_Row_Abstract
{
/* plus a bunch of getters and setters */
}
 
// table
class Model_DbTable_Peak extends Zend_Db_Table_Abstract
{
    protected $_rowClass = 'Model_Peak';
    protected $_name = 'peaks_location';
}

 

Then I wrote a mapper, which accessed the DB models and exposed the functionality I wanted to provide for all of the different types of requests

1
2
3
4
5
6
7
8
9
class Mapper_Peaks
{
    public function getStates() { /* TODO */ }
 
    public function getRanges($state) { /* TODO */ }
 
    public function getMountains($state, $range) { /* TODO */ }   
 
}

 

After that I wrote a controller that handled the web and JSON-bootstrap pages.

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class IndexController extends Zend_Controller_Action
{
 
    public function indexAction()
    {
        $mapper = new Mapper_Peaks();
        $this->view->states = $mapper->getStates();
    }
 
    public function rangesAction()
    {
        $this->view->state = $this->_request->getParam('state');
        $mapper = new Mapper_Peaks();
        $this->view->ranges = $mapper->getRanges(
            $this->view->state
        );
    }
 
    public function mountainsAction()
    {
        $mapper = new Mapper_Peaks();
        $this->view->state = $this->_request->getParam('state');
        $this->view->range = $this->_request->getParam('range');
        $this->view->mountains = $mapper->getMountains(
            $this->view->state,
            $this->view->range
        );
    }
 
    public function jsonrpcAction() {}
 
    public function serviceAction()
    {
        $serviceHandler = $this->getRequest()->getParam('serviceHandler');
        if ($serviceHandler instanceof Zend_Server_Interface) {
            $smd = $this->_request->getParam('smd');
            if ($smd) {
                echo $smd;
                exit;
            }
 
            $out = $serviceHandler->handle();
            ob_clean();
            echo $out;
            exit;
        }
        throw new Zend_Controller_Response_Exception('Not Found', 404);
    }
}

 

Zend_Server

Now for the fun part.  Notice serviceAction()?  That’s where the Zend_Server_* stuff comes in.  In a plugin, which I will show you in a bit, I attached an instance of Zend_Server_Interface.  Then in the controller I simple told it to do its thing, with the exception of handing the Service Mapping Description for JSON-RPC ($smd).  That’s it.  Because Zend_Json_Server, Zend_XmlRpc_Server and Zend_Amf_Server all implement the Zend_Server_Interface interface that’s all I needed to do.

But since everything is going to one place you need to do a bunch of freaky logic to get the right one, right?  Nope.  It’s actually quite easy.  What I did was create a plugin that detected the request type, instantiated the required server instance and attached it to the request after setting the request object to execute the serviceAction() method instead of the requested one.  In other words, you could send your request to /34gas/q43ar/13/asdfg/as and it would still go to the serviceAction() method.  Here’s the code

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
31
32
33
34
35
36
37
class Ctx_Controller_Plugin_ServicePlugin extends Zend_Controller_Plugin_Abstract
{
    public function routeShutdown ( Zend_Controller_Request_Abstract $request)
    {
        $serviceHandler = null;
        if ($request->isXmlHttpRequest() && $request->isPost()) {
            $serviceHandler = new Zend_Json_Server();
            $serviceHandler->getServiceMap()->setDojoCompatible(true);
        } else if ($request->isFlashRequest()) {
            $serviceHandler = new Zend_Amf_Server();
 
        /* Note: This may be different depending on the SAPI you are using */
        } else if (strpos($request->CONTENT_TYPE, 'text/xml') === 0) {
            $serviceHandler = new Zend_XmlRpc_Server(
                'http://' .$_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME']
            );
        } else if($request->isXmlHttpRequest()) {
            $serviceHandler = new Zend_Json_Server();
            $smd = $serviceHandler->getServiceMap();
            $smd->setDojoCompatible(true);
            $request->setParam('smd', $smd);
            $view = new Zend_View();
            $serviceHandler->setTarget($view->url())
                           ->setEnvelope(Zend_Json_Server_Smd::ENV_JSONRPC_2);
        }
        if ($serviceHandler) {
            foreach (glob(APPLICATION_PATH.'/mappers/*.php') as $dir) {
                $name = substr(basename($dir), 0, -4);
                $class = 'Mapper_' . $name;
                $serviceHandler->setClass($class, $name);
            }
            $request->setParam('serviceHandler', $serviceHandler);
            $request->setControllerName('index');
            $request->setActionName('service');
        }     
    }
}

Looks like a lot, right?  Actually there’s not much there.  Here’s the logic flow.

  • Is it an XMLHTTP Request and is it a POST? Create the Json server
  • Is it an AMF request? Create the AMF server
  • Is it an XmlRpc request? Create the XmlRpc server
  • Is it an XMLHTTP Request and is it a GET? Create the Service Map (for JSON-RPC 2.0)
  • If a service handler has been created add all of the application’s mappers, attach the service handler to the request and redirect to the service action.

And with that you have an application that can serve content for multiple different types of service with almost no effort on your part.  At least.. if you copy and paste this code.

Have a good Friday!!!

Pre-caching PHP content with Zend_Cache_Manager and the Zend Server Job Queue

With the web being what it is today there can be a lot of times when you want to aggregate data from many different sources and bring them together in a single page.  I have not done much of that on my site simply because that means that I then need to learn a bunch of different API's.  However, since Youtube is the #2 search engine I figured that it might not be a bad idea to aggregate some of my YouTube content on my page automatically.  I don't necessarily want to do a blog post about each individual video I post, but I wanted there to be some place where I could just list them out.

I have two places where I post content.  Youtube and Facebook.  However, polling each site individually for each request is not conducive to having a page that renders quickly.  The thing you do NOT want to do is poll YouTube each time someone comes to an individual page.  The way around this is to cache the contents of the YouTube or Facebook query so you don't have to do that.  Then people are able to re-use the previously defined data when they view that page.  What this does is make most of the new requests to that page much faster since they don't have to re-load that data from YouTube or Facebook.  However, there's a bit of a problem there as well.  Every X number of minutes, the cache will expire and someone will take the hit of connecting to Youtube.  With a moderately low traffic site such as mine, that hit is something I didn't want to make my users endure when they came to the site since there is a decent probability that the cache will expire in between individual page requests.  And, working for Zend, I can't have a page that renders slowly, can I.

So what I did was create a new Zend Server Job Queue task, which I have detailed several times (and there should be a link to several on the side) that would connect to both YouTube and Facebook.  This task would insert the results into a cache (you could use a database if you liked) so that when someone came to a page that they would be seeing the cached data rather than polling YouTube.  From a settings perpective, the cache is set to never expire the content there.  But because I set the task to run once an hour the content is going to be refreshed.  Using this pre-population method I am able to keep requests snappy which at the same time providing mostly up to date content.

The task to do this is relatively simple.  First I edit my application.ini file to set up the cache manager.

resources.cachemanager.video.frontend.name = Core
resources.cachemanager.video.frontend.options.automatic_serialization = true
resources.cachemanager.video.frontend.options.lifetime = null
resources.cachemanager.video.backend.name = File

By defining these ini settings, Zend_Application will automatically instantiate an instance of Zend_Cache_Manager and set up a cache that is named "video" with the individual options as specified.  What this means is that I could create another cache interface by taking these configuration lines and giving it its own configuration settings.  It could be different settings or even a completely different backend, or a different front end.

Then I create my task class.

class Admin_Task_VideoPreCache extends Esc_Queue_TaskAbstract

    protected function _execute(Zend_Application $app)
    {
        $yt = new Zend_Gdata_YouTube();
        $options = $app->getOption('video');
        $uploads = $yt->getUserUploads($options['youtube']['id']);
        $manager = $app->getBootstrap()->getResource('cachemanager');
        /* @var $manager Zend_Cache_Manager */
        $manager->getCache('video')->save($uploads, 'youtube');
       
        $query = 'SELECT title, description, embed_html FROM video WHERE owner=' . $options['facebook']['id'];
        $url = 'https://api.facebook.com/method/fql.query?query='.urlencode($query);
        $data = simplexml_load_string(file_get_contents($url));
        $videos = array();
        foreach ($data->video as $video) {
            $videos[] = array(
                'title'    => (string)$video->title,    
                'description'    => (string)$video->description,
                'embed_html'    => (string)$video->embed_html
            );
        }
        $manager->getCache('video')->save($videos, 'facebook');
    }
}

Because the Zend_Application instance is always passed in I can easily get access to the predefined cache manager object in here for when I need to store the data at the end of the task.  Then in the task I use Zend_GData_Youtube to query YouTube and I do a simple FQL query to Facebook to get the Facebook videos (which stopped working between test, staging and production.  Go figure).

The next thing I have to do is make that data available to a view.  To do that I need to create a new controller action that queries the cache manager.

    public function myvideosAction()
    {
        $app = $this->getInvokeArg('bootstrap')->getApplication();
        /* @var $app Zend_Application */
        $cm = $app->getBootstrap()->getResource('cachemanager');
        /* @var $cm Zend_Cache_Manager */
        $this->view->youtube = $cm->getCache('video')->load('youtube');
        $this->view->facebook = $cm->getCache('video')->load('facebook');
    }

Then all I need to do in my view is iterate over the data and I'm pretty much good to go.  Because the cache data has been prepopulated my visitors should never have to take the hit of populating the cache and by using the Zend Server Job Queue the task of populating the cache is extremely easy to do.

Little Schrade asks about Flash and Zend Framework

Following closely on the heels of my previous blog posting on how to create a basic Hello World application using Flash RIA with Zend Framework based remoting I have created a video showing how to do exactly what I just did in the blog posting.  Additionally, I have a compressed copy of the workspace I used so you can download it and run it in your own copy of Zend Studio.  You will probably need to change a few settings to make it work in your IDE.  Be forewarned that you will likely also need the Flash Builder 4 plugin installed.  The 60 day trial should be more than enough to let you do that.

Friday Framework Highlight: Zend Framework MVC Request Lifecycle

Dispatch loop

Matthew wrote up an article on modules in Zend_Application and that got me thinking a little bit.  When I have done training for Zend Framework, one of the things that mystifies students to some extent is the whole plugin architecture and where things can go.  There has been several articles written about it, but they tend to use code to describe it.  I was only able to find a small handfull of articles that used some kind of chart to describe what goes on.  Not that that's a problem, but I had found that when I drew out the request lifecycle that it helped the students understand it better.

The chart on the right is a color-coded chart that shows when something is executed and where it is executed from.  This chart is intentionally missing a whole bunch of things for the purpose of simplicity.  If you want a more full explanation of the request lifecycle here is a great PDF that might help.  For an introduction to plugins (with code examples but no diagrams :-) ) go here.

Note: to have a plugin potentially execute in all of the hooks you will need to add it in your bootstrap file, or class, prior to the front controller calling its dispatch() method.

The first hook that is run is the routeStartup().  It is run one time prior to the route being decided.  If you have any kinds of custom routing that needs to be done, this might be the place to do it.

The second hook is the routeShutdown() hook.  If you have module/controller/action based access control, this is not a bad place to put it.  It is the very first time that you will know what the module, controller and action will be.  That way if someone tries to access a certain URL that they shouldn't have access to they will be stopped immediately.  However, if you use a lot of forwards or if you use the action stack a lot to hop around between controllers, modules and actions then this would not be a good place for access control since it is only called once.  The error handler uses the routeShutdown() hook to check if it is handling an error for the current dispatch loop iteration so it can forward the dispatcher to the error controller prior to any other logic being called.

The next hook is the dispatchLoopStartup() hook.  This is called immediately before the dispatch loop is started, and right after routeShutdown().  Functionally, at this point in time, there is no difference between dispatchLoopStartup() and route

After dispatchLoopStartup() is called, we enter the dispatch loop which is handled within a do-while statement, so it will run at least once.  The next hook to run is the plugin preDispatch().  This method will be executed for each and every dispatch loop that runs.  If you have a lot of forwards or use the action stack a lot to cross access control lines, this is a good place to put that control, instead of in routeShutdown() or dispatchLoopStartup().

After that, the execution moves to the dispatcher.  This is where the controller class is loaded and instantiated.  Because this is where the controller is instantiated, it is also where the controller init() method is called.  A new controller object will be created for each and every dispatch loop, whether or not it is iterating over the same controller name.  Therefore, init() will be called once per dispatch loop.

After the controller has been instantiated we move from the dispatcher's dispatch() method into the controller's dispatch() method.  The first thing that happens is that the controller dispatch calls all of the controller helper's preDispatch() method.  The preDispatch() method will only be called if the helper has been either a) referenced, or b) explicitly instantiated.  Therefore, the preDispatch() method in a controller action helper MAY be called.  In a plugin, the preDispatch() method WILL be called.

Once the controller action helper preDispatch() method has been called the controller's individual preDispatch() method is called.  This is used to implement any functionality that is universal to the individual controller.  Setting up contexts is an example of what you can do here.

Then the individual action is called.  This, of course, is where you implement your logic for the individual piece of functionality that this required for this specific module, controller and action combo.

After that, the postDispatch() method is called on the controller.  This is where the View Renderer is called.  If you need to do something either immediately before or immediately after the View Renderer, this is a good place to do it.

The last point of execution for the individual dispatch is the plugin postDispatch().  An example of what is executed here is the Zend_Layout plugin.  This call is handled in the front controller.

Here, the front controller has a decision to make.  It will check to see if there are any outstanding actions to be executed.  If so, it will start the do-while loop over again.  If not, it will exit the loop and call the dispatchLoopShutdown() method on all of the plugins.  In Zend Framework 1.10 there is only one component that uses this, and that is the Wildfire component.

So that is a brief overview of the request lifecycle in Zend Framework MVC applications.  There's nothing new or special here, except that this posting has a picture with funny colors.

Friday Framework Highlight: Creating tickets with Zend_Barcode and Zend_Pdf

So you’re now in charge of some kind of event and people are going to need tickets to get in.  You could do it the old fashion way and actually have people at the door checking the tickets, but that is so 1980’s and also so easy to forge (for the nefarious).  Event organizers are adament that tickets are only used once and so they want to use some kind of barcode reader to ensure that tickets are valid and you are responsible for the web side of this.

An easy way to solve this conundrum is to use a combination of Zend_Pdf and the new Zend_Barcode in your application.  Barcodes are actually surprisingly simple to do, but you still have to make them.  Zend_Barcode makes it simple AND fun.  OK.  I don’t know about fun, but less bad.

The tickets will be on standard paper so people can easily print them out from their regular printer.  We also want them to be look a little nice, so we’ll be adding some graphics.  I love great design, but I suck at it, so nobody complain that the example doesn’t look good.

The first thing we need to do is to set up our script so we’ll just start with the basics.

1
2
3
4
require_once 'Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance()->setFallbackAutoloader(true);
 
$ticketNum = mt_rand(0, PHP_INT_MAX);

 

Obviously, $ticketNum would actually be a generated value, probably not a simple random number since that could be duplicated.  The value for $ticketNum also needs to be valid according to the individual adapter.  Each adapter has one or more validators with the prefix Zend_Validate_Barcode.  In this case a simple number was sufficient for the purposes of testing.

The next thing we need to do is to create the actual barcode image.

1
2
3
4
5
6
7
8
9
10
11
12
$barcodeOptions = array('text' => $ticketNum);
 
$bc = Zend_Barcode::factory(
    'code39',
    'image',
    $barcodeOptions,
    array()
);
/* @var $bc Zend_Barcode */
$res = $bc->draw();
$filename = tempnam('/tmp', 'image').'.png';
imagepng($res, $filename);

 

What we do here is use the factory to create an instance of Zend_Barcode which we then write to the file system.  You don’t need to write it to the file system.  You can keep it in memory.  But from a teaching perspective this is a little easier since you can see it seperately from the PDF document.

Several people have notified me that you do not need to create a temp file (thanks for doing so, BTW).  While I was aware of that, I probably didn’t say it very clearly in the paragraph preceeding this.  The reason I didn’t go that route was so that you could see the actual output of the barcode image file.  If you are going to be using something like this in a real-life application, absolutely use PDF renderer for the barcode instead.  But here I wanted to show it in a way where you could see the image ahead of time.

The next thing we do is create a new PDF document that we’re going to work with and add the page that we’re going to place our content on.

1
2
$pdf = new Zend_Pdf();
$pdf->pages[] = $page = new Zend_Pdf_Page(Zend_Pdf_Page::SIZE_A4);

After that we will place a header in the PDF file.  Here is the header image.

All Access

No, this is not an official Zendcon 2010 All Access Pass.  But it could be…

Now that we have our header image let’s add it to the page.

1
2
$image = Zend_Pdf_Image::imageWithPath('allaccess.png');
$page->drawImage($image, 10, 680, 586, 820);

You might be asking yourself “what are all those funky numbers?”  They are a box that defines the area that the image is going to inhabit.  In this case the start position will be 10×680 (starting from the bottom left of the page) and go to 586×820.  The image is 576 (586-1) x 140 (820-680).

Now having the image attached to the page we can add our barcode image

1
2
3
$image = Zend_Pdf_Image::imageWithPath($filename);
$size = getimagesize($filename);
$page->drawImage($image, 10, 500, $size[0]+10, $size[1]+500);

Same deal as before except that since we don’t know what the image size is going to be we called getimagesize().  The size can be defined, but for clarity I did not do it here.  Your image will look something like this

Barcode image

After that all we need to do is render the PDF file, do something with it, and delete the temporary files.

1
2
3
4
5
6
7
$pdfFilename = tempnam('/tmp', 'pdf').'.pdf';
$pdf->save($pdfFilename);
 
// Do something
 
unlink($filename);
unlink($pdfFilename);

 

And with that we have our PDF-based, scan-able ticket.

Documentation: Zend_Barcode Zend_Pdf

Adapters for Zend_Barcode

  • Code25
  • Code39
  • Ean13
  • Ean2
  • Ean5
  • Ean8
  • Error (Used for handling errors)
  • Identcode
  • Itf14
  • Leitcode
  • Planet
  • Postnet
  • Royalmail
  • Upca
  • Upce

Summary

  1. Create the Zend_Barcode image
  2. Create the Zend_Pdf document
  3. Create the Zend_Pdf page, attaching it to the document
  4. Render
  5. Delete temp files
Web Analytics