SVN Externals

The official SVN online manual has an article on this, please read it. I won’t let myself off the hook that easy and will give you a brief example on how to use them. Let’s say you have your own MVC project based on Zend Framework. You should off course store your app in SVN, but using the following command you can dynamically link the framework to you library folder. Please consider the following command knowing that our present working directory is ‘library’:

svn propset 'svn:externals' 'Zend http://framework.zend.com/svn/framework/standard/tags/release-1.7.6/library/Zend' .

This command wil link the Zend library and after performing an svn up your working copy will also include version 1.7.6 of Zend Framework. Please also consider commiting, because other checkouts need to sync the framework too. Maybe I should explain the different fragments of the command too:

  • svn propset: this part will set a property
  • svn:externals: the property we’re gonna set is called ’svn:externals’
  • Zend: we will create a folder called ‘Zend’ which is a checkout of a specific version
  • The URL. Quite self-explanatory
  • The dot (.): the dot specifies that the checkout will happen in the current folder. You can also put a directory name in there

Happy versioning!

Zend_Acl & Zend_Navigation

Setting up a simple working example of Acl & Navigation in Zend Framework 1.9.x as demonstrated by jscherer26.
Enjoy

models/Acl.php

< ?php class Model_Acl extends Zend_Acl { public function __construct() { // define Roles $this->addRole(new Zend_Acl_Role('guest')); // not authenicated
$this->addRole(new Zend_Acl_Role('member'), 'guest'); // authenticated as member inherit guest privilages
$this->addRole(new Zend_Acl_Role('admin'), 'member'); // authenticated as admin inherit member privilages

// define Resources
$this->add(new Zend_Acl_Resource('error'));
$this->add(new Zend_Acl_Resource('index'));
$this->add(new Zend_Acl_Resource('authentication'));
$this->add(new Zend_Acl_Resource('activity'));

// assign privileges
$this->allow('guest', array('index','error'));
$this->allow('guest', 'authentication', array('index','signin'));

$this->allow('member', 'authentication', array('index','signout'));
$this->deny( 'member', 'authentication', 'signin');
$this->allow('member', 'activity', array('index','list')); // member has list privilages for resource activity

$this->allow('admin', 'activity'); // admin has all privileges for resource activity
}
}

plugins/Authenticated.php

< ?php class Plugin_Authenticated extends Zend_Controller_Plugin_Abstract { private $_acl = null; private $_auth = null; public function __construct(Zend_Acl $acl, Zend_Auth $auth) { $this->_acl = $acl;
$this->_auth = $auth;
}

public function preDispatch(Zend_Controller_Request_Abstract $request)
{
$resource = $request->getControllerName();
$action = $request->getActionName();

$role .= $this->_auth->getStorage()->read()->role;
if(!$this->_acl->isAllowed($role, $resource, $action)) {
$request->setControllerName('authentication')
->setActionName('notauthorized');
}
}
}

Bootstrap.php

< ?php class Bootstrap extends Zend_Application_Bootstrap_Bootstrap { private $_acl = null; private $_auth = null; protected function _initAutoload() { $modelLoader = new Zend_Application_Module_Autoloader(array( 'namespace' => '',
'basePath' => APPLICATION_PATH));

$this->_acl = new Model_Acl;
$this->_auth = Zend_Auth::getInstance();
if(!$this->_auth->hasIdentity()) {$this->_auth->getStorage()->read()->role = 'guest';}

$fc = Zend_Controller_Front::getInstance();
$fc->registerPlugin(new Plugin_Authenticated($this->_acl, $this->_auth));

return $modelLoader;
}

function _initViewHelpers()
{
$this->bootstrap('layout');
$layout = $this->getResource('layout');
$view = $layout->getView();

$config = new Zend_Config_Ini(APPLICATION_PATH .'/configs/application.ini', APPLICATION_ENV);

$view->doctype('HTML4_STRICT');
$view->headMeta()->appendHttpEquiv('Content-Type', 'text/html; charset=UTF-8')
->appendHttpEquiv('Content-Language', 'en-US')
->appendName('keywords', $config->head->meta->keywords)
->appendName('description', $config->head->meta->description);

$view->headLink()->appendStylesheet($config->head->css->site)
->appendStylesheet($config->head->css->menu)
->appendStylesheet($config->head->css->form)
->appendStylesheet($config->head->css->view);

$view->headTitle()->setSeparator(' - ');
$view->headTitle($config->head->title);

}

function _initNavigation()
{
$this->bootstrap('layout');
$layout = $this->getResource('layout');
$view = $layout->getView();

$navConfig = new Zend_Config_Xml(APPLICATION_PATH . '/configs/navigation.xml', 'nav');
$navigation = new Zend_Navigation($navConfig);

$view->navigation($navigation)->setAcl($this->_acl)
->setRole($this->_auth->getStorage()->read()->role);

}

}

configs/Navigation.xml

< ?xml version="1.0" encoding="UTF-8"?>


InnoDB Performance Monitoring with innotop

Manually extracting relevant information from repeated incantations of SHOW ENGINE INNODB STATUS while trying to figure out what InnoDB is doing is not only error prone, it’s just plain hard to do. And since MySQL doesn’t expose the data you really want in an INFORMATION_SCHEMA table (yet?), the option is use an external program to help: innotop.

As luck would have it, in 2006 Baron Schwartz announced his innotop: the most advanced MySQL and InnoDB monitor. And in the time since then it has definitely evolved into a suitable replacement for the 10-year-old mytop.

Install and Use

Edit: Updated version 1.7.x located at http://code.google.com/p/innotop/updates/list

You can download the latest version of innotop from Sourceforge. As of this writing, the latest version if 1.6. Once you’ve grabbed it, installation is like any CPAN module (since innotop is written in Perl).

$ tar -zxf innotop-1.6.0.tar.gz
$ cd innotop-1.6.0
$ perl Makefile.PL
Checking if your kit is complete...
Looks good
Writing Makefile for innotop
$ sudo make install
cp InnoDBParser.pm blib/lib/InnoDBParser.pm
cp innotop blib/script/innotop
/usr/bin/perl "-MExtUtils::MY" -e "MY->fixin(shift)" blib/script/innotop
Manifying blib/man1/innotop.1p
Manifying blib/man3/InnoDBParser.3pm
Installing /usr/local/share/perl/5.10.0/InnoDBParser.pm
Installing /usr/local/man/man1/innotop.1p
Installing /usr/local/man/man3/InnoDBParser.3pm
Installing /usr/local/bin/innotop
Writing /usr/local/lib/perl/5.10.0/auto/innotop/.packlist
Appending installation info to /usr/local/lib/perl/5.10.0/perllocal.pod

There is also a Debian package available from Sourceforge that should work on Debian and Ubuntu.

The Basics

Once innotop is installed, you can simply run innotop. The first time you run it, you’ll be prompted to create a new database connection and name it. With innotop you can have any number of saved named database connections that you can then refer to by name to quickly connect to a server without re-specifying the hostname, username, password, and so on.

If you have a MySQL server running on the same host where you’re testing innotop, you might call the first connection “localhost”.

Enter a name: localhost

Next you’re prompted to enter a “DSN string” which is what Perl DBI uses to represent the database connection parameters.

Typical DSN strings look like
   DBI:mysql:;host=hostname;port=port
The db and port are optional and can usually be omitted.
If you specify 'mysql_read_default_group=mysql' many options can be read
from your mysql options files (~/.my.cnf, /etc/my.cnf).

Enter a DSN string: DBI:mysql:;host=localhost

Then you’re prompted for an optional table name that innotop can use for deadlock detection information, along with a username and password:

