How dan I list controller and actions?

Hi folks,

I’m looking for some way to list the controllers and actions of my application dynamically.

there are some way to make this?

eg: I have a controller called "application" and a into it I have two actions "index" and "about", how can I list it into a hash like array("application" => "index, about")?

Thanks.

try using metadata extension. just get all actions and arrange them in an array.

Hi boydzethuong,

I need to find another solution because it not work if the system is case-sensitive.

There are other way?

You need to come up with your own solution. What i would do: Read in protected/controller, and check all /.*Controller.php/ files. Then load them and use PHP’s reflection to inspect the classes. I don’t know, what you aim for, but that should give you a start.

Mike, I understood, but I found a method more simple to do this using "get_declared_classes()", this method show me all loaded classes.

Now I need know, how can I load all controllers at the same time?

Thanks.

Hi GodFather, I’ll get you started! this is the simplest, you can definitely use PHP reflection, thats exactly how MetaData extension does it and gives much more information than just running get_class_methods() http://php.net/manual/en/book.reflection.php

try this




$declaredClasses = get_declared_classes();

foreach (glob(Yii::getPathOfAlias('application.controllers') . "/*Controller.php") as $controller){

  $class = basename($controller, ".php");

  if (!in_array($class, $declaredClasses))

    Yii::import("application.controllers." . $class, true);

    

  //if you want to use reflection

  $reflection = new ReflectionClass($class); 

  $methods = $reflection->getMethods();

  //uncomment this if you want to get the class methods with more details

  //print_r($methods);

   

  //uncomment this if you want to get the class methods

  //print_r(get_class_methods($class));

}


//you should see a list of all Controllers/Models

print_r(get_declared_classes());



then you can use preg_match function to get all actions

maybe:




$methods = $reflection->getMethods();

foreach($methods as $method) {

if (preg_match('/^action+\w{2,}/',$method)) {

// do something

}

}



I am doing this for discover all routes :) (i borrowed some code from an extension and added with module support… (for route based access checking)




    /**

	 * Autodetect all routes and

     * insert them in the database.

	 */

	public function actionAutodetect()

	{        

        if(isset($_POST['autoDetect'])){            

            

            // Install controller permissions

            $permissions = $this->detectPermissions(Yii::app()->controllerPath);


            // Install module permissions

            $modules = Yii::app()->getModules();            

            $this->scanModules($modules);

        }

        

		$this->render('autodetect');

	}


    /**

	 * Detects permissions in the path and inserts them to the database

     * if they not already exists

	 *

     * @param string the directory name that contains the controller classes.

	 * Defaults to Yii::app()->controllerPath.

	 * @return list of permission items.

	 */

	public function detectPermissions($path, $module = null)

	{

		$permissions = array();

		foreach (glob($path.DIRECTORY_SEPARATOR.'*Controller.php') as $full_name) {

			$class_name = pathinfo($full_name, PATHINFO_FILENAME);

			if (!class_exists($class_name, false))

				require($full_name);

			if (!class_exists($class_name, false) || !is_subclass_of($class_name, 'CController'))

				continue;

			$controller_id = preg_replace('/controller/', '', strtolower($class_name), 1);

			foreach (get_class_methods($class_name) as $method_name) {

				if (!preg_match("/^action\w+$/", $method_name))

					continue;

				$action_id = preg_replace('/action/', '', strtolower($method_name), 1);


                if ($action_id !== 's') {

                    

					$name = ucfirst($controller_id).' '.ucfirst($action_id);


                    $route = $controller_id.'/'.$action_id;

                    if($module){

                        $name = ucfirst($module).' '.$name;

                        $route = $module.'/'.$route;

                    }


                    if(Permissions::model()->findByAttributes(array('route' => $route)) === null){

                        

                        $permission = new Permissions();

                        $permission->name = $name;

                        $permission->route = $route;

                        $permission->module = $module;

                        $permission->controller = $controller_id;

                        $permission->save();

                    }

				} else {

                    $controller = new $class_name('/'.$controller_id);

                    

					foreach ($controller->actions() as $action_id => $config) {


                        $name = ucfirst($controller_id).ucfirst($action_id);

                        $route = $controller_id.'/'.$action_id;

                        if($module){

                            $name = ucfirst($module).' '.$name;

                            $route = $module.'/'.$route;

                        }

                        

                        if(Permissions::model()->findByAttributes(array('route' => $route)) === null){

                            if($module){

                                $name = ucfirst($module).' '.$name;

                                $route = $module.'/'.$route;

                            }

                            

                            $permission = new Permissions();

                            $permission->name = $name;

                            $permission->route = $route;

                            $permission->module = $module;

                            $permission->controller = $controller_id;

                            $permission->save();

                        }

					}

                }


                

			}

		}

		return $permissions;

	}


   /**

     * Recursivly scan over modules for the controller paths

     * @param module array

     * @param module object

     */

    protected function scanModules($modules, $module = false)

    {

        foreach($modules as $id => $mod){


            if($module){

                $m = $module->getModule($id);

            } else {                

                $m = Yii::app()->getModule($id);                

            }

            

            if($m){

                $this->detectPermissions($m->controllerPath, $id);


                $m_modules = $m->getModules();

                if($m_modules){

                    foreach($m_modules as $mod_id => $xm){

                        $this->scanModules($m_modules, $m);

                    }                    

                }

            }

        }

    }






Hi folks,

I thought to use the tips of Sniper and boydzethuong.

The idea is make a rule based access system different of default yii, I intend to do this dynamic, where I’ll can create groups and set rules to each group separately.

My idea is:

  • create a user interface, where the admin user can create groups, this interface has a list of all controllers and actions

  • the user select this actions and controllers and save this on a database

  • on user manager area the admin can set the group to each user

Have you ever saw or did some thing like this?

Thanks.

I did the same thing… inspired on Alternate RBAC… and the other RBAC extensions but the code and ui is still pretty rough…

A filter in the controller will run CheckAccess on the user current route.

The only problem that I have stumbled on is when using bizrules for example edit own… You will have 2 routes. And it will try to execute both rules. In practice I haven’t found this to be a problem though but if you require many rules with different bizrules for the same route you could run into issues :)

