Skip to content

Instantly share code, notes, and snippets.

@tleilax
Created February 9, 2012 10:22
Show Gist options
  • Save tleilax/1779091 to your computer and use it in GitHub Desktop.
Save tleilax/1779091 to your computer and use it in GitHub Desktop.
My current, but almost deprecated approach to MVC in PHP (not guaranteed to work like this, misses files)
<?php
abstract class Controller implements ArrayAccess
{
static protected $messages = array(
'error' => array(),
'notice' => array(),
'success' => array()
);
protected $template = null,
$data = array(),
$controller, $method, $action;
public $view_directory = './views',
$tabs = array();
public function __construct($controller, $method)
{
$this->controller = $controller;
$this->method = $method;
$this->tab = isset($_REQUEST['tab']) ? $_REQUEST['tab'] : false;
$this->action = isset($_REQUEST['action']) ? $_REQUEST['action'] : false;
$this->view_directory = rtrim($this->view_directory, '/');
if (method_exists($this, 'initialize'))
call_user_func(array($this, 'initialize'));
}
public function submitted($input = null)
{
return is_null($input)
? ($_SERVER['REQUEST_METHOD'] == 'POST')
: (isset($_REQUEST[$input]) or isset($_REQUEST[$input.'_x']));
}
/**
* @return Template
*/
public function &get_template($template = null)
{
if (!is_null($template))
$this->set_template($template);
if (is_null($this->template))
{
$filename = $this->controller.'/'.$this->method;
if ($this->tab and isset($this->tabs[$this->tab]))
$filename = $this->controller.'/'.$this->tabs[$this->tab]['method'];
elseif ($this->action)
$filename = $this->controller.'/'.key($this->action);
$filename = strtolower($filename);
if (!file_exists($this->view_directory.'/'.$filename.'.php'))
$filename = str_replace('/', '_', $filename);
if (!file_exists($this->view_directory.'/'.$filename.'.php'))
$filename = str_replace('_', '/', $filename);
$this->template = new Template($this->view_directory.'/'.$filename.'.php');
}
return $this->template;
}
public function set_template($filename)
{
$this->get_template()->set_filename(strtolower($this->view_directory.'/'.$filename));
return $this;
}
public function relocate($section = null, $site = null, $parameters = array())
{
$arguments = func_get_args();
$url = call_user_func_array('FrontController::GetLink', $arguments);
Helper::Relocate($url);
}
public function report_error($message)
{
$arguments = array_slice(func_get_args(), 1);
$message = vsprintf($message, $arguments);
array_push(self::$messages['error'], $message);
return false;
}
public function report_notice($message)
{
$arguments = array_slice(func_get_args(), 1);
$message = vsprintf($message, $arguments);
array_push(self::$messages['notice'], $message);
return null;
}
public function report_success($message)
{
$arguments = array_slice(func_get_args(), 1);
$message = vsprintf($message, $arguments);
array_push(self::$messages['success'], $message);
return true;
}
public function flash($key, $value, $ignore_method = false)
{
$hash = $ignore_method
? md5($this->controller)
: md5($this->controller.'|'.$this->method);
if (!isset($_SESSION[$hash]))
$_SESSION[$hash] = array();
$_SESSION[$hash][$key] = $value;
}
public function flashed($key, $ignore_method = false)
{
$hash = $ignore_method
? md5($this->controller)
: md5($this->controller.'|'.$this->method);
$value = false;
if (isset($_SESSION[$hash][$key]))
{
$value = $_SESSION[$hash][$key];
unset($_SESSION[$hash][$key]);
}
return $value;
}
public function add_tab($title, $method = null, $key = null, $parameters = array())
{
if (is_null($method))
$method = $title;
if (is_null($key))
$key = strtolower($method);
$this->tabs[$key] = array(
'title' => $title,
'method' => $method,
'controller' => get_class($this),
'parameters' => $parameters,
);
return $this;
}
public function execute_tab($tab = null)
{
if (!$this->tab)
$this->tab = reset(array_keys($this->tabs));
$this->get_template()->assign('TABS', $this->tabs);
$this->get_template()->assign('TAB_CURRENT', $this->tab);
FrontController::SetTab($this->tab);
if (array_key_exists($this->tab, $this->tabs))
{
$template_file = strtolower($this->controller.'/'.$this->tabs[$this->tab]['method']);
if (!file_exists($this->view_directory.'/'.$template_file.'.php'))
$template_file = str_replace('/', '_', $template_file);
if (!file_exists($this->view_directory.'/'.$template_file.'.php'))
$template_file = str_replace('_', '/', $template_file);
$this->set_template($template_file);
return call_user_func(array($this, $this->tabs[$this->tab]['method']));
}
}
/** STATIC FUNCTIONS **/
public static function GetAll()
{
$controllers = array();
foreach (glob('./controllers/*.class.php') as $file)
{
require_once $file;
$controller = basename($file, '.class.php');
$class = $controller.'_Controller';
if (!class_exists($class))
continue;
$methods = array_diff(get_class_methods($class), get_class_methods('Controller'));
foreach ($methods as $index => $method)
if (preg_match('/^(.*)_ACTION_/', $method, $match) and in_array($match[1], $methods))
unset($methods[$index]);
sort($methods);
$controllers[$controller] = $methods;
}
return $controllers;
}
public static function &Execute($controller, $method, $parameters = null)
{
$controller_filename = './controllers/'.$controller.'.class.php';
if (!file_exists($controller_filename))
throw new Exception('Unknown controller called', FrontController::E_INVALID_CALL);
require_once $controller_filename;
$controller_name = $controller.'_Controller';
$c = new $controller_name($controller, $method);
if ($c->action)
{
$action = '_ACTION_'.camelize(key($c->action));
$action_method = false;
if ($c->tab and is_callable(array($c, @$c->tabs[ $c->tab ]['method'].$action)))
$action_method = $c->tabs[ $c->tab ]['method'].$action;
elseif (is_callable(array($c, $method.$action)))
$action_method = $method.$action;
if (is_callable(array($c, $action_method)))
{
$result = call_user_func(array($c, $action_method), reset($_REQUEST['action']));
if (is_null($result))
return $c;
elseif (false === $result)
$c->relocate();
}
}
if (!is_callable(array($c, $method)))
throw new Exception('Unknown controller method');
call_user_func_array(array($c, $method), $parameters);
return $c;
}
public static function GetMessages()
{
$messages = array_filter(self::$messages);
self::$messages = array('error'=>array(), 'notice'=>array(), 'success'=>array());
return $messages;
}
public function SetMessages($messages)
{
self::$messages = $messages;
}
## ARRAY ACCESS ##
public function offsetExists($offset)
{
return $this->get_template()->get_variable($offset);
}
public function offsetGet($offset)
{
return $this->get_template()->get_variable($offset);
}
public function offsetSet($offset, $value)
{
$this->get_template()->assign($offset, $value);
}
public function offsetUnset($offset)
{
throw new Exception('Yet unsupported');
$this->get_template();
}
}
<?php
/** Auf singleton umschreiben **/
class FrontController
{
const E_UNPRIVILEDGED_ACCESS = -1;
const E_INVALID_CALL = -2;
protected $controller, $output;
public $via_ajax;
protected $section, $site;
protected static $default_section, $default_site;
protected static $sitemap;
protected static $link_parameters = array();
public function __construct($default_section, $default_site)
{
set_exception_handler(array($this, 'exception_handler'));
self::$default_section = $default_section;
self::$default_site = $default_site;
self::$sitemap = new Sitemap();
$this->via_ajax = $GLOBALS['VIA_AJAX'] = (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) and $_SERVER['HTTP_X_REQUESTED_WITH']=='XMLHttpRequest');
if (!empty($_SESSION['MESSAGES']))
{
Controller::SetMessages(unserialize($_SESSION['MESSAGES']));
unset($_SESSION['MESSAGES']);
}
}
public function execute()
{
$this->section = self::GetCurrentSection();
$this->site = self::GetCurrentSite();
if ($GLOBALS['CONFIG']['CLOSED_SYSTEM'] and !User::IsLoggedIn())
{
$this->section = 'user';
$this->site = 'login';
}
$action = self::$sitemap->get_action($this->section, $this->site);
ob_start();
$this->controller = Controller::Execute($action['controller'], $action['method'], array_map('trim', explode(',', $action['parameters'])));
$this->output = ob_get_clean();
return $this;
}
public function display($layout)
{
$template = $this->get_template();
$template->set_layout($layout);
$template['INTRO'] = DBManager::Get()->query("SELECT template FROM templates WHERE `code` = ?", 'intro-'.$this->section.'-'.$this->site)->fetch_item();
$template['OUTRO'] = DBManager::Get()->query("SELECT template FROM templates WHERE `code` = ?", 'outtro-'.$this->section.'-'.$this->site)->fetch_item();
$template['SITE_TITLE'] = self::$sitemap->get_title($this->section, $this->site);
$template['NAVIGATION'] = self::$sitemap->get_navigation($this->section, $this->site);
$template['MESSAGES'] = Controller::GetMessages();
$template['CONSOLE'] = $this->output;
try {
$template->display();
} catch (Exception $e) {
throw new Exception('Unable to render "'.$this->section.'::'.$this->site.'"<br/><pre>'.$e.'</pre>');
}
ob_end_flush();
flush();
}
public function get_template()
{
return $this->controller->get_template();
}
public function exception_handler(Exception $exception)
{
if (is_a($exception, 'UnpriviledgedAccessException'))
Helper::Relocate( FrontController::GetLink('user', 'login', array('return_to' => FrontController::GetAbsoluteLink())) );
$template = new Template('exception', 'layouts/layout.php');
$template['code'] = $exception->getCode();
$template['message'] = $exception->getMessage();
$template['trace'] = $exception->getTraceAsString();
$template['EXCEPTION'] = true;
$template->display();
}
/** STATIC FUNCTIONS **/
public static function GetCurrentSection()
{
return isset($_REQUEST['section'])
? $_REQUEST['section']
: self::$default_section;
}
public static function GetCurrentSite()
{
if (isset($_REQUEST['site']))
return $_REQUEST['site'];
if (self::GetCurrentSection()==self::$default_section)
return self::$default_site;
$navigation = self::$sitemap->get_navigation(self::$default_section, self::$default_site);
if (!isset($navigation[self::GetCurrentSection()]))
return false;
$sites = $navigation[self::GetCurrentSection()]['items'];
reset($sites);
return key($sites);
}
public function GetAbsoluteLink($section = null, $site = null, $parameters = array())
{
$arguments = func_get_args();
$link = call_user_func_array(array('FrontController', 'GetLink'), $arguments);
return (isset($_SERVER['HTTPS'])?'https':'http').'://'.Config::Get('system', 'host').$link;
}
public static function GetLink($section = null, $site = null, $parameters = array())
{
$arguments = func_get_args();
$parameters = call_user_func_array(array('self', 'GetLinkParameters'), $arguments);
$append = '';
if (isset($parameters['#']))
{
$append = '#'.$parameters['#'];
unset($parameters['#']);
}
if (empty($_SERVER['MOD_REWRITE']))
return 'index.php?'.http_build_query($parameters, '', '&').$append;
$url = $parameters['section'];
unset($parameters['section']);
if (isset($parameters['site']))
{
$url .= '/'.$parameters['site'];
if (isset($parameters['tab']))
$url .= '/'.$parameters['tab'];
unset($parameters['site'], $parameters['tab']);
}
$parameters = array_filter($parameters);
if (!empty($parameters))
$url .= '?'.http_build_query($parameters, '', $GLOBALS['VIA_AJAX'] ? '&' : '&').$append;
return $url;
}
public function GetLinkParameters($section = null, $site = null, $parameters = array())
{
if (func_num_args()==2 and is_array($site))
{
$parameters = $site;
$site = $section;
$section = self::GetCurrentSection();
}
elseif (func_num_args()==1 and is_array($section))
{
$parameters = $section;
$site = self::GetCurrentSite();
$section = self::GetCurrentSection();
}
elseif (func_num_args()==1 and $section=='DEFAULT')
{
$site = self::$default_site;
$section = self::$default_section;
}
elseif (func_num_args()==1)
{
$site = $section;
$section = self::GetCurrentSection();
}
elseif (func_num_args()==0)
{
$site = self::GetCurrentSite();
$section = self::GetCurrentSection();
}
if ($section==self::GetCurrentSection() and $site==self::GetCurrentSite() and !isset($parameters['tab']) and isset(self::$link_parameters['tab']))
$parameters['tab'] = self::$link_parameters['tab'];
elseif ($section==self::GetCurrentSection() and $site==self::GetCurrentSite() and !isset($parameters['tab']) and isset($_REQUEST['tab']))
$parameters['tab'] = $_REQUEST['tab'];
return array_merge($parameters, compact('section', 'site'));
}
public static function SetTab($value)
{
self::$link_parameters['tab'] = $value;
}
public static function AreWeAt($section, $site)
{
return $section==self::GetCurrentSection() and $site==self::GetCurrentSite();
}
}
<?php
class Helper
{
public static function Relocate($url)
{
$_SESSION['MESSAGES'] = serialize(Controller::GetMessages());
if (!headers_sent())
header('Location: '.$url);
else
echo '<script type="text/javascript">location.href="'.$url.'";</script><noscript><meta http-equiv="Refresh" content="0;url='.$url.'" /></noscript>';
die;
}
}
<?php
require './includes/bootstrap.inc.php';
$front_controller = new FrontController($GLOBALS['CONFIG']['DEFAULT_SECTION'], $GLOBALS['CONFIG']['DEFAULT_SITE']);
$front_controller->execute();
$layout = $front_controller->via_ajax
? $GLOBALS['CONFIG']['AJAX_LAYOUT']
: $GLOBALS['CONFIG']['LAYOUT'];
$location = User::IsLoggedIn()
? Factory::Get('location')->load(User::GetLocation())
: Factory::Get('location')->load(Location::Locate());
$front_controller->get_template()->assign('LOCATION', $location);
$front_controller->get_template()->register_filter('gzip');
$front_controller->display($layout);
<?php
class Sitemap implements ArrayAccess
{
protected $sitemap = array();
protected $sitemap_categories = array();
public function __construct()
{
$this->sitemap_categories = DBManager::Get()->query("SELECT category_key, title, position, visible, action FROM sitemap_categories ORDER BY position ASC")->to_array('category_key');
$this->sitemap = DBManager::Get()->query("SELECT category_key, `key`, position, title, action, parameters, visible, required_permission, divider FROM sitemap ORDER BY position ASC")->distribute('category_key', 'key');
}
public function get_action($section, $site)
{
if (!isset($this->sitemap[$section], $this->sitemap[$section][$site]))
return false;
if (!$this->check_priviledges($this->sitemap[$section][$site]['required_permission']))
throw new UnpriviledgedAccessException();
$action = array_combine(array('controller', 'method'), explode(':', $this->sitemap[$section][$site]['action']));
$action['parameters'] = $this->sitemap[$section][$site]['parameters'];
return $action;
}
private function check_priviledges($permission)
{
if (is_null($permission))
return true;
if (is_string($permission))
$permission = explode(',', $permission);
foreach ($permission as $p)
if (User::HasFlag($p))
return true;
return false;
}
public function get_title($section, $site)
{
if (!isset($this->sitemap[$section], $this->sitemap[$section][$site]))
return false;
return $this->sitemap_categories[$section]['title'].' - '.$this->sitemap[$section][$site]['title'];
}
public function get_navigation($section, $site)
{
$navigation = array();
foreach ($this->sitemap_categories as $key => $category)
{
if (!$category['visible'])
continue;
$item = array(
'title' => $category['title'],
'section' => $key,
'site' => ($key==$section) ? $site : null,
'active' => $section_active=($key==$section),
'items' => $this->get_subnavigation($key, $site, $section_active),
);
if (!empty($item['items']))
$navigation[$key] = $item;
}
return $navigation;
}
private function get_subnavigation($section, $site, $section_active = false)
{
$navigation = array();
foreach ((array)@$this->sitemap[$section] as $key => $item)
{
if (!$item['visible'] or !$this->check_priviledges($item['required_permission']))
continue;
$item = array(
'title' => $item['title'],
'divider' => $item['divider'],
'section' => $section,
'site' => $key,
'active' => $section_active and ($key==$site),
);
$navigation[$key] = $item;
}
return $navigation;
}
/** ARRAY ACCESS **/
public function offsetExists($offset)
{
return isset($this->sitemap[$offset]);
}
public function offsetGet($offset)
{
return isset($this->sitemap[$offset])
? $this->sitemap[$offset]
: null;
}
public function offsetSet($offset, $value)
{
$this->sitemap[$offset] = $value;
}
public function offsetUnset($offset)
{
unset($this->sitemap[$offset]);
}
}
class UnpriviledgedAccessException extends Exception
{
public function __construct($message = null, $code = null, $previous_exception = null)
{
parent::__construct('Unpriviledged access!', FrontController::E_UNPRIVILEDGED_ACCESS);
}
}
<?php
// Needs set_include_path()
class Template implements ArrayAccess
{
protected $filename;
protected $variables = array();
protected $layout;
protected $filters = array();
protected $blank = false;
protected $content = null;
protected $rendered = false;
public function __construct($filename, $layout = false)
{
if ($this->blank = ($filename === false))
{
$this->content = ($layout === false)
? ''
: $layout;
}
else
{
$this->filename = $filename;
$this->layout = $layout;
}
}
public function register_filter($filter_name)
{
$arguments = func_get_args();
$this->filters[$filter_name] = array_slice($arguments, 1);
return $this;
}
public function assign($variable, $value = null)
{
if (is_array($variable) and is_null($value))
$this->set_variables($variable);
elseif (is_array($variable))
$this->set_variables(array_combine($variable, array_fill(0, count($variable), $value)));
else
$this->variables[$variable] = $value;
return $this;
}
public function set_variables($variables)
{
$this->variables = array_merge($this->variables, $variables);
return $this;
}
public function get_variable($key)
{
return isset($this->variables[$key])
? $this->variables[$key]
: null;
}
public function get_variables()
{
return $this->variables;
}
public function evaluate($string, $url_encoded = false)
{
return self::Interpolate($string, $this->variables, $url_encoded);
}
public function set_filename($filename)
{
$this->filename = $filename;
return $this;
}
public function set_layout($layout)
{
$this->layout = $layout;
return $this;
}
public function remove_layout()
{
$this->layout = null;
return $this;
}
public function has_layout()
{
return !empty($this->layout);
}
public function append($variable, $value)
{
if (!isset($this->variables[$variable]))
$this->variables[$variable] = $value;
elseif (is_array($this->variables[$variable]))
$this->extend($variable, $value);
elseif (is_numeric($this->variables[$variable]))
$this->variables[$variable] += $value;
else
$this->variables[$variable] .= $value;
return $this;
}
public function extend($variable, $value)
{
if (!isset($this->variables[$variable]))
$this->variables[$variable] = array();
elseif (!is_array($this->variables[$variable]))
$this->variables[$variable] = array($this->variables[$variable]);
array_push($this->variables[$variable], $value);
return $this;
}
protected function load_template($identifier)
{
$__original_filename = $__filename = $identifier;
$found = false;
if (!($found = file_exists($__filename)) and strpos($__filename, '.php') === false)
$__filename .= '.php';
if (!$found and !($found = file_exists($__filename)))
$__filename = './templates/'.$__filename;
if (!$found and !($found = file_exists($__filename)))
$__filename = './templates/'.basename($__filename);
if ($found or file_exists($__filename))
return file_get_contents($__filename);
throw new Template_Exception('Could not find template file "'.$identifier.'"');
}
private function _render($__variables, $__filename)
{
$__template = $this->load_template($__filename);
extract($__variables);
ob_start();
eval('?>'.$__template.'<?');
$content = ob_get_clean();
return $content;
}
public function render_partials($filename, $array, $parameters = array())
{
$result = array();
foreach (array_values($array) as $index => $item)
{
$item = array_merge($parameters, $item);
array_push($result, $this->render_partial($filename, $item, $index));
}
return implode("\n", $result);
}
public function render_partial($filename, $variables = array(), $index = null)
{
/*
$variables = array_merge($this->variables, $variables);
*/
if (!is_null($index))
$variables = array_merge(array('partial_index'=>$index), $variables);
if (file_exists($filename))
return $this->_render($variables, $filename);
$partial_file = ($filename .= '.partial');
if (count(glob($partial_file.'.*', GLOB_NOSORT)))
return $this->_render($variables, $partial_file);
$partial_file = './templates/partials/'.$filename;
if (count(glob($partial_file.'.*', GLOB_NOSORT)))
return $this->_render($variables, $partial_file);
$partial_file = dirname($this->filename).'/'.$filename;
if (count(glob($partial_file.'.*', GLOB_NOSORT)))
return $this->_render($variables, $partial_file);
$partial_file = dirname($this->filename).'/'.basename($filename, '.partial');
if (count(glob($partial_file, GLOB_NOSORT)))
return $this->_render($variables, $partial_file);
$partial_file = dirname($this->filename).'/'.basename($this->filename, '.php').'.'.$filename;
if (count(glob($partial_file.'.*', GLOB_NOSORT)))
return $this->_render($variables, $partial_file);
$partial_file = './templates/'.$filename;
if (count(glob($partial_file.'.*', GLOB_NOSORT)))
return $this->_render($variables, $partial_file);
if ($this->layout)
{
$partial_file = dirname($this->layout).'/'.basename($this->layout, '.php').'.'.$filename;
if (count(glob($partial_file.'.*', GLOB_NOSORT)))
return $this->_render($variables, $partial_file);
}
$partial_file = $filename;
return $this->_render($variables, $partial_file);
}
public function render()
{
if (!$this->blank)
$this->content = $this->_render($this->variables, $this->filename);
if ($this->layout)
{
$variables = array_merge($this->variables, array(
'CONTENT' => $this->content,
));
$this->content = $this->_render($variables, $this->layout);
}
$this->rendered = true;
return $this;
}
public function parse()
{
if (!$this->rendered)
$this->render();
return $this->content;
}
public function fetch($text_only = false)
{
$this->parse();
return $this->content;
}
public function display()
{
$arguments = func_get_args();
call_user_func_array(array($this, 'fetch'), $arguments);
$this->execute_filters();
print $this->content;
}
public function execute_filters()
{
foreach ($this->filters as $filter => $arguments)
{
@include_once dirname(__FILE__).'/plugins/filter.'.$filter.'.php';
array_unshift($arguments, $this->content);
$this->content = call_user_func_array('filter_'.$filter, $arguments);
}
}
public function __toString()
{
return $this->content;
}
public static function Interpolate($string, $parameters, $url_encoded = false)
{
if (is_array($string))
return 'Invalid call';
$replaces = array();
foreach ((array)$parameters as $key => $value)
{
if (strpos($string, '#{'.$key) === false)
continue;
if (is_object($value))
$value = get_object_vars($value);
elseif (!is_array($value))
$value = array(-1 => $value);
foreach ($value as $index => $data)
{
if (is_array($data))
continue;
$idx = '#{'.$key.($index!=-1 ? '.'.$index : '').'}';
$replaces[$idx] = $url_encoded
? urlencode($data)
: $data;
/** Ugly hack for now **/
$idx = '#{'.$key.($index!=-1 ? '.'.$index : '').'+1}';
$replaces[$idx] = $url_encoded
? urlencode($data + 1)
: $data + 1;
}
}
return str_replace(array_keys($replaces), array_values($replaces), $string);
}
/** Array Access **/
public function offsetExists($offset)
{
return isset($this->variables[$offset]);
}
public function offsetGet($offset)
{
return isset($this->variables[$offset])
? $this->variables[$offset]
: null;
}
public function offsetSet($offset, $value)
{
$this->variables[$offset] = $value;
}
public function offsetUnset($offset)
{
unset($this->variables[$offset]);
}
}
class Template_Exception extends Exception
{
public function __construct($message = null, $code = null, $last_exception = null)
{
parent::__construct('[TEMPLATE] '.$message, $code, $last_exception);
}
}
class DBTemplate extends Template
{
protected function load_template($identifier)
{
return ($template = DBManager::Get()->query("SELECT template FROM templates WHERE code = ?", $identifier)->fetch_item())
? $template
: sprintf('Could not load template "%s" from db', $identifier);
}
public function evaluate()
{
$template = $this->load_template($this->filename);
return self::Interpolate($template, $this->variables);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment