Wednesday, March 2, 2011

Zend Self Contained Modules

So I've been playing around with Zend lately; and since my last web project was back in college, I was surprised how much development has happened! Where was I when all of this happened? Back then there were no Joomla nor Drupal, only "hand-woven" CMS! Anyways, I'm here back to square one and trying to relearn everything; luckily/unluckily, I picked Zend as my framework of choice.

Compared to Yii, I was surprised that modules in Zend are very hard to implement. What I wanted was a way to initialize my resources using a module specific configuration file; and a way to run code specific for a module (i.e. add actions to an action stack). Thanks to guidelines from Matthew[1] and module configurators from Padraic[2]: I implemented my own module configurator.

Here is my directory structure:
./
application/
 configs/
  application.ini
 layouts/
modules/
 default/
  configs/
   module.ini
  controllers/
  forms/
  layouts/
  library/
 Configurator/
  Layout.php
  models/
  views/
  helpers/
  scripts/
  Bootstrap.php
 news/
  Bootstrap.php
  docs/
  library/
MyApp/
 Controller/
 Plugin/
  ModuleConfigurator.php
 public/
 tests/
Similar from [2], I registered a plugin at Bootstrap.php:
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
 protected function _initControllerPlugins()
 {
  // Register module configurator so that it will run first and initialize the modules
  $frontController = Zend_Controller_Front::getInstance();
  $frontController->registerPlugin(new MyApp_Controller_Plugin_ModuleConfigurator(),1);
 }
}
The Module Configurator does the following:
class MyApp_Controller_Plugin_ModuleConfigurator extends Zend_Controller_Plugin_Abstract
{
  protected $_view;
  protected function moduleInit($_bootstrap, $_config)
  {
   // Run resource configurators

   $resources = array_keys($_config->resources->toArray());
   foreach ($resources as $resourceName)
   {
    $options = $_config->resources->$resourceName;
    $configuratorClass = $this->_view->originalModule . '_Library_Configurator_' . ucfirst($resourceName);
    $configurator = new $configuratorClass($options);
    $configurator->setBootstrap($_bootstrap);
    $configurator->init();
   }

  // Attach plugins
  $plugins = $_config->plugins->toArray();
  foreach ($plugins as $pluginName)
  {
   $frontController = Zend_Controller_Front::getInstance();
   $frontController->registerPlugin(new $pluginName());
  }
 }

 public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
 {
  // Get view
  $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
  $viewRenderer->init();
  $view = $viewRenderer->view;
  $this->_view = $view;
  // set up common variables for the view
  $view->originalModule = $request->getModuleName();
  $view->originalController = $request->getControllerName();
  $view->originalAction = $request->getActionName();
  $front = Zend_Controller_Front::getInstance();
  $bootstrap = $front->getParam('bootstrap');
  $moduleName = $request->getModuleName();
  $moduleDirectory = Zend_Controller_Front::getInstance()->getModuleDirectory($moduleName);
  $configPath = $moduleDirectory . DIRECTORY_SEPARATOR . 'configs' . DIRECTORY_SEPARATOR . 'module.ini';
  if (file_exists($configPath))
  {
   if (!is_readable($configPath))
   {
    throw new Exception('modules.ini is not readable for module "' . $moduleName . '"');
   }
   $config = new Zend_Config_Ini($configPath, $bootstrap->getEnvironment());
   $this->moduleInit($bootstrap, $config);
  }
 }
}
So basically, what it does is that it reads the module.ini of the module, then init() every resource. Also, attach plugins if given in the module.ini. So a resource initiliazor is just a realization of a Zend_Application_Resource_ResourceAbstract, like below:
class News_Library_Configurator_Layout extends Zend_Application_Resource_ResourceAbstract
{
 public function init()
 {
  $layout = $this->getBootstrap()->getResource('layout');
  $layout->setOptions($this->getOptions());
 } 
}
Here is an example of a module.ini:
[production]
resources.layout.layout = "layout"
resources.layout.layoutPath = APPLICATION_PATH "/modules/Core/layouts/scripts"
plugins[] = "MyApp_Controller_Plugin_ViewSetup"
Each module has a specific library by autoloading it from a module bootstrap, like below:
class News_Bootstrap extends Zend_Application_Module_Bootstrap
{
 protected function _initLibraryAutoloader()
 {
  return $this->getResourceLoader()->addResourceType('library','library','Library');
 }
}
If all of these seems advanced to you, check out the references below, these guys explained these stuffs in great detail and I don't want to steal their spotlight (actually I'm just too lazy!)

 If I miss something, just tell me! :)

 References:
 [1] http://www.weierophinney.net/matthew/archives/234-Module-Bootstraps-in-Zend-Framework-Dos-and-Donts.html
[2] http://blog.astrumfutura.com/2009/09/self-contained-reusable-zend-framework-modules-with-standardised-configurators/
[3] http://framework.zend.com/wiki/pages/viewpage.action?pageId=16023853
[4] http://blog.vandenbos.org/2009/07/07/zend-framework-module-config/
[5] http://binarykitten.me.uk/dev/zend-framework/177-active-module-based-config-with-zend-framework.html

Wednesday, January 19, 2011

Setting up default routes and nameservers on Ubuntu 10.x

Usually I set up dns entries using the network connection gui but somehow I found myself needing to manually set it up. Also, there are certain routes that I want to be added by default on the kernel routing table. Now it's been ages since I tried to do this and it surprised me how a lot of things have changed. Learning is really a continuous and painful but fun process :)

First I needed to setup my default routes; after googling and reading man pages [1], I found out that you can add scripts that can be ran during bringing up/down of an interface. So what I did was I added a post-up command on the interface that needed static routes. Now the scripts that are run are stored at /etc/network/[event], where [event] can be if-down.d, if-post-down.d, if-up.d, if-post-up.d. I named my script routes.sh; below is a sample interface entry:

# The primary network interface
auto eth1
iface eth1 inet static
address x.x.x.x
netmask x.x.x.x
network x.x.x.x
broadcast x.x.x.x
gateway x.x.x.x
# dns-* options are implemented by the resolvconf package, if installed
dns-nameservers x.x.x.x x.x.x.x
dns-search artesyncp.com emrsn.org
# Static routes
post-up /etc/network/if-up.d/routes.sh

routes.sh:
#!/bin/bash

ip route flush dev eth1
ip route add to x.x.x.x/24 dev eth1
ip route add to x.x.x.x/16 via x.x.x.x dev eth1
ip route add to x.x.x.x/16 via x.x.x.x dev eth1

Of course x.x.x.x should be replaced by a valid IP address.

The problem was there is also an interface that retrieves its address through a DHCP server but the DNS entries that it sets are not complete. I can't touch the DHCP server and /etc/resolv.conf always gets overwritten during network reboots. Because of this, I needed to add option modifiers to my /etc/dhcp3/dhclient.conf. Below are what i added:

prepend domain-search "something.com", "another.something.com";
prepend domain-name-servers x.x.x.x, x.x.x.x;

I used prepend because I want to use my static name servers before the one that the DHCP server gives.

References

[1] man 5 interfaces
[2] man 5 dhclient.conf
[3] man 5 dhcp-options