2009-05-20 3 views
12

Je souhaite créer un contrôleur Zend pour la gestion des ACL. Mon problème est: comment puis-je obtenir tous les noms de modules, noms de contrôle et noms d'actions dans une application Zend pour créer un Contrôle ACL? J'utilise Zend_Navigation et si la ressource n'existe pas dans votre ACL Zend_Navigation est levée une exception. Et je veux utiliser une base de données pour refuser et autoriser l'accès. Donc je dois d'abord construire la base de données. Et si je dois le faire à la main c'est une douleur de le faire.Obtenir tous les modules, contrôleurs et actions d'une application Zend Framework

+2

Pourquoi est-il nécessaire d'avoir TOUTES les actions et noms de contrôleurs? Pensez simplement à une liste blanche: seules les actions ou les contrôleurs appartenant à un groupe spécial sont autorisés à accéder. Tous les autres ne le sont pas. – powtac

+0

Vous auriez dû faire une réponse, comme vous avez raison. Une liste blanche est vraiment la meilleure façon de procéder avec ACL basé sur contrôleur/action –

+2

Il y a un seul problème si vous utilisez Zend_Navigation et que la ressource n'existe pas dans votre liste de contrôle d'accès, une exception est levée. Et je veux utiliser une base de données pour refuser et autoriser l'accès. Donc je dois d'abord construire la base de données. Et si je dois le faire à la main, c'est une douleur de le faire. –

Répondre

7

J'ai créé une fonction qui peut obtenir toutes les actions, les contrôleurs et les modules d'une application zend. Ici, il est:

$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; 
      } 
     } 
    }  
} 
0

Je ne pense pas qu'il existe une solution pour cela dans Zend. Vous devrez le faire vous-même ...

Une façon de le faire, est de lister toutes les classes, et vérifier si les classes s'étendent (par exemple) la classe Zend_Controller_Action ...

contrôle les fonctions php get_declared_classes et is_subclass_of

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

Cela peut être une vieille question, mais voilà comment je fais cela ...

$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; 
      } 
     } 
    } 
+0

fonctionne comme un charme! – smoove

+0

Un minuscule tweak: "$ actions [] = $ action;" -> "$ actions [] = substr ($ action, 0, -6);" était utile dans mon cas. Il s'est débarrassé de "Action" de la chaîne. – understack

+0

Il semble que si cette fonction est appelée à partir d'un contrôleur lui-même, alors ce contrôleur et toutes ses actions sont manquants à partir de $ acl. – understack

1

j'ai trouvé la meilleure façon d'avoir une référence de réflexion facilement disponible était de tokenise récursivement les répertoires corrects, puis crée un document XML en conséquence. Mise en cache du document XML pour la vitesse et utilisation de xpath pour récupérer les données.

Le plugin construit le reflet xml et le met en cache pour plus tard. J'ai sorti ce code de son implémentation d'origine, donc c'est plus pour vous donner une impression plutôt que pour copier et coller.

Bien sûr, une base de données fonctionne aussi bien ici. Mais si vous essayez de limiter vos requêtes par page, un document XML en cache fonctionne plutôt bien.

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; 
    } 
} 

tokenisation passe à l'intérieur du filtre:

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; 
    } 
} 

Une fois que vous avez votre xml de réflexion rempli avec toutes les informations dont vous avez besoin de la classe, votre plugin acl peut venir après et interroger cette information avec XPath .

Questions connexes