从Zend Framework应用程序获取所有模块,控制器和操作

时间:2009-05-20 13:37:07

标签: php zend-framework

我想创建一个用于ACL管理的Zend Controller,所以我的问题是:如何在Zend应用程序中获取所有模块名称,控件名称和操作名称以构建ACL控件?

我使用Zend_Navigation,如果您的ACL中不存在资源,Zend_Navigation会抛出异常。我想使用数据库拒绝并允许访问。所以我必须首先构建数据库。如果我必须亲手做到这一点,那就很难。

4 个答案:

答案 0 :(得分:25)

这可能是一个老问题,但这就是我这样做的方式......

//        $front = Zend_Controller_Front::getInstance(); // use this line instead on a model class
    $front = $this->getFrontController(); // this in controller
    $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;
                $class = substr($file,0,strpos($file,".php"));

                if (is_subclass_of($class, 'Zend_Controller_Action')) {

                    $controller = strtolower(substr($file, 0, strpos($file, "Controller")));
                    $methods = array();

                    foreach (get_class_methods($class) as $method) {
                        if (strstr($method,"Action") != false) {
                            array_push($methods,substr($method,0,strpos($method,"Action")));
                        }
                    }
                }

                $acl[$module][$controller] = $methods;
            }
        }
    }

答案 1 :(得分:7)

我创建了一个可以从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;
            }
        }
    }       
}

答案 2 :(得分:1)

我实际上找到了一个容易获得的反射引用的最佳方法是以递归方式标记正确的目录,然后构建一个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查询该信息。

答案 3 :(得分:0)

我认为Zend没有解决方案。你必须自己做...

一种方法是列出所有类,并检查类是否扩展(例如)Zend_Controller_Action类......

检查php函数get_declared_classesis_subclass_of

foreach(get_declared_classes() as $c){
  if(is_subclass_of($c, 'Zend_Controller_Action')){
     ...
  }
}