Получите все модули, контроллеры и действия из приложения Zend Framework

Я хочу создать Zend Controller для управления ACL, поэтому моя проблема: как я могу получить все имена модулей, имена элементов управления и имена действий в приложении Zend для создания элемента управления ACL?

Я использую Zend_Navigation, и если ресурс не существует в вашем ACL, Zend_Navigation выдает исключение. И я хочу использовать базу данных, чтобы запретить и разрешить доступ. Поэтому сначала я должен создать базу данных. И если мне приходится делать это вручную, это больно.


person Ivo Trompert    schedule 20.05.2009    source источник
comment
Почему необходимо иметь ВСЕ действия и имена контроллеров? Подумайте о белом списке: доступ только к действиям или контроллерам, которые находятся в специальной группе. Все остальные нет.   -  person powtac    schedule 20.05.2009
comment
Вы должны были дать ответ, поскольку вы правы. Белый список - действительно лучший способ продолжить работу с ACL на основе контроллера / действия   -  person David Snabel-Caunt    schedule 21.05.2009
comment
Есть только одна проблема, если вы используете Zend_Navigation и ресурс не существует в вашем ACL, возникает исключение. И я хочу использовать базу данных, чтобы запретить и разрешить доступ. Поэтому сначала я должен создать базу данных. И если мне придется делать это вручную, это будет больно.   -  person Ivo Trompert    schedule 21.05.2009


Ответы (4)


Это может быть старый вопрос, но вот как я это делаю ...

//        $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;
            }
        }
    }
person Community    schedule 27.07.2009
comment
Небольшая настройка: $ actions [] = $ action; - ›$ actions [] = substr ($ action, 0, -6); был полезен в моем случае. Он избавился от действия из строки. - person understack; 23.05.2011
comment
Кажется, что если эта функция вызывается из самого контроллера, то этот контроллер и все его действия отсутствуют в $ acl. - person understack; 23.05.2011
comment
На самом деле вы должны иметь возможность поместить внутренний foreach () и строку после него за пределы других циклов, потому что, когда это будет сделано, как здесь, вы будете искать каждый включенный класс несколько раз. - person Dominik; 28.04.2012

Я создал функцию, которая может получать все действия, контроллеры и модули из приложения 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;
            }
        }
    }       
}
person Ivo Trompert    schedule 21.05.2009

На самом деле я обнаружил, что лучший способ получить легко доступную ссылку на отражение - это рекурсивно разметить правильные каталоги, а затем в результате создать 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.

person jhogendorn    schedule 17.01.2011

Я не думаю, что в 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')){
     ...
  }
}
person Fortega    schedule 20.05.2009