Optional: enter a table (must not exist) to use when resetting InnoDB deadlock information: blahtable
Do you want to specify a username for localhost?: y
Do you want to specify a password for localhost?: y
Enter username for localhost: root
Enter password for 'root' on localhost: *****
Save password in plain text in the config file?: y

With all that information, innotop will save a configuration file as ~/.innotop/innotop.ini connect to the server and start showing some high-level statistics that may look something like this:

________________________________ InnoDB Row Operations ________________________________
CXN   Ins         Upd        Read        Del        Ins/Sec  Upd/Sec  Read/Sec  Del/Sec
root  1717192746  141503339  1935029792  146254835   166.42    97.45    257.37     0.00

______________________ Row Operation Misc _______________________
CXN   Queries Queued  Queries Inside  Rd Views  Main Thread State
root               0               0         1  flushing log

_____________________________________ InnoDB Semaphores ______________________________________
CXN   Waits    Spins      Rounds     RW Waits  RW Spins  Sh Waits  Sh Spins  Signals   ResCnt
root  2475263  104367748  344363574   1572553   3573154   3082885   5833069  11083664  7957967

_______________________________ InnoDB Wait Array _______________________________
CXN  Thread  Time  File  Line  Type  Readers  Lck Var  Waiters  Waiting?  Ending?

If so, your innotop installation is working correctly.

Monitoring Modes

Like mytop, innotop can be switched into a variety of monitoring modes based on what you want to focus on. By default, you’ll see mode “R” or InnoDB Row Operations. As its name implies, this mode summarizes row-level statistics such as rows inserted or deleted per second. You can press “?” to see a list of all the monitoring modes and the single letter keystroke used to switch to each of them.

Here are some of the other useful modes:

B: InnoDB Buffers

This view presents buffer pool information, page statistics, insert buffer information, and metrics for adaptive hash indexes. I often check this mode to see what the buffer pool hit rate is as well as how many page reads and writes per second the server is doing.

C: Command Summary

The command summary shows the breakdown of which commands MySQL has been handling. They’re listed from most frequent to least frequent and the display shows total counts for each as well as a percentage. Two sets of each numbers are presented. The first looks at numbers since the server was started while the second only shows numbers from the last polling cycle.

D: InnoDB Deadlocks

If you’re seeing deadlocks with some regularity, this display pulls together the necessary information to diagnose what’s happening, including username, hostname, query, time, victim, and so on.

F: InnoDB Foreign Key Errors

While I’ve never needed it, the foreign key mode will present foreign key problems in a useful format.

I: InnoDB File I/O

The File I/O mode is very useful when looking at some performance problems. You can see at a glance how many I/O threads have pending I/O requests as well as the total number of reads and writes (overall and per second) as well as the number of bytes read and written. Finally, the I/O mode summarizes the InnoDB log file checkpoint statistics and overall I/O as well.

L: Locks

When you suspect that you’re running into lock contention, the Locks view will help you see which clients/queries are involved and how often it is happening.

M: Master/Slave Replication Status

This view presents a summary of the slaving information for both the SQL and IO threads on a slave. You can see the binary log file being used, replication lag, log file size, and so on.

Q: Query List

The Query List view shows the queries that MySQL is processing. And like in mytop, you can select a specific query to view the full query and run it through the EXPLAIN command.

R: InnoDB Row Operations and Semaphores

As noted earlier, this view provides statistics about rows created, updated, deleted, read as well as InnoDB semaphores (internal locking) and the Wait Array.

T: InnoDB Transactions

Finally, the Transactions mode shows the transaction state of all the threads (at least those connected to a client) running inside of MySQL. You can see who owns the transaction, which host they are connected from, the transaction status, running time, and the query that’s currently being run inside the transaction.

That’s Not All

Believe it or not, innotop can do more than that. Spend a bit of time reading the biult-in help (hit the “?” key in any of the modes) and read the innotop manual page. You’ll find that innotop can talk to multiple servers and maintain lists of server groups, since we often deploy MySQL in multi-machine clusters. In other words, innotop doesn’t assume that you’re only going to watch a single server like mytop did.

The more I use innotop, the less inclined I am to continue maintaining and updating mytop, even though I use it almost daily at work. If you’ve been an mytop user and depend on InnoDB, I highly recommend giving innotop a try.

Jeremy Zawodny is a software engineer at Craigslist where he works on MySQL, Search, and various back-end infrastructure. He’s also the co-author of “High Performance MySQL” and blogs at http://jeremy.zawodny.com/blog/

Custom Flash Messenger for Zend Framework

Mohammed Alsharaf wrote a good working example; FlashMessenger is an action helper in Zend Framework used to pass messages for the users on the next request.
Thats all good, but what about presenting the messages for the users? how to store different type of messages, System, Error, or Success messages?
To achive my goal, i have built two classes, one is an action helper and the other one is view helper.

Action Helper Class

This action helper class act as a factory and singleton pattern. It store the messages in different namespaces of the flash messenger. You can also, retrieve a namesapce by its key to add another message.


/**
* @author Mohammed Alsharaf
* @category Core
* @package Core_ActionHelper
* @copyright Copyright (c) 2008-2009 Mohammed Alsharaf.
* @license http://framework.zend.com/license/new-bsd
*/
class Core_ActionHelper_Messenger extends Zend_Controller_Action_Helper_Abstract
{
protected $_flashMessenger = null;

public function messenger($name='error',$message=null) {
if($name == 'error' &amp;amp; $message === null) {
return $this;
}
if(!isset($this->_flashMessenger[$name])) {
$this->_flashMessenger[$name] = $this->getActionController() ->getHelper('FlashMessenger') ->setNamespace($name.'_message');
}
if($message !== null) {
$this->_flashMessenger[$name]->addMessage($message);
}
return $this->_flashMessenger[$name];
}

public function direct($name='error',$message=null) {
return $this->messenger($name,$message);
}
}

View Helper Class

The view helper loops all the namespaces of the flash messenger and render the messages using the htmlList view helper

/**
* @author Mohammed Alsharaf
* @category Core
* @package Core_ViewHelper
* @copyright Copyright (c) 2008-2009 Mohammed Alsharaf.
* @license http://framework.zend.com/license/new-bsd
*/
class Core_ViewHelper_Messenger extends Zend_View_Helper_Abstract
{
protected $_messageKeys = array(
'msg_message',
'error_message',
'info_message',
'success_message',
'warning_message',
);

public function messenger()
{
foreach($this->_messageKeys as $messageKey) {
if($messages = $this->_getMessages($messageKey)) {
echo $this->_renderMessage($messages,$messageKey);
}
}
}

protected function _getMessages($messageKey)
{
$result = array();
$flashMessenger = Zend_Controller_Action_HelperBroker::getStaticHelper('FlashMessenger');
$flashMessenger->setNamespace($messageKey);

if($flashMessenger->hasMessages()) {
$result = $flashMessenger->getMessages();
}

// check view object
if(isset($this->view->$messageKey)) {
array_push($result, $this->view->$messageKey);
}

//add any messages from this request
if ($flashMessenger->hasCurrentMessages()) {
$result = array_merge($result,
$flashMessenger->getCurrentMessages()
);
//we don’t need to display them twice.
$flashMessenger->clearCurrentMessages();
}
return $result;
}

protected function _renderMessage($message, $name)
{
if(!is_array($message)) {
$message = array($message);
}
return $this->view->htmlList($message, false, array('class'=>$name), true);
}
}

Usage:

In your controller to add a message for the next request

// option one
$this->_helper->messenger('success',"Your message is here.");
// another option to add message
$this->_helper->messenger('success')->addMessage('Your message is here.');

To add a message in the current view:

$this->view->info_message = 'stiky message for the current view';

In your layout file add the following, so if there are messages to view, the helper will print them.

<div id="messages"><?php $this->messenger(); ?></div>

Update:
Carlton Gibson over at noumenal also have a very nice tutorial and view helper that you might want to look at.
http://blog.noumenal.co.uk/2009/08/using-zend-framework-flashmessenger.html

Cloud computing with PHP – Using Amazon EC2 with the Zend Framework

Doug Tidwell is a senior software engineer in IBM’s Emerging Technology group. He just wrote a two part article on Moving data into and out of the cloud with the Zend Framework and Using virtual machines with the Zend Framework.

Summary: The Zend Framework contains several classes that make using cloud-based storage services easy.

Part 1 and Part 2.

Cloud computing with PHP, Part 2: Using Amazon EC2 with the Zend Framework

Converting your Zend Framework MVC application into an XML webservice using one single plugin

Thijs Feryn writes an excellent article on how to convert your entire MVC app or one or more controllers into a XML service. I actually have this one in a production environment and it works like a charm.

That’s right folks, in this blog post I’ll show you how you can convert your entire MVC application into a REST-style XML webservice. And I’m not talking about refactoring tons of code … NO, we’ll plug this option in without changing a single thing to your action controllers.

This post will contain a detailed description of the concepts used. The source could of the plugin is also shown and finally how it will look like when using it.

The concept

Thanks to those lovely hooks in Zend Framework you can simply intervene in nearly every aspect of the MVC workflow. The image below shows you the workflow.

Zend Controller Basics

Zend Controller workflow The hooks I’m talking about are actually just methods that are called by the plugin broker system. They already exist as empty methods in the Zend_Controller_Plugin_Abstract which we inherit from. So we override them in order to hook into the MVC flow.

Alle information on Zend Framework plugins can be found in the reference pages. A lot of you Zend Framework experts will now say: “why don’t you just use the context switching action helper?”. I could have done that, but this requires modifying your application. This post is about plugging it in, remember?

How am I doing it then? Well … I hook into the workflow at “post dispatch” time, this means that de dispatcher has already processed al controllers and is about to send all about back to the browser by using a Zend Controller Response object.

Before the front controller sends the output back to the browser, we empty the output’s body and add our own content. This content is retrieved from the view object which would normally parse and render the view object. The view has a set of properties which are set by the controllers. I use the reflection API to get a hold of all these items. Finally I serialize them and output it all.

The plugin


<?php
/**
* My_Plugin_Xml component
* Turns an Zend Framework MVC website into an XML webservice
*/
/**
* My_Plugin_Xml class
*
* @author Thijs Feryn <[email protected]>
*/
class My_Plugin_Xml extends Zend_Controller_Plugin_Abstract
{
/**
* Stores the front controller
*
* @var Zend_Controller_Front
*/
private $_front;
/**
* Stores the XML output in DOMDocument format
*
* @var DOMDocument
*/
private $_xml;
/**
* Class constructor
*/
public function __construct()
{
$this->_front = Zend_Controller_Front::getInstance();
$layout = Zend_Layout::getMvcInstance();
$layout->disableLayout();
}
/**
* Build DOMDocument to convert output to XML
*
* @param mixed $return
* @param Exception $exception
* @return string
*/
private function _getXML($return = null,Exception $exception = null)
{
$this->_xml = new DOMDocument('1.0', 'UTF-8');
$this->_xml->formatOutput = true;

$responseNode = $this->_xml->createElement('response');

$exceptionNode = $this->_xml->createElement('exception');
if(null !== $exception && $exception == instanceof( Exception ){
$exceptionNode->appendChild(
$this->_xml->createElement('message',
$exception->getMessage()
)
);
$exceptionNode->appendChild(
$this->_xml->createElement('code',
$exception->getCode()
)
);
$exceptionNode->appendChild(
$this->_xml->createElement('type',
get_class($exception)
)
);
}

$responseNode->appendChild($exceptionNode);
if(null !== $return){
$responseNode->appendChild(
$this->_serialize('return',$return)
);
} else {
$responseNode->appendChild(
$this->_xml->createElement('return')
);
}

$this->_xml->appendChild($responseNode);
return $this->_xml->saveXML();
}
/**
* Modify the HTTP response object
* Remove the HTML body, replace with XML and change the content-type
*
* @param mixed $return
* @param Exception $exception
*/
private function _setResponse($return = false,Exception $exception = null)
{
$this->getResponse()->setHeader('Content-Type','text/xml; charset=UTF-8');
$this->getResponse()->clearBody();
$this->getResponse()->setBody(
$this->_getXML($return,$exception)
);
}
/**
* Serialize a mixed value to XML in DOMElement format
* This method can be used recursively in case of objects and arrays
*
* @param string $name
* @param mixed $value
* @return DOMElement
*/
private function _serialize($name,$value)
{
if(is_array($value)){
$element = $this->_xml->createElement($name);
foreach ($value as $k=>$v){
if(is_numeric($k)){
$k = 'item';
}
$element->appendChild($this->_serialize($k,$v));
}
} elseif(is_object($value)){
$element = $this->_xml->createElement($name);
$reflection = new ReflectionObject($value);
$properties = $reflection->getProperties();
foreach ($properties as $property){
if($property->isPublic()){
$element->appendChild(
$this->_serialize(
$property->getName(),
$property->getValue($value)
)
);
}
}
}else{
$element = $this->_xml->createElement(
$name,
(string)$value
);
}
return $element;
}
/**
* preDispatch hook that retrieves if an Exception was thrown in the application
* If an exception is thrown, the exception is passed to the exception part of the XML output and script execution is terminated
*
* @param Zend_Controller_Request_Abstract $request
*/
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
if($this->getResponse()->isException()){
$exArray = $this->getResponse()->getException();
$this->_setResponse(null,$exArray[0]);
$this->getResponse()->sendResponse();
exit();
}
}
/**
* postDispatch hook that serializes the view object to XML by modifying the HTTP response
* If no exception was thrown script execution continues and the postDispatch method will be called
*
* @param Zend_Controller_Request_Abstract $request
*/
public function postDispatch(Zend_Controller_Request_Abstract $request)
{
$view = Zend_Controller_Action_HelperBroker::getExistingHelper('ViewRenderer')->view;
$this->_setResponse($view);
}
}

The plugin registration

To activate the plugin, just call the registerPlugin method from the front controller. In my case this is done in my Initializer class which is allready a plugin.

$this->_front->registerPlugin(new My_Plugin_Xml());

The app

This application has one single controller which is the IndexController. There are 2 actions:

* IndexAction: the default action which assigns an object to the view
* ExceptionAction: an action which throws an exception

Pretty simple, pretty basic, but mind the exception: my plugin can catch it by hooking into the flow at preDispatch time. At that time we can already determine if the response contains an exception. Luckily, in this stage the front controller hasn’t yet sent additional error output to the view.

<?php
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$obj = new stdClass();
$obj2 = new stdClass();
$obj2->c = 'xyz';
$obj2->d = '123';
$obj->a = 'abc';
$obj->b = $obj2;
$this->view->content= $obj;
}
public function exceptionAction()
{
throw new Exception('It all goes wrong!');
}
}

The output

indexAction

<?xml version="1.0" encoding="UTF-8"?>
<response>
<exception/>
<return>
<content>
<a>abc</a>
<b>
<c>xyz</c>
<d>123</d>
</b>
</content>
</return>
</response>

exceptionAction

<?xml version="1.0" encoding="UTF-8"?>
<response>
<exception>
<message>It all goes wrong!</message>
<code>0</code>
<type>Exception</type>
</exception>
<return/>
</response>