2009-05-20 10 views
12

Quiero crear un Zend Controller para la gestión de ACL así que mi problema es: ¿Cómo puedo obtener todos los nombres de Módulo, Nombres de control y Acción en una aplicación Zend para construir un Control de ACL?Obtenga todos los módulos, controladores y acciones desde una aplicación Zend Framework

Uso Zend_Navigation y si el recurso no existe en su ACL Zend_Navigation se lanza como una excepción. Y quiero usar una base de datos para denegar y permitir el acceso. Entonces debo construir la base de datos primero. Y si debo hacer eso a mano, es un dolor hacer eso.

+2

¿Por qué es necesario tener TODAS las acciones y nombres de controlador? Solo piense en una lista blanca: solo se permite el acceso a acciones o controladores que están en un grupo especial. Todos los demás no son – powtac

+0

Deberías haber hecho esta una respuesta, ya que estás en lo correcto. Una lista blanca es realmente la mejor manera de proceder con la ACL basada en controlador/acción –

+2

. Solo hay un problema si utiliza Zend_Navigation y el recurso no existe en su ACL. Se produce una excepción. Y quiero usar una base de datos para denegar y permitir el acceso. Entonces debo construir la base de datos primero. Y si debo hacerlo a mano, es un dolor hacer eso. –

Respuesta

7

he creado una función que puede conseguir todas las acciones, los controladores y los módulos de una aplicación de Zend. Aquí 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

No creo que haya una solución para esto en Zend. Tendrá que hacerlo usted mismo ...

Una forma de hacerlo, es enumerar todas las clases, y comprobar si las clases se extienden (por ejemplo) la clase Zend_Controller_Action ...

verificar las funciones de php get_declared_classes y is_subclass_of

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

esto puede ser una cuestión de edad, pero esta es la forma en que estoy haciendo esto ...

$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

funciona como un encanto! – smoove

+0

Una pequeña modificación: "$ actions [] = $ action;" -> "$ actions [] = substr ($ action, 0, -6);" fue útil en mi caso. Se deshizo de "Acción" de la cadena. – understack

+0

Parece que si se llama a esta función desde un controlador, entonces ese controlador y todas sus acciones faltan desde $ acl. – understack

1

De hecho, me pareció que la mejor manera de tener una referencia de reflexión era fácilmente disponibles a Conversar de forma recurrente los directorios correctos y luego crear un documento xml como resultado. Almacenamiento en caché del documento xml para mayor velocidad y uso de xpath para recuperar los datos.

El complemento crea el reflejo xml y lo almacena en caché para más adelante. He sacado este código de su implementación original, así que es más para darle una idea en lugar de copiar y pegar.

Por supuesto, una base de datos funciona igual de bien aquí. Pero si intenta limitar sus consultas por página, un documento XML en caché funciona bastante 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; 
    } 
} 

Estandarización de fichas que sucede en el interior 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; 
    } 
} 

vez que tenga su xml reflexión poblada de toda la información que necesita salir de la clase, su plugin ACL puede venir después de él y consultar esa información con XPath .

Cuestiones relacionadas