Я хочу создать Zend-контроллер для управления ACL, поэтому моя проблема: как я могу получить все имена модулей, имена управления и имена Action в приложении Zend для создания ACL-элемента управления?
Я использую Zend_Navigation, и если ресурс не существует в вашем ACL Zend_Navigation, это исключение. И я хочу использовать базу данных для отказа и доступа. Поэтому я должен сначала создать базу данных. И если я должен сделать это вручную, это больно.
Это может быть старый вопрос, но я так делаю это …
$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; } } }
Я создал функцию, которая может получить все действия, контроллеры и модули из приложения 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; } } } }
Я на самом деле нашел лучший способ иметь легкодоступную ссылку на отражение, чтобы рекурсивно маркировать правильные каталоги, а затем создавать 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.
Я не думаю, что в 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')){ ... } }