2009-05-20 4 views
12

Voglio creare un controller Zend per la gestione ACL, quindi il mio problema è: come posso ottenere tutti i nomi dei moduli, i nomi dei controlli e i nomi delle azioni in un'applicazione Zend per creare un Controllo ACL?Ottieni tutti i moduli, i controller e le azioni da un'applicazione Zend Framework

Uso Zend_Navigation e se la risorsa non esiste nell'ACL, Zend_Navigation genera un'eccezione. E voglio usare un database per negare e consentire l'accesso. Quindi devo prima creare il database. E se devo farlo a mano è doloroso farlo.

+2

Perché è necessario avere TUTTE le azioni e i nomi dei controller? Basti pensare a una whitelist: è possibile accedere solo alle azioni o ai controller che si trovano in un gruppo speciale. Tutti gli altri non lo sono. – powtac

+0

Avresti dovuto fare una risposta, dato che sei corretto. Una lista bianca è davvero il modo migliore per procedere con l'ACL basato su controller/azioni –

+2

C'è un solo problema se si utilizza Zend_Navigation e la risorsa non esiste nella LCA in cui viene generata un'eccezione. E voglio usare un database per negare e consentire l'accesso. Quindi devo prima creare il database. E se devo farlo a mano è doloroso farlo. –

risposta

7

ho creato una funzione che può ottenere tutte le azioni, controller e moduli da un'applicazione Zend. Eccolo:

$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

Non credo ci sia una soluzione per questo in Zend. Si dovrà farlo da soli ...

Un modo per farlo, è quello di elencare tutte le classi, e verificare se le classi estendono (per esempio) la classe Zend_Controller_Action ...

controllo delle funzioni PHP get_declared_classes e is_subclass_of

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

Questa potrebbe essere una vecchia questione, ma questo è come sto facendo questo ...

$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

funziona come un fascino! – smoove

+0

Un piccolo aggiustamento: "$ actions [] = $ action;" -> "$ actions [] = substr ($ action, 0, -6);" è stato utile nel mio caso. Si è sbarazzato di "Azione" dalla stringa. – understack

+0

Sembra che questa funzione venga chiamata da un controller stesso, quindi quel controller e tutte le sue azioni mancano da $ acl. – understack

1

in realtà ho trovato il modo migliore per avere un riferimento riflessione facilmente disponibile era quello di come risultato ricorsivamente le directory corrette e quindi creare un documento xml. Memorizzazione nella cache del documento xml per la velocità e utilizzo di xpath per il recupero dei dati.

Il plug-in crea il riflesso xml e lo memorizza nella cache per dopo. Ho preso questo codice fuori dalla sua implementazione originale, quindi è più per darti un'idea piuttosto che copiare e incollare.

Ovviamente, un database funziona altrettanto bene qui. Ma se stai cercando di limitare le tue query per pagina, un documento XML memorizzato nella cache funziona piuttosto bene.

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

tokenizzazione accade all'interno del filtro:

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

Una volta che avete la vostra xml riflessione popolato con qualsiasi informazioni di cui avete bisogno fuori della classe, il vostro plugin ACL può venire dopo di esso e richiedere che le informazioni con XPath .