Preview of the next version of ZF 1.10
http://www.mikaelkael.fr/doczf/1.10/en/
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"?>
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; $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.
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 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>