Over the years I have been learning PHP (big thanks to Sitepoint) and toying with the idea to make my own framework to further learn and maybe possibly help me build sites. I have a base that I have been slowly building and would like to move on building it - unless it's broken, which is why I would like a review for bad practices or code smells. I would like to keep the code small, simple, clean, and modular, if at all possible.

The base code does some basic stuff:
a) Loads base/extension/template ini configs
b) Connects to the db (PDO)
c) Loads, sets, deletes options/extensions/templates/languages
- Extensions/Templates to require index.php/item.ini to load
d) Create an API for extensions/languages/validators/routes
- Validator influenced by Zend
- Extensions/Templates influenced by WordPress
- Languages use strtr instead gettext
- Url parsing from rewrite & non rewrite (www.site.com/?/admin/login)
e) Sends the system to load the correct plugin controller (ala HMVC pattern)

My worries are:
1. Static class or just plain procedural functions?
2. Too much code or possible refactoring needed (Extensions for one)
3. Is my interpretation of MVC/HMVC correct in the examples below?
4. Could this be improved upon at this stage?

Please keep in mind, this is not finished at all.

Base Framework
PHP Code:
<?php

    error_reporting
(-1);

#---------- Helper Functions ---------------------------------------------------

    // Returns value from array[key]
    // Based from https://wiki.php.net/rfc/functionarraydereferencing
    // @return mixed on success, false on failure    
    
function value($key, array $array) {
        return isset(
$array[(string) $key])
            ? 
$array[(string) $key]
            : 
false;
    }

    
// Get values from an config (ini) file based on section/key
    // @return mixed on success    
    
function ini_value($key$file 'config.php') {
        static 
$ini$array;
        if( 
$ini !== (string) $file ) {    
            
$ini   = (string) $file;        
            
$array parse_ini_file((string) $fileTRUE);
        }
        return 
value((string) $key$array);
    }
    
    
// PDO database connection wrapper
    // @return object on success, dies on failure
    
function db() {
        static 
$db;
        if( !
$db ) {
            
$db FALSE;
            try {
                
$db = new PDO(
                    
ini_value('database.dsn'),
                    
ini_value('database.user'),
                    
ini_value('database.pass'),
                    (array) 
ini_value('database.options')
                );
            } catch( 
PDOException $e ) {
                die( 
$e->getMessage() );
            }
        }
        return 
$db;
    }
    
    
// From http://www.php.net/manual/en/function.strip-tags.php#62705    
    
function strip_tags_deep($value) {
        return 
is_array($value
            ? 
array_map('strip_tags_deep'$value
            : 
strip_tags($value);
    }        

    
// Modified version of http://php.net/manual/en/function.ksort.php#105399
    
function ksort_recursive(array &$array) {
        
ksort$array );
        foreach( 
$array as &$a ) {
            if( 
is_array($a) ) {
                
ksort_recursive($a);
            }
        }
    }    
    
#---------- Registry Class -----------------------------------------------------

    
final class Registry {
        private static 
            
$_data = array();

        static function 
get$key ) {
            if( isset(
self::$_data[$key]) ) {
                return 
self::$_data[$key];
            }
        }
        
        static function 
set$key$value '' ) {
            
self::$_data[$key] = $value;
        }
    }
    
#---------- Observable & Observer classes --------------------------------------
    
    
class Observable {
        private 
            
$_event,
            
$_observers = array();
        
        final function 
attachObserver $observer ) {
            
$i array_search($observer$this->_observers);
            if( 
$i === false ) {
                
$this->_observers[] = $observer;
            }
        }        

        final function 
createEvent$event ) {
            
$this->_event $event;
            
$this->_notify();
        }

        final function 
getEvent() {
            return 
$this->_event;
        }
        
        final private function 
_notify() {
            foreach( 
$this->_observers as $observer ) {    
                
$observer->update$this );
            }            
        }
    }
    
    interface 
Observer {
        function 
updateObservable $subject );
    }

#---------- AddOn Template -----------------------------------------------------
    
    
abstract class AddOn extends Observable {
        protected 
            
$_active,
            
$_list = array();

        abstract function 
load();
        abstract function 
get();
        abstract function 
process($str);

        abstract protected function 
set($addon);
    }

    
// Procedural helpers
    
function addon_init(AddOn $addon) {
        
$addon->load();
    }

    function 
addon_get(AddOn $addon) {
        return 
$addon->get();
    }

    function 
addon_process(AddOn $addon$str) {
        
$addon->process($str);
    }
    
#---------- Options CRUD Class ------------------------------------------------

    
final class Options {
        private static
            
$_db,
            
$_data = array();

        static function 
init$db ) {
            
self::$_db $db;
            
$sql 'SELECT name, value 
                    FROM options;'
;
            foreach( 
self::$_db->query($sql) as $r ) {
                
self::$_data[$r['name']] = $r['value'];
            }
        }

        static function 
get$key ) {
            if( 
self::exists($key) ) {
                return 
self::$_data[$key];
            }
        }

        static function 
set$key$value ) {
            if( 
self::exists($key) ) {
                
$sql "UPDATE options 
                        SET value = :value 
                        WHERE name = :key;"
;
            } else {
                
$sql "INSERT INTO options 
                        VALUES(:key, :value);"
;            
            }
            
$sth self::$_db->prepare($sql);
            
$sth->execute(
                array(
                    
':key'   => $key,
                    
':value' => $value
                
)
            );
        }

        static function 
delete$key ) {
            if( 
self::exists($key) ) {
                
$sql "DELETE 
                        FROM options 
                        WHERE name = :key;"
;
                
$sth self::$_db->prepare($sql);
                
$sth->execute(
                    array(
                        
':key' => $key
                    
)
                );
            }
        }

        static function 
exists$key ) {
            return 
array_key_exists($keyself::$_data);
        }    
    }
    
// Initialize options
    
Options::init(db());
    
    
// Procedural helpers
    
function get_option$value ) {
        return 
Options::get$value );
    }
    
    function 
set_option$key$value ) {
        
Options::set$key$value );
    }
    
    function 
delete_option$key ) {
        
Options::delete$key );
    }

#---------- Plugin Helper ------------------------------------------------------
    
    
define(
        
'EXT_PATH',
        
ini_value('path.extensions')
    );

    
Registry::set('ext'
        array(
            
'ext' => array(),
            
'hook'=> array()
        )
    );

    function 
ext() {
        return 
Registry::get('ext');
    }

    
# Extension CRUD Class
    
final class Extension {
        private static
            
$_db,
            
$_data = array();

        static function 
init$db ) {
            
self::$_db $db;
            
$sql 'SELECT name 
                    FROM plugins;'
;
            foreach ( 
self::$_db->query($sql) as $r ) {
                
self::$_data[] = $r['name'];
            }
        }

        static function 
get() {
            return 
self::$_data;
        }

        static function 
add$name ) {
            if( !
self::exists($name) ) {
                
$sth self::$_db->query("
                    INSERT INTO plugins 
                    VALUES(:name);"
                
);
                
$sth->execute(
                    array(
                        
':name' => $name
                    
)
                );
            }
        }

        static function 
delete$name  ) {
            if( 
self::exists($name) ) {
                
$sth self::$_db->query("
                    DELETE 
                    FROM plugins
                    WHERE name = :name;
                "
);
                
$sth->execute(
                    array(
                        
':name' => $name
                    
)
                );
            }
        }

        static function 
exists$name ) {
            return 
in_array$nameself::$_data ); 
        }        
    }
    
    final class 
Extensions extends AddOn {
        function 
__construct() {
            
$this->_active Extension::get();
        }
        
        function 
load() {
            foreach( 
$this->_active as $p ) {

                
$file EXT_PATH'/' .$p'/index.php';
                
$ini  EXT_PATH'/' .$p'/plugin.ini';

                if( 
file_exists($file) && file_exists($ini) ) {
                    require( 
$file );

                    if( 
class_exists($p) ) {    
                        
$i = new $p;
                        
$i->commit();

                        
// Attach the observer
                        
$this->attach($i);
                    }                    
                }
            }        
        } 

        function 
get() {
            
$dir opendir(EXT_PATH);
            if( 
$dir ) {
                while( 
false !== ($file readdir($dir)) ) {
                    if( 
                           
$file != '.' 
                        
&& $file != '..' 
                        
&& file_exists(EXT_PATH'/' .$file'/index.php')                        
                        && 
file_exists(EXT_PATH'/' .$file'/plugin.ini')
                    ) {    

                        
$this->_list[$file] = $this->data(
                            
EXT_PATH'/' .$file'/plugin.ini'
                        
);

                        if( 
in_array($file$this->_active) ) {
                            
$this->_list[$file]['active'] = TRUE;
                        }
                    }
                }
            }
            return 
$this->_list;
        }
            
        protected function 
data$file ) {
            
$array parse_ini_file($fileTRUE);            
            return 
strip_tags_deep($array);
        }

        protected function 
set$addon ) {
            
Extension::set$addon );
            
$this->createEvent('activated_' $addon);
        }
        
        protected function 
delete$addon ) {
            
Extension::delete$addon );
            
$this->createEvent('deactivated_' $addon);            
        }    

        function 
process$str ) { 
            switch (
true) {
                case (
strpos($str'activate') === ):
                    
$this->set(substr($str'9'));
                break;
                case (
strpos($str'deactivate') === ):
                    
$this->delete(substr($str'11'));
                break;
            }
        }            
    }    
    
    function 
extensions() {
        static 
$exts;
        if( !
$exts ) {
            
$exts = new Extensions;
        }
        return 
$exts;
    }    
    
    class 
Ext implements Observer {
        protected 
            
$_name,
            
$_data = array();    
        private
            
$_ext
            
$_hook = array();        
    
        final function 
__construct() {
            
$this->_ext  ext();
            
$this->_name get_class($this);
            
$this->_data value(
                
$this->_name,
                
addon_get(extensions())
            );
        }    
        
        final function 
bind$hook$callback$priority 10 ) {
            if( 
is_callable($callback) ) {
                
$this->_hook[(string) $hook][(int) $priority][] = $callback;
            }
        }
        
        final private function 
_check_requirements() {
            if( isset(
$this->_data['plugin.dependencies']['requires']) ) {
                
$requirement $this->_data['plugin.dependencies']['requires'];
                
                foreach( 
$requirement as $dep ) {
                    if( !isset(
$this->_ext['ext'][$dep]) ) {
                        
$this->_data['req_error'] = TRUE;
                    }
                }    
            }
        }            

        final private function 
_set_hooks() {
            
// Parse hook/callback in plugin.ini
            
if( isset($this->_data['plugin.hooks']) ) {
                foreach( 
$this->_data['plugin.hooks'] as $hook => $calls ) {
                    if( 
is_array($calls) ) {    
                        foreach( 
$calls as $k => $func ) {
                            
$this->_ext['hook'][$hook][10][] = $func;
                        }                    
                    } else {
                        
$this->_ext['hook'][$hook][10][] = $calls;
                    }        
                }
            }
            
            
// Parse hook/callback set from Ext::bind($hook, $fn, $priority)
            
foreach ($this->_hook as $hook => $calls) {
                foreach( 
$calls as $k => $call ) {
                    foreach( 
$call as $func ) {
                        
$this->_ext['hook'][$hook][$k][] = $func;
                    }
                }
            }            
        }
        
        final function 
commit() {
            
$this->_check_requirements();
            
            if( 
method_exists($this'main') ) {
                
$this->main();
            }
            
            if(    !isset(
$this->_data['req_error']) 
                || 
$this->_data['req_error'] === FALSE 
            
) {

                
$this->_ext['ext'][$this->_name] = $this->_data;
                
                
$this->_set_hooks();
                
ksort_recursive($this->_ext['hook']);
            }
            
Registry::set('ext'$this->_ext);
        }

        final function 
updateObservable $subject ) {
            switch( 
$subject->getEvent() ) {
                case( 
'activated_' $this->_name ):
                    if( 
method_exists($this'install') ) {
                        
$this->install();
                    }
                break;
                
                case( 
'deactivated_' $this->_name  ):
                    if( 
method_exists($this'uninstall') ) {
                        
$this->uninstall();
                    }
                break;                
            }
        }
    }

    function 
ext_commit(Ext $ext) {
        
$ext->commit();

        
// Log this transaction
        
$e ext();
        
$e['ext'][get_class($ext)]['internal_commit'] = TRUE;
        
Registry::set('ext'$e);
    }    

    function 
set_hook$hook$var NULL ) {
        
$ext ext();

        if( isset(
$ext['hook'][$hook]) ) { 
            foreach( 
$ext['hook'][$hook] as $k => $calls ) {
                foreach( 
$calls as $func ) {
                    if( 
is_callable($func) ) {
                        
$var $func($var);
                    
#    $var = call_user_func_array($func, array($var));
                    
}
                }
            }
        }
        return 
$var;
    }
    
#---------- Template Helper ----------------------------------------------------

    
define
        
'TPL_PATH',    
        
ini_value('path.templates'
    );

    final class 
Templates extends AddOn {
        function 
__construct() {
            
$this->_active get_option('template');
        }

        function 
load() {
            
$file TPL_PATH .'/'$this->_active '/index.php';
            
$ini  TPL_PATH .'/'$this->_active '/template.ini';    
            if( 
file_exists($file) && file_exists($ini) ) {
                require(
$file);
            }
        } 

        function 
get() {
            
$dir opendir(TPL_PATH);
            if( 
$dir ) {
                while( 
false !== ($file readdir($dir)) ) {
                    if( 
                           
$file != '.' 
                        
&& $file != '..' 
                        
&& file_exists(TPL_PATH'/' .$file'/index.php')                        
                        && 
file_exists(TPL_PATH'/' .$file'/template.ini')
                    ) {                        
                        
$this->_list[$file] = $this->data(
                            
TPL_PATH'/' .$file'/template.ini'
                        
);
                        if( 
$file === $this->_active ) {
                            
$this->_list[$file]['active'] = TRUE;
                        }
                    }
                }
            }
            return 
$this->_list;
        }

        protected function 
data$file ) {
            
$array = array();
            
$array parse_ini_file($fileTRUE);    
            return 
strip_tags_deep($array);        
        }

        protected function 
set$addon ) {
            
set_option'template'$addon );
        }

        function 
process$str ) {
            if( 
strpos($str'activate') === ) {
                
$this->set(substr($str'9'));
            }
        }            
    }
    
    function 
templates() {
        static 
$tpls;
        if( !
$tpls ) {
            
$tpls = new Templates;
        }
        return 
$tpls;
    }    
    
#---------- Language Helper ----------------------------------------------------

    
define
        
'LANG_PATH'
        
ini_value('path.languages'
    );

    
Registry::set('lang', array());

    function 
lang() {
        return 
Registry::get('lang');
    }    

    final class 
Languages extends AddOn {
        function 
__construct() {
            
$this->_active get_option('language');
        }

        function 
load() {
            
$array = array();
            
$file LANG_PATH .'/'$this->_active '.php';
            if( 
file_exists($file) ) {
                
$array = require $file;
            }
            
Registry::set('lang'$array);
        }     

        function 
get() {
            
$dir opendir(LANG_PATH);
            if (
$dir) {
                while (
false !== ($file readdir($dir))) {
                    if (
                               
$file != '.' 
                            
&& $file != '..' 
                            
&& file_exists(LANG_PATH .'/'$file)
                    ) {
                        
$filename basename($file'.php');
                        
$this->_list[$filename] = array();
                        
                        if( 
$filename === $this->_active ) {
                            
$this->_list[$filename]['active'] = TRUE;
                        }
                    }
                }
            }
            return 
$this->_list;        
        }

        protected function 
set$addon ) {
            
set_option'language'$addon );
        }

        function 
process$str ) {
            
$this->set$str );
        }                    
    }
    
// initialize languages
    
addon_init(new Languages);    

    
// Function to translate strings 
    // Uses http://php.net/manual/en/function.strtr.php
    // Do not translate placeholder
    
function __$string, array $args NULL ) {
        
$string value($stringlang())
            ? 
value($stringlang())
            : 
$string;

        return 
$args === null
            
$string
            
strtr($string$args);
    }
    
#---------- Form Validator Class -----------------------------------------------
    
    // Based from http://framework.zend.com/manual/1.12/en/zend.validate.introduction.html
    
abstract class Validator {
        protected 
            
$_errorMsg 'undefined';

        final function 
getError() {
            return 
$this->_errorMsg;
        }

        abstract function 
validate($value);
    }
    
    final class 
Validate {
        private 
            
$_data = array(),
            
$_errors = array();
    
        function 
addValidator($fieldValidator $obj) {
            
$this->_data[$field][] = $obj;
            return 
$this;
        }
        
        function 
isValid($array) {
            
$valid true;
            
            foreach( 
$this->_data as $field => $objects ) {
                foreach(
$objects as $i => $obj) {
                    if(     isset(
$array[$field]) 
                        && !
$obj->validate($array[$field]) 
                    ) {
                    
                        
$valid false;
                        
$this->_errors[] = array(
                            
'field' => $field,
                            
'error' => $obj->getError()
                        );
                        break;
                    }
                }
            }
            return 
$valid;        
        }
        
        function 
getErrors() {
            return 
$this->_errors;        
        }
    }    

    function 
get_validation_error($field, array $array) {
        
$exists false;
        foreach( 
$array as $k => $v ) {
            if(         isset(
$array[$k]['field']) 
                &&  
$array[$k]['field'] === $field 
            
) {
                
$exists true;
            }
        }
        if( 
$exists ) {
            return 
$array[$k]['error'];
        }
    }

#---------- URI Helpers --------------------------------------------------------

    // Remove index.php, and duplicate slashes from $_SERVER['REQUEST_URI']
    // Based on http://brandonwamboldt.ca/my-php-router-class-825/
    
function _prepare_uri() {
        
$uri $_SERVER['REQUEST_URI'];
        
$uri str_replacedirname($_SERVER['SCRIPT_NAME']), ''$uri );
        
$uri str_replace('index.php'''$uri);
        
$uri str_replace('?/'''$uri);        
        
$uri preg_replace'/\/+/''/'$uri );
        
$uri ltrim$uri'/' );        

        return 
$uri;
    }

    function 
parse_uri($component null) {
        
$array = array();

        
$uri _prepare_uri();
        
$uri parse_url($uri);

        if( isset(
$uri['path']) ) {
            
$uri['path'] = trim($uri['path'], '/');
            
$array['path'] = explode('/'$uri['path']);
        }
        if( isset(
$uri['query']) ) {
            
parse_str($uri['query'], $array['query']);
        }

        return isset(
$array[$component])
            ? 
$array[$component]
            : 
$array;
    }
    
    function 
uri_part($num) {
        return 
value($numparse_uri('path'));
    }    

    function 
uri_qstr($key) {
        return 
value($keyparse_uri('query'));
    }
    
    function 
build_url() {    
        
$str  'http://' $_SERVER['HTTP_HOST'];
        
$str .= str_replace'index.php'''$_SERVER['SCRIPT_NAME'] );
        
$qs ini_value('rewrite.url') === TRUE
            
''
            
'?/';
        
$args func_get_args();
        
        if( !empty(
$args[0]) ) {
            
$args implode('/'$args);
            return 
$str $qs $args;
        } else {
            return 
$str;
        }
    }    

#---------- Router Class -------------------------------------------------------
    
    // Modified from http://brandonwamboldt.ca/my-php-router-class-825/
    
function route$route$callback ) {
        
$path implode('/'parse_uri('path'));
    
        
// Custom
        // Format: <:var_name|regex>
        
$route preg_replace('/\<\:(.*?)\|(.*?)\>/''(?P<\1>\2)'$route);
    
        
// Alphanumeric
        // Format: <:var_name>
        
$route preg_replace('/\<\:(.*?)\>/''(?P<\1>[A-Za-z0-9\-\_]+)'$route);
        
        
// Numeric
        // Format: <#var_name>
        
$route preg_replace('/\<\#(.*?)\>/''(?P<\1>[0-9]+)'$route);
        
        
// Wildcard (INCLUDING dir separators) 
        // Format: <*var_name>
        
$route preg_replace('/\<\*(.*?)\>/''(?P<\1>.+)'$route);
        
        
// Wildcard (EXCLUDING dir separators)
        // Format: <!var_name>
        
$route preg_replace('/\<\!(.*?)\>/''(?P<\1>[^\/]+)'$route);
        
        
// Add regex for a full match or no match
        
$route '#^' $route '$#';    

        if (
preg_match($route$path$matches)) {
            
$params = array();
    
            foreach ( 
$matches as $key => $match ) {
                if (
is_string($key)) {
                    
$params[$key] = $match;
                }
            }
            if (
is_callable($callback)) {
                return 
call_user_func_array($callback$params);    
            }
        }
    }    
    
#---------- Admin Base Functions -----------------------------------------------

    
define(
        
'ADMIN_PATH',
        
ini_value('path.admin')
    );    

    abstract class 
AdminModule extends Ext {
        function 
register_admin_module() {
            
$this->_data['admin_module'][] = substr($this->_name5);
        }            
    }
    
    function 
registered_admin_modules() {
        
$modules = array();    
        foreach( 
value('ext'ext()) as $ext ) {
            if( isset(
$ext['admin_module'][0]) ) {
                
$modules[] = strtolower(
                    
$ext['admin_module'][0]
                );
            }
        }
        
$modules array_flip($modules);
        return 
$modules;
    }    

#-------------------------------------------------------------------------------

    
Extension::init(db());    
    
addon_init(extensions());
    
    require(
'validators.php');
    
    
// View code
    
function center() {
        echo 
set_hook('center');
    }    
    
    
$baseController 'PageController';
    switch( 
true ) {
        case( 
uri_part(0) ):    
            
$fn ucfirst(uri_part(0)) . 'Controller';
            
            if( 
is_callable($fn) ) {
                
$fn();
            } else {
                
$baseController();
            }
        break;
        
        default:
            
$baseController();
        break;
    }
    unset(
$baseController);
    
    function 
AdminController() {
        require(
ADMIN_PATH '/index.php');    
    }
    
    function 
PageController() {
        
addon_init(templates());    
    }
Example Admin loader, Login/Logout model, & View loader:
PHP Code:
<?php

    
if( !ADMIN && uri_part(1) != 'login') {
        if( !
headers_sent() ) {
            
$url build_url('admin''login');
            
header("Location: $url");
        }
    }

    class 
AdminCenter extends Ext {
        function 
main() {
            
$this->bind('center''AdminCenterController');
        }
    }
    
ext_commit(new AdminCenter);
    
    function 
AdminCenterController() {
        
$modules registered_admin_modules();
        if( isset(
$modules[uri_part(1)]) ) {
            
$fn 'Admin'ucfirst(uri_part(1)) .'Controller';
            if( 
function_exists($fn) ) {
                
$fn();
            }
        }
    }
    
#---------- Login --------------------------------------------------------------

    
class AdminLogin extends AdminModule {
        function 
main() {
            
$this->register_admin_module();
        }
    }
    
ext_commit(new AdminLogin);    
    
    function 
AdminLoginController() {
        require(
ADMIN_PATH '/_/inc/AdminLoginView.php');
    }
    
    function 
AdminLogin_reqAttr() {
        
$array = array(
            
'form'         => array(
                
'action'=> build_url('admin''login')
            ),
            
'email'     => array(
                
'name'     => 'login_email'
            
),
            
'password'     => array(
                
'name'     => 'login_password'
            
),
            
'submit'     => array(
                
'name'     => 'login_submit'
            
)
        );
    
        return 
set_hook('AdminLogin_reqAttr'$array);
    }

    
# Unset any lingering errors..
    
if( isset($_SESSION['Login_error']) ) {
        unset(
$_SESSION['Login_error']);
    }

    function 
process_AdminLogin($array) {
        
$valid         false;

        
$fields AdminLogin_reqAttr();
        
$_email $fields['email']['name'];
        
$_pword $fields['password']['name'];
        
        
$validation = new Validate;
        
$validation->addValidator$_email, new IsBlank)
                    ->
addValidator($_email, new IsEmail);        
        
$validation->addValidator$_pword, new IsBlank);
        
set_hook('process_login'$validation);
        
        if( !
$array ) return;
    
        if( !
$validation->isValid($array) ) {
            
$_SESSION['Login_error'] = $validation->getErrors();
        } else {
            
$sql 'SELECT * 
                    FROM users
                    WHERE email = :email
                        AND password = :password'
;
            
$sth db()->prepare($sql);
            
            
$email _hash$array[$_email] );
            
$pword _hash$array[$_pword] );
            
$sth->bindParam(':email',     $email );
            
$sth->bindParam(':password',$pword );

            
$sth->execute();

            if( 
$sth->fetchAll() ) {
                
$valid true;
                
$_SESSION['Logged_In'] = _hash($_SERVER['HTTP_USER_AGENT']);
            } else {
                
$_SESSION['Login_error'] = array(
                    
__('Please enter the correct email/password combination to continue.')
                );
            }
        }
    }
    
#---------- Logout -------------------------------------------------------------

    
class AdminLogout extends AdminModule {
        function 
main() {
            
$this->register_admin_module();
        }
    }
    
ext_commit(new AdminLogout);    
    
    function 
AdminLogoutController() {
        
session_destroy();
        require(
ADMIN_PATH '/_/inc/AdminLogoutView.php');
    }    

#---------- Load View ----------------------------------------------------------
    
    
include(ADMIN_PATH '/_theme.php');
Example (unfinished) Login View:
PHP Code:
<?php

    
function post_login() {
        if( isset(
$_SESSION['Logged_In']) ) {
            echo 
'<meta http-equiv="refresh" content="0;url='.build_url('admin').'">';
        }
    }
    
post_login();

    
$array AdminLogin_reqAttr(); 

    
// Process login
    
if( isset($_POST[$array['submit']['name']]) ) {
        
process_AdminLogin($_POST);
        
post_login();
    }
    
?>
<h2><?php echo __('Login'); ?></h2>
<form method="post" action="<?php echo $array['form']['action']; ?>">
    <label><?php echo __('Email'); ?>:</label> 
    <input type="email" name="<?php echo $array['email']['name']; ?>">
    
    <label><?php echo __('Password'); ?>:</label> 
    <input type="password" name="<?php echo $array['password']['name']; ?>" />

    <input type="submit" name="<?php echo $array['submit']['name']; ?>" value="<?php echo __('Login'); ?>">
</form>