Location: PHPKode > scripts > MyACL > myacl/acl.class.php
<?php

/**
 * This class extends a standard Zend_ACL for use with a database.
 * Written by Michael MistaGee Ziegler
 * License: LGPL v2 or above
 * The constructor expects a Zend_DB object as parameter.
 *
 *
 * Database structure:
 *
 * +------------------+       +----------------+       +-------------+      +--------------+
 * |    Resources     |       |    Access      |       |   Roles     |      |  Inheritance |
 * +------------------+       +----------------+       +-------------+      +--------------+
 * | *id              |<--.   | *role_id       |------>| *id         |<-----| *child_id    |
 * | parent_id        |---'`--| *resource_id  N|       +-------------+   `--| *parent_id   |
 * | *privilege      N|<------| *privilege    N|                            | order        |
 * +------------------+       | allow          |                            +--------------+
 *                            +----------------+
 *
 *
 *   *field = PRIMARY KEY( field )
 *   -----> = foreign key constraint
 *
 *   The actual table names should be: acl_resources, acl_access, acl_roles, acl_inheritance.
 *
 * access.allow is a boolean field, that specifies whether the respective rule is an allow rule or a deny rule (important for inherited access).
 *
 * The inheritance table stores which Role is to inherit rights from which parent rules. There can
 * be multiple parent rules. If a rule inherits rights from more than one parent, the first rule applicable
 * will be used to determine whether to allow or deny the access rights in question.
 * The order field stores in which order the parents are to be introduced to Zend_ACL, effectively setting
 * the order the parent rights are evaluated in.
 * Using a relational database for this is strongly advised, as it guarantees data integrity.
 *
 * If you intend to give each resource a specific name or collect other data about it, you should create
 * an extra table storing this data and put a foreign key referencing this into the resources table. Same
 * goes for the privileges.
 *
 */


require_once 'Zend/Db.php';

require_once 'Zend/Acl.php';
require_once 'Zend/Acl/Role.php';
require_once 'Zend/Acl/Resource.php';


class MyACL extends Zend_ACL {
	
	private $dbase;
	
	public function hasAllRolesOf( array &$searchRoles ){
		foreach( $searchRoles as $theRole )
			if( !$this->hasRole( $theRole ) )
				return false;
		return true;
		}
	
	public function __construct( Zend_Db_Adapter_Abstract &$db ){
		$this->dbase = &$db;
		
		// I chose to write the field names into these SQL statements, so that the tables can actually contain more
		// fields than just the ones I need here without producing heavier DB load as neccessary.
		
		/// First: Create all the resources we have.
		$resources = $db->fetchAll( $db->select()->distinct()->from( 'acl_resources', array( 'id', 'parent_id' ) ) );
		
		$resCount  = count( $resources );
		$addCount  = 0;
		
		$allResources = array();
		foreach( $resources as $theRes ){
			$allResources[] = $theRes['id'];
			}
		foreach( $resources as $theRes ){
			if( $theRes['parent_id'] !== null && !in_array( $theRes['parent_id'], $allResources ) ){
				require_once 'Zend/Acl/Exception.php';
				throw new Zend_Acl_Exception(
					"Resource id '".$theRes['parent_id']."' does not exist"
					);
				}
			}
		
		while( $resCount > $addCount ){
			foreach( $resources as $theRes ){
				// Check if parent resource (if any) exists
				// Only add if this resource hasn't yet been added and its parent is known, if any
				if( !$this->has( $theRes['id'] ) &&
				    ( $theRes['parent_id'] === null || $this->has( $theRes['parent_id'] ) )
				  ){
					$this->add( new Zend_Acl_Resource( $theRes['id'] ), $theRes['parent_id'] );
					$addCount++;
					}
				}
			}
		
		/// Now create all roles
		$roles = $db->fetchAll(
			$db->select()
			->from(     array( 'r' => 'acl_roles' ),       array( 'r.id', 'i.parent_id' ) )
			->joinLeft( array( 'i' => 'acl_inheritance' ), 'r.id=i.child_id'              )
			->order(    array( 'child_id', 'order' ) )
			);
		
		// Create an array that stores all roles and their parents
		$dbElements = array();
		foreach( $roles as $theRole ){
			if( !isset( $dbElements[ $theRole['id'] ] ) )
				$dbElements[ $theRole['id'] ] = array();
			if( $theRole['parent_id'] !== null )
				$dbElements[ $theRole['id'] ][] = $theRole['parent_id'];
			}
		
		// Now add to the ACL
		$dbElemCount  = count( $dbElements );
		$aclElemCount = 0;
		
		// while there are still elements left to be added
		while( $dbElemCount > $aclElemCount ){
			// Check every element in the db
			foreach( $dbElements as $theDbElem => $theDbElemParents ){
				// Check if a parent is invalid to prevent an infinite loop
				// if the relational DBase works, this shouldn't happen
				foreach( $theDbElemParents as $theParent ){
					if( !array_key_exists( $theParent, $dbElements ) ){
						require_once 'Zend/Acl/Exception.php';
						throw new Zend_Acl_Exception(
							"Role id '$theParent' does not exist"
							);
						}
					}
				if( !$this->hasRole( $theDbElem ) &&            // if it has not yet been added to the ACL
				    ( empty( $theDbElemParents )  ||            // and no parents exist or
				      $this->hasAllRolesOf( $theDbElemParents ) // we know them all
				    )
				  ){
					// we can add to ACL
					$this->addRole( new Zend_Acl_Role( $theDbElem ), $theDbElemParents );
					$aclElemCount++;
					}
				}
			}
		
		
		/// Now create all access rules
		$access = $db->fetchAll( $db->select()->from( 'acl_access', array( 'role_id','resource_id','privilege','allow' ) ) );
		
		foreach( $access as $theRule ){
			if( $theRule['allow'] == true )
				$this->allow( $theRule['role_id'], $theRule['resource_id'], $theRule['privilege'] );
			else	$this->deny(  $theRule['role_id'], $theRule['resource_id'], $theRule['privilege'] );
			}
		}
	
	}

Return current item: MyACL