The code above will add all routes to the database.

hi mech7:

have you realized your idea ,and can kindly share your works :rolleyes: may be as an extension.

i am interested in your works and ideas .

best regards :D

and one question : does it support nested module ?

hi GodFather:

some times passed, have you accomplished you goal, recently i faced the same things :( ;so need some help, could you describe what 's your final approach to get it?

Please find the below code to get the list of controllers in the YII application :


$appControllerPath = Yii::getPathOfAlias('application.controllers'); 

            //checking existence of controllers directory

            if(is_dir($appControllerPath))

                $fileLists = CFileHelper::findFiles($appControllerPath);

            foreach($fileLists as $controllerPath)

            { 

                //getting controller name like e.g. 'siteController.php' 

                $controllerName = substr($controllerPath,  strrpos($controllerPath, DIRECTORY_SEPARATOR)+1,-4); 


          

            }

Hi yiqing95,

I have recently completed my custom user authentication module for the requirements like you have mentioned,

Please find my appraoch as follows:

  1. Implemented user authentication module by that enables admin to create user roles, tasks and operation.

  2. These auth items could be further assigned as a child to other auth item.

  3. One can assign auth items created above directly to user also.

  4. Automated list of controllers and their action, that gives you to save them as auth item into the application.

    Here I will suggest you to save their name as prefix of theri alias (replace "." with "_").

    For example : "op_controllers_site_index"

  5. Either check for access in each action or use beforeAction method of Controller component at application.components.controller in that you can check for authorization in generalized way as follows:

Note : For above first three steps, you should refer user authentication system of FULL CMS, they have also implemented in the almost same way.


function beforeAction($action){

    $accessRule = 'op_controllers_'.Yii::app()->controller->id.'_'.$action->id;

    if(Yii::app()->user->checkAccess($accessRule)){

       return true;

    }else{

       //do something here to show error that you have no access to this page  

    }

}



Hope this might help you, well it is not a example with 100% code, you can ask me for that also, if you need.

Thanks,

Dinesh

Change

preg_match(’/public[ \t]+function[ \t]+action([A-Z]{1}[a-zA-Z0-9]+)[ \t]*\(/’, $line, $matches);

by

preg_match(’/public[ \t]+function[ \t]+action([A-Z]{1}[_a-zA-Z0-9]+)[ \t]*\(/’, $line, $matches);

in metadata.php

Thanks for sharing data I am looking for a solution for my application dynamically. :rolleyes: