我想創建一個用於ACL管理的Zend控制器,所以我的問題是:如何在Zend應用程序中獲取所有模塊名稱,控件名稱和操作名稱以構建ACL控制?從Zend Framework應用程序獲取所有模塊,控制器和操作
我使用Zend_Navigation,如果資源不存在於您的ACL中Zend_Navigation被拋出異常。我想用數據庫來拒絕和允許訪問。所以我必須先建立數據庫。如果我必須手動做到這一點,那就很痛苦。
我想創建一個用於ACL管理的Zend控制器,所以我的問題是:如何在Zend應用程序中獲取所有模塊名稱,控件名稱和操作名稱以構建ACL控制?從Zend Framework應用程序獲取所有模塊,控制器和操作
我使用Zend_Navigation,如果資源不存在於您的ACL中Zend_Navigation被拋出異常。我想用數據庫來拒絕和允許訪問。所以我必須先建立數據庫。如果我必須手動做到這一點,那就很痛苦。
我創建了一個功能,可以讓所有的動作,控制器和模塊從zend的應用。那就是:
$module_dir = substr(str_replace("\\","/",$this->getFrontController()->getModuleDirectory()),0,strrpos(str_replace("\\","/",$this->getFrontController()->getModuleDirectory()),'/'));
$temp = array_diff(scandir($module_dir), Array(".", "..", ".svn"));
$modules = array();
$controller_directorys = array();
foreach ($temp as $module) {
if (is_dir($module_dir . "/" . $module)) {
array_push($modules,$module);
array_push($controller_directorys, str_replace("\\","/",$this->getFrontController()->getControllerDirectory($module)));
}
}
foreach ($controller_directorys as $dir) {
foreach (scandir($dir) as $dirstructure) {
if (is_file($dir . "/" . $dirstructure)) {
if (strstr($dirstructure,"Controller.php") != false) {
include_once($dir . "/" . $dirstructure);
}
}
}
}
$default_module = $this->getFrontController()->getDefaultModule();
$db_structure = array();
foreach(get_declared_classes() as $c){
if(is_subclass_of($c, 'Zend_Controller_Action')){
$functions = array();
foreach (get_class_methods($c) as $f) {
if (strstr($f,"Action") != false) {
array_push($functions,substr($f,0,strpos($f,"Action")));
}
}
$c = strtolower(substr($c,0,strpos($c,"Controller")));
if (strstr($c,"_") != false) {
$db_structure[substr($c,0,strpos($c,"_"))][substr($c,strpos($c,"_") + 1)] = $functions;
}else{
$db_structure[$default_module][$c] = $functions;
}
}
}
}
我不認爲Zend有這個解決方案。你將不得不自己做...
一種方式做到這一點,是列出所有類別,並檢查類擴展(例如)將依靠Zend_Controller_Action類...
檢查的PHP函數get_declared_classes和is_subclass_of
foreach(get_declared_classes() as $c){
if(is_subclass_of($c, 'Zend_Controller_Action')){
...
}
}
這可能是一個老問題,但是這是我正在做這個...
$front = $this->getFrontController();
$acl = array();
foreach ($front->getControllerDirectory() as $module => $path) {
foreach (scandir($path) as $file) {
if (strstr($file, "Controller.php") !== false) {
include_once $path . DIRECTORY_SEPARATOR . $file;
foreach (get_declared_classes() as $class) {
if (is_subclass_of($class, 'Zend_Controller_Action')) {
$controller = strtolower(substr($class, 0, strpos($class, "Controller")));
$actions = array();
foreach (get_class_methods($class) as $action) {
if (strstr($action, "Action") !== false) {
$actions[] = $action;
}
}
}
}
$acl[$module][$controller] = $actions;
}
}
}
我居然發現有一個容易獲得的反射參照的是最好的方式遞歸地標記正確的目錄,然後構建一個xml文檔。高速緩存xml文檔並使用xpath檢索數據。
該插件構建反射xml並將其緩存以備後用。我已經將這些代碼從其原始實現中解脫出來,因此它更多地給你一種感覺而不是複製和粘貼。
當然,數據庫也可以在這裏使用。但是,如果你想限制每頁的查詢,緩存的xml文檔工作得很好。
class My_Reflection_Plugin extends My_Controller_Plugin_Abstract
{
public function routeShutdown(Zend_Controller_Request_Abstract $request)
{
$cache = $this -> getCacheManager() -> getCache('general');
if (!$xml = $cache->load("Reflection"))
{
$paths = array(
PATH_APPLICATION . "/Core",
PATH_SITE . "/Project"
);
foreach ($paths as $path)
{
$this -> inspectDir($path);
}
$cache -> save($this->getReflectionXML(), "Reflection");
}
else
{
$this -> getReflectionXML($xml);
}
}
private function inspectDir($path)
{
$rdi = new RecursiveDirectoryIterator($path);
$rii = new RecursiveIteratorIterator($rdi);
$filtered = new My_Reflection_Filter($rii);
iterator_apply($filtered, array($this, 'process'), array($filtered));
}
private function process($it = false)
{
$this -> getReflectionXML() -> addItem($it -> current());
return true;
}
}
斷詞發生在過濾器內:
class My_Reflection_Filter extends FilterIterator
{
public function accept()
{
$file = $this->getInnerIterator()->current();
// If we somehow have something other than an SplFileInfo object, just
// return false
if (!$file instanceof SplFileInfo) {
return false;
}
// If we have a directory, it's not a file, so return false
if (!$file->isFile()) {
return false;
}
// If not a PHP file, skip
if ($file->getBasename('.php') == $file->getBasename()) {
return false;
}
// Resource forks are no good either.
if (substr($file->getBaseName(), 0, 2) == '._')
{
return false;
}
$contents = file_get_contents($file->getRealPath());
$tokens = token_get_all($contents);
$file->className = NULL;
$file->classExtends = NULL;
$file->classImplements = array();
$last = null;
while (count($tokens) > 0)
{
$token = array_shift($tokens);
if (!is_array($token))
{
continue;
}
list($id, $content, $line) = $token;
switch ($id)
{
case T_ABSTRACT:
case T_CLASS:
case T_INTERFACE:
$last = 'object';
break;
case T_EXTENDS:
$last = "extends";
break;
case T_IMPLEMENTS:
$last = "implements";
break;
case T_STRING:
switch ($last)
{
case "object":
$file -> className = $content;
break;
case "extends":
$file -> classExtends = $content;
break;
case "implements":
$file -> classImplements[] = $content;
break;
}
break;
case T_WHITESPACE:
// Do nothing, whitespace should be ignored but it shouldnt reset $last.
break;
default:
// If its not directly following a keyword specified by $last, reset last to nothing.
$last = null;
break;
}
}
return true;
}
}
一旦你的倒影XML填入你需要出去之類的任何信息,您的ACL插件可以來後,和查詢使用XPath信息。
爲什麼有必要擁有所有操作和控制器名稱?只要想一下白名單:只允許訪問特殊組中的動作或控制器。所有其他人都沒有。 – powtac 2009-05-20 13:56:36
你應該做出這個答案,因爲你是正確的。白名單是進行基於控制器/動作的ACL的最佳方式 – 2009-05-21 09:43:29