Skip to content

Instantly share code, notes, and snippets.

@ashleydw
Created October 31, 2011 13:53
Show Gist options
  • Save ashleydw/1327542 to your computer and use it in GitHub Desktop.
Save ashleydw/1327542 to your computer and use it in GitHub Desktop.
Cascading module directory file structure for Zend Framework
<?php
/**
* Cascading module directory file structure
*
* @version 1
* @author Ashley White, www.needathinkle.com
* @license @license http://creativecommons.org/licenses/by-sa/3.0/
*/
final class Thinkle_Controller_Dispatcher_Cascading extends Zend_Controller_Dispatcher_Standard {
/**
* Class name prefixes
* @see addControllerDirectory
* @see formatClassName
* @var array
*/
protected $_prefixes = array();
/**
* Add multiple directories of controllers for modules. This should really be extended with other functions for unshift_ etc
*
* @param string $path
* @param string $module
* @param string $prefix The prefix for the non default classes
* @return Thinkle_Controller_Dispatcher_Cascading
*/
public function addControllerDirectory($path, $module = null, $prefix = null) {
if (null === $module) {
$module = $this->_defaultModule;
}
$module = (string) $module;
$path = rtrim((string) $path, '/\\');
if(!array_key_exists($module, $this->_controllerDirectory)) {
$this->_controllerDirectory[$module] = array();
}
if(!in_array($path, $this->_controllerDirectory[$module])) {
$this->_controllerDirectory[$module][] = $path;
if($prefix) {
$this->_prefixes[$path] = $prefix;
}
}
return $this;
}
/**
* Gets the controller class as the Standard dispatcher does, but checks the module controller directories for the file
*
* @param Zend_Controller_Request_Abstract $request
* @return type
*/
public function getControllerClass(Zend_Controller_Request_Abstract $request) {
$className = parent::getControllerClass($request);
$module = $request->getModuleName();
$fileSpec = $this->classToFilename($className);
foreach($this->getControllerDirectory($module) as $directory) {
$test = $directory . DIRECTORY_SEPARATOR . $fileSpec;
if(Zend_Loader::isReadable($test)) {
$this->_curDirectory = $directory;
break;
}
}
return $className;
}
/**
* Same as parent, but we loop the dispatchDirectory(ies) testing each
*
* @param Zend_Controller_Request_Abstract $request
* @return type
*/
public function isDispatchable(Zend_Controller_Request_Abstract $request)
{
$className = $this->getControllerClass($request);
if (!$className) {
return false;
}
$finalClass = $className;
if (($this->_defaultModule != $this->_curModule)
|| $this->getParam('prefixDefaultModule'))
{
$finalClass = $this->formatClassName($this->_curModule, $className);
}
if (class_exists($finalClass, false)) {
return true;
}
$fileSpec = $this->classToFilename($className);
$dispatchDir = $this->getDispatchDirectory();
if(!is_array($dispatchDir))
$dispatchDir = array($dispatchDir);
foreach($dispatchDir as $directory) {
$test = $directory . DIRECTORY_SEPARATOR . $fileSpec;
if(Zend_Loader::isReadable($test)) {
return true;
}
}
return false;
}
/**
* Format action class name with prefix
*
* @param string $moduleName Name of the current module
* @param string $className Name of the action class
* @return string Formatted class name
*/
public function formatClassNameWithPrefix($moduleName, $className) {
if(array_key_exists($this->getDispatchDirectory(), $this->_prefixes))
return $this->_prefixes[$this->getDispatchDirectory()].'_'.$this->formatModuleName($moduleName) . '_' . $className;
return $this->formatModuleName($moduleName) . '_' . $className;
}
/**
* Same as the Standard function, but we check for prefixes.
* Only difference is: formatClassNameWithPrefix() not formatClassName();
*
* @param string $className
* @return string Class name loaded
* @throws Zend_Controller_Dispatcher_Exception if class not loaded
*/
public function loadClass($className)
{
$finalClass = $className;
if (($this->_defaultModule != $this->_curModule)
|| $this->getParam('prefixDefaultModule'))
{
$finalClass = $this->formatClassNameWithPrefix($this->_curModule, $className);
}
if (class_exists($finalClass, false)) {
return $finalClass;
}
$dispatchDir = $this->getDispatchDirectory();
$loadFile = $dispatchDir . DIRECTORY_SEPARATOR . $this->classToFilename($className);
if (Zend_Loader::isReadable($loadFile)) {
include_once $loadFile;
} else {
require_once 'Zend/Controller/Dispatcher/Exception.php';
throw new Zend_Controller_Dispatcher_Exception('Cannot load controller class "' . $className . '" from file "' . $loadFile . "'");
}
if (!class_exists($finalClass, false)) {
require_once 'Zend/Controller/Dispatcher/Exception.php';
throw new Zend_Controller_Dispatcher_Exception('Invalid controller class ("' . $finalClass . '")');
}
return $finalClass;
}
}
<?php
/**
* To go with the cascading file directory structure disaptcher
*
* @version 1
* @author Ashley White, www.needathinkle.com
* @license @license http://creativecommons.org/licenses/by-sa/3.0/
*/
class Thinkle_Application_Resource_Modules extends Zend_Application_Resource_Modules {
public function init()
{
$bootstraps = array();
$bootstrap = $this->getBootstrap();
$bootstrap->bootstrap('FrontController');
$front = $bootstrap->getResource('FrontController');
$modules = $front->getControllerDirectory();
$default = $front->getDefaultModule();
$curBootstrapClass = get_class($bootstrap);
foreach ($modules as $module => $moduleDirectory) {
//Thinkle:
//Difference to standard resource loader, here we loop the directories
$bootstrapClass = $this->_formatModuleName($module) . '_Bootstrap';
foreach($moduleDirectory as $controllerDirectory) {
if (!class_exists($bootstrapClass, false)) {
$bootstrapPath = dirname($controllerDirectory) . '/Bootstrap.php';
if (file_exists($bootstrapPath)) {
$eMsgTpl = 'Bootstrap file found for module "%s" but bootstrap class "%s" not found';
include_once $bootstrapPath;
if (($default != $module)
&& !class_exists($bootstrapClass, false)
) {
throw new Zend_Application_Resource_Exception(sprintf(
$eMsgTpl, $module, $bootstrapClass
));
} elseif ($default == $module) {
if (!class_exists($bootstrapClass, false)) {
$bootstrapClass = 'Bootstrap';
if (!class_exists($bootstrapClass, false)) {
throw new Zend_Application_Resource_Exception(sprintf(
$eMsgTpl, $module, $bootstrapClass
));
}
}
}
} else {
continue;
}
}
//Thinkle:
//Move inside of the foreach to avoid looking for bootstraps that don't exist
if ($bootstrapClass == $curBootstrapClass) {
// If the found bootstrap class matches the one calling this
// resource, don't re-execute.
continue;
}
$bootstraps[$module] = $bootstrapClass;
}
}
return $this->_bootstraps = $this->bootstrapBootstraps($bootstraps);
}
}
<?php
/**
* To go with the cascading file directory structure disaptcher
*
* @version 1
* @author Ashley White, www.needathinkle.com
* @license @license http://creativecommons.org/licenses/by-sa/3.0/
*/
class Thinkle_Controller_Action_Helper_ViewRenderer extends Zend_Controller_Action_Helper_ViewRenderer {
/**
* For the cascading file structure, we check the module directories
*
* @return type
*/
public function getModuleDirectory()
{
$module = $this->getModule();
$moduleDir = $this->getFrontController()->getControllerDirectory($module);
if(is_array($moduleDir)) {
foreach($moduleDir as $directory) {
if(is_dir($directory)) {
$moduleDir = $directory;
break;
}
}
}
if ((null === $moduleDir)) {
/**
* @see Zend_Controller_Action_Exception
*/
require_once 'Zend/Controller/Action/Exception.php';
throw new Zend_Controller_Action_Exception('ViewRenderer cannot locate module directory for module "' . $module . '"');
}
$this->_moduleDir = dirname($moduleDir);
return $this->_moduleDir;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment