├── LICENSE ├── MVC ├── MVC.php ├── Db │ ├── Table.php │ ├── Adapter │ │ └── Mysqli.php │ └── Adapter.php ├── Log │ ├── Entry.php │ ├── Adapter.php │ ├── Entry │ │ └── Filter.php │ └── Adapter │ │ └── File.php ├── Config.php ├── Exception.php ├── Model.php ├── Controller.php ├── Db.php ├── Router │ ├── Route.php │ └── DefaultRoute.php ├── Pagination.php ├── Registry.php ├── Router.php ├── View.php ├── Loader.php ├── User.php ├── User │ └── Session.php └── Config │ └── Ini.php └── README /LICENSE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmasters/php-mvc/HEAD/LICENSE -------------------------------------------------------------------------------- /MVC/MVC.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright (c) 2009 Ross Masters. 8 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 9 | * @version 0.1 10 | */ 11 | 12 | namespace MVC; 13 | 14 | /** 15 | * MVC core class 16 | * Contains version information, library constants and static methods 17 | * used throughout the library. 18 | * @category MVC 19 | * @package MVC 20 | * @copyright Copyright (c) 2009 Ross Masters. 21 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 22 | */ 23 | class MVC 24 | { 25 | /** 26 | * The library version number. 27 | */ 28 | const VERSION_NUMBER = 0.1; 29 | 30 | /** 31 | * Used by MVC\Router to define the default controller and action 32 | * to use if unable to determine one. 33 | */ 34 | const DEFAULT_CONTROLLER = 'Index'; 35 | const DEFAULT_ACTION = 'index'; 36 | } 37 | -------------------------------------------------------------------------------- /MVC/Db/Table.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright (c) 2009 Ross Masters. 8 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 9 | * @version 0.1 10 | */ 11 | 12 | namespace MVC\Db; 13 | use MVC\Db as Db; 14 | 15 | /** 16 | * Database table class 17 | * Used for data-table models - provides the default database adapter. 18 | * @category MVC 19 | * @package Db 20 | * @copyright Copyright (c) 2009 Ross Masters. 21 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 22 | */ 23 | class Table 24 | { 25 | /** 26 | * Database adapter 27 | * @var MVC\Db\Adapter 28 | */ 29 | protected $adapter; 30 | 31 | /** 32 | * Constructor 33 | * @param MVC\Db\Adapter $adapter Database adapter 34 | */ 35 | public function __construct($adapter = null) { 36 | // Set the default database adapter if none supplied 37 | if ($adapter == null) { 38 | $adapter = Db::getDefaultAdapter(); 39 | } 40 | $this->adapter = $adapter; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /MVC/Log/Entry.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright (c) 2009 Ross Masters. 8 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 9 | * @version 0.1 10 | */ 11 | 12 | namespace MVC\Log; 13 | 14 | /** 15 | * Log entry 16 | * An individual entry, held in MVC\Log\Adapter's. 17 | * @category MVC 18 | * @package Log 19 | * @copyright Copyright (c) 2009 Ross Masters. 20 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 21 | */ 22 | class Entry 23 | { 24 | /** 25 | * Log message 26 | * @var string 27 | */ 28 | protected $message; 29 | 30 | /** 31 | * When the entry was logged (an RFC 2822 formatted date string) 32 | * @var string 33 | */ 34 | protected $logged; 35 | 36 | /** 37 | * Constructor 38 | * @param string $message Log message 39 | */ 40 | public function __construct($message) { 41 | $this->message = $message; 42 | $this->logged = date('r'); 43 | } 44 | 45 | /** 46 | * String representation of log entry 47 | * @return string Log line 48 | */ 49 | public function __toString() { 50 | return "[$this->logged] $this->message"; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Disclaimer 2 | ---------- 3 | PHP-MVC is an MVC framework written for PHP 5.3. It is in very early development (it's being written alongside a web application) but is hoped to develop into a full framework in the process of creating said web app. Because of this it's wise to take the framework with a pinch of salt - nearly all the classes are documented and most of the core components work properly but there will only be as much documentation as is necessary to configure the framework. A sample app is coming soon. 4 | 5 | PHP-MVC requires PHP 5.3, since it takes advantage of namespaces and other 5.3 feautres. Database support is limited to MySQL (using Mysqli) and only has support for prepared statements. A multi-platform query-builder is planned. 6 | 7 | Inspiration and aims 8 | -------------------- 9 | This framework is written in mind of the larger, monolithic-stlye frameworks and the tiny "micro"-frameworks available. It aims to be about half-way between these extremes: containing enough libraries to write a fully capable web app yet not being restricted by desires to be as compact as possible. The framework aims to be as decoupled as possible, so that you can use as many or as few components as you like. 10 | 11 | As far as inspiration goes, this PHP-MVC is probably most like the Zend Framework - it has a similar controller and model API. 12 | 13 | If you decide to make something using PHP-MVC let me know, similarly if you have any ideas, requests or things you'd like the framework to do differently from others I'm interested :) 14 | -------------------------------------------------------------------------------- /MVC/Config.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright (c) 2009 Ross Masters. 8 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 9 | * @version 0.1 10 | */ 11 | 12 | namespace MVC; 13 | 14 | /** 15 | * Configuration reader 16 | * Reads configuration information from an adapter. 17 | * @category MVC 18 | * @package Config 19 | * @copyright Copyright (c) 2009 Ross Masters. 20 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 21 | */ 22 | abstract class Config 23 | { 24 | /** 25 | * Identifier of the config file 26 | * @var string 27 | */ 28 | protected $name; 29 | 30 | /** 31 | * Whether the config file can be updated by manipulating the class 32 | * @var bool 33 | */ 34 | protected $writable; 35 | 36 | /** 37 | * Constructor 38 | * @param string $name Identifier of the config file 39 | * @param bool $writable Whether the config can be updated 40 | */ 41 | public function __construct($name, $writable = false) { 42 | $this->name = $name; 43 | $this->writable = (bool) $writable; 44 | $this->init(); 45 | } 46 | 47 | /** 48 | * Initialisation hook for adapters 49 | */ 50 | abstract protected function init(); 51 | 52 | /** 53 | * Update method to save changes made to the configuration back 54 | * to the source file. 55 | */ 56 | abstract public function save(); 57 | } 58 | -------------------------------------------------------------------------------- /MVC/Exception.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright (c) 2009 Ross Masters. 8 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 9 | * @version 0.1 10 | */ 11 | 12 | namespace MVC; 13 | 14 | /** 15 | * Default library exception 16 | * Applies logging functionality to library exceptions. 17 | * @category MVC 18 | * @package Exception 19 | * @copyright Copyright (c) 2009 Ross Masters. 20 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 21 | */ 22 | class Exception extends \Exception 23 | { 24 | /** 25 | * Constructor 26 | * @param string $message Message to send 27 | * @param int $code Error code 28 | * @param bool $log Whether to log the exception 29 | */ 30 | public function __construct($message, $code = 0, $log = true) { 31 | // Make sure variables are assigned properly 32 | parent::__construct($message, $code); 33 | 34 | // Log the exception if enabled 35 | if ($log) { 36 | $this->log(); 37 | } 38 | } 39 | 40 | /** 41 | * Log the exception 42 | * Use the file log adapter to save the exception to file. 43 | * Note that you should set the default path for logs to save in 44 | * using MVC\Log\Adapter\File::setPath(). 45 | * @todo Create a new adapter to save more information (serialize it) 46 | * @todo User-defined logging method 47 | */ 48 | private function log() { 49 | $log = new Log\Adapter\File('exception.log'); 50 | // Log a brief message 51 | $log->addEntry("($this->code) $this->message"); 52 | $log->save(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /MVC/Log/Adapter.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright (c) 2009 Ross Masters. 8 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 9 | * @version 0.1 10 | */ 11 | 12 | namespace MVC\Log; 13 | 14 | /** 15 | * Logging adapter abstract 16 | * Provides a base for logging adapters. 17 | * @category MVC 18 | * @package Log 19 | * @copyright Copyright (c) 2009 Ross Masters. 20 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 21 | */ 22 | abstract class Adapter 23 | { 24 | /** 25 | * Log identifer 26 | * @var string 27 | */ 28 | protected $name; 29 | 30 | /** 31 | * Log entries (array of MVC\Log\Entry instances) 32 | * @var array 33 | */ 34 | protected $entries = array(); 35 | 36 | /** 37 | * Constructor 38 | * @param string $name Identifer of log 39 | */ 40 | public function __construct($name) { 41 | $this->name = $name; 42 | 43 | $this->open(); 44 | } 45 | 46 | /** 47 | * Open log hook 48 | */ 49 | abstract protected function open(); 50 | 51 | /** 52 | * Get an entry by numerical index 53 | * @param int $index Numerical index 54 | * @return MVC\Log\Entry|false False if no index set 55 | */ 56 | public function getEntry($index) { 57 | if (isset($this->entries[$index])) { 58 | return $this->entries[$index]; 59 | } 60 | return false; 61 | } 62 | 63 | /** 64 | * Return all entries 65 | * @return array Entry array 66 | */ 67 | public function getEntries() { 68 | return $this->entries; 69 | } 70 | 71 | /** 72 | * Add an entry to the stack 73 | * @param string $message Message to add 74 | */ 75 | public function addEntry($message) { 76 | $this->entries[] = new Entry($message); 77 | } 78 | 79 | /** 80 | * Write entries to the log source 81 | */ 82 | abstract public function save(); 83 | } 84 | -------------------------------------------------------------------------------- /MVC/Db/Adapter/Mysqli.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright (c) 2009 Ross Masters. 8 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 9 | * @version 0.1 10 | */ 11 | 12 | namespace MVC\Db\Adapter; 13 | use \MVC\Exception as Exception; 14 | 15 | /** 16 | * MySQLi database adapter 17 | * Extends database support to MySQL databases using the MySQL improved PHP extension. 18 | * @category MVC 19 | * @package Db 20 | * @copyright Copyright (c) 2009 Ross Masters. 21 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 22 | */ 23 | class Mysqli extends \MVC\Db\Adapter 24 | { 25 | /** 26 | * Connect to the database 27 | * @throws \MVC\Exception If there is a connection error 28 | */ 29 | protected function connect() { 30 | // Attempt to create a handle 31 | $this->handle = mysqli_connect($this->host, 32 | $this->user, $this->password, $this->name, $this->port, $this->socket); 33 | 34 | // Forward connection errors 35 | if (mysqli_connect_errno()) { 36 | throw new Exception(mysqli_error(), mysqli_errno()); 37 | } 38 | } 39 | 40 | /** 41 | * Create a prepared statement 42 | * @param string $query SQL to prepare 43 | * @return MySQLi_Stmt Prepared statement 44 | * @throws MVC\Exception If SQL errors were found 45 | */ 46 | public function prepare($query) { 47 | // Catch SQL errors from the prepared statement 48 | if (!$stmt = mysqli_prepare($this->handle, $query)) { 49 | throw new Exception($this->error()); 50 | } 51 | 52 | return $stmt; 53 | } 54 | 55 | /** 56 | * Get the last error message from the database 57 | * @return string Error emssage 58 | */ 59 | public function error() { 60 | return mysqli_error($this->handle); 61 | } 62 | 63 | /** 64 | * Get the last error code from the database 65 | * @return int Error code 66 | */ 67 | public function errno() { 68 | return mysqli_errno($this->handle); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /MVC/Log/Entry/Filter.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright (c) 2009 Ross Masters. 8 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 9 | * @version 0.1 10 | */ 11 | 12 | namespace MVC\Log\Entry; 13 | 14 | /** 15 | * Filtered log entry 16 | * Provides an interface for reading plain-text log entries in a set format. 17 | * @category MVC 18 | * @package Log 19 | * @copyright Copyright (c) 2009 Ross Masters. 20 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 21 | */ 22 | class Filter extends \MVC\Log\Entry 23 | { 24 | /** 25 | * Mask variables 26 | * @var array 27 | */ 28 | private $variables = array(); 29 | 30 | /** 31 | * Constructor 32 | * @param string $mask Mask of filter 33 | * @param string $text Text to parse 34 | */ 35 | public function __construct($mask, $text) { 36 | $this->match($mask, $text); 37 | $this->logged = $this->variables['logged']; 38 | $this->message = $this->variables['message']; 39 | } 40 | 41 | /** 42 | * Match a mask and text 43 | * @param string $mask Mask of filter 44 | * @param string $text Text to parse 45 | */ 46 | private function match($mask, $text) { 47 | // firstly match vars from the mask 48 | // In format of '%name' but only logged|message atm 49 | preg_match_all('/%(logged|message)/', $mask, $maskVars); 50 | 51 | // Build a regex match string 52 | $maskVars[0] = array_map(function ($var) { 53 | return "/$var/"; 54 | }, $maskVars[0]); 55 | $regexMask = preg_replace($maskVars[0], '(.+)', $mask); 56 | 57 | // match vars from actual message 58 | // escape regex characters 59 | $regexMask = str_replace(array('[', ']'), array('\[', '\]'), $regexMask); 60 | // add regex newlines instead of actual new lines 61 | $regexMask = str_replace(array("\n", "\r"), array('\n', '\r'), $regexMask); 62 | // match 63 | preg_match("/$regexMask/", $text, $matches); 64 | 65 | // match variables 66 | // remove original regex string from matches and reindex 67 | array_shift($matches); 68 | $matches = array_values($matches); 69 | // join labels with variables 70 | foreach ($matches as $index => $value) { 71 | $this->variables[$maskVars[1][$index]] = $value; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /MVC/Model.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright (c) 2009 Ross Masters. 8 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 9 | * @version 0.1 10 | */ 11 | 12 | namespace MVC; 13 | 14 | /** 15 | * Model base for retrieving data from a database 16 | * @category MVC 17 | * @package Model 18 | * @copyright Copyright (c) 2009 Ross Masters. 19 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 20 | */ 21 | abstract class Model 22 | { 23 | /** 24 | * Method to set properties of the model using the setName() methods 25 | * @param array $options Key/Value array of properties and values 26 | * @throws MVC\Exception If property didn't exist/didn't have a setter method 27 | */ 28 | public function setOptions(array $options) { 29 | // Rather than call method_exists each time check an array 30 | $methods = get_class_methods($this); 31 | // Loop through each option and set 32 | foreach ($options as $property => $value) { 33 | $method = 'set' . ucfirst($property); 34 | // If no setter method is found throw an exception 35 | if (!in_array($method, $methods)) { 36 | throw new Exception("Can't set value for $name."); 37 | } 38 | $this->$method($value); 39 | } 40 | } 41 | 42 | /** 43 | * Overloaded setter method 44 | * @param string $name Name of property 45 | * @param mixed $value Value to set property to 46 | * @throws \MVC\Exception If no setter method exists 47 | */ 48 | public function __set($name, $value) { 49 | $method = 'set' . ucfirst($name); 50 | // If no setter method is found throw an exception 51 | if (!method_exists($this, $method)) { 52 | throw new Exception("Can't set value for $name."); 53 | } 54 | 55 | $this->$method($value); 56 | } 57 | 58 | /** 59 | * Overloaded getter method 60 | * @param string $name Name of property 61 | * @throws \MVC\Exception If no getter method exists 62 | */ 63 | public function __get($name) { 64 | $method = 'get' . ucfirst($name); 65 | // If no getter method is found throw an exception 66 | if (!method_exists($this, $method)) { 67 | throw new Exception("Can't get value for $name."); 68 | } 69 | 70 | return $this->$method(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /MVC/Controller.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright (c) 2009 Ross Masters. 8 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 9 | * @version 0.1 10 | */ 11 | 12 | namespace MVC; 13 | 14 | /** 15 | * Abstract controller base 16 | * @category MVC 17 | * @package Controller 18 | * @copyright Copyright (c) 2009 Ross Masters. 19 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 20 | */ 21 | class Controller 22 | { 23 | /** 24 | * Registry instance for easy manipulation 25 | * @var MVC\Registry|null 26 | */ 27 | protected static $registry; 28 | 29 | /** 30 | * Router instance 31 | * @var MVC\Router|null 32 | */ 33 | protected static $router; 34 | 35 | /** 36 | * An array of controller actions 37 | * @var array|null 38 | */ 39 | private static $actions; 40 | 41 | /** 42 | * Constructor 43 | * Loads actions and calls initialisation hook 44 | */ 45 | public function __construct() { 46 | $this->getActions(); 47 | 48 | // Call controller initialisation hook 49 | if (method_exists($this, 'init')) { 50 | $this->init(); 51 | } 52 | } 53 | 54 | /** 55 | * Get the actions the controller implements 56 | * Only check the action list once and store it statically 57 | * @return array 58 | */ 59 | public function getActions() { 60 | // If controller actions haven't been read yet 61 | if (self::$actions == null) { 62 | $methods = get_class_methods($this); 63 | self::$actions = array(); 64 | foreach ($methods as $method) { 65 | // Read only methods ending in the 'Action' suffix 66 | if (substr($method, -6) == 'Action') { 67 | self::$actions[] = substr($method, 0, -6); 68 | } 69 | } 70 | } 71 | 72 | return self::$actions; 73 | } 74 | 75 | /** 76 | * Set the registry instance 77 | * @var MVC\Registry 78 | */ 79 | public static function setRegistry(Registry $registry) { 80 | self::$registry = $registry; 81 | } 82 | 83 | /** 84 | * Set the router instance 85 | * @var MVC\Router 86 | */ 87 | public static function setRouter(Router $router) { 88 | self::$router = $router; 89 | } 90 | 91 | /** 92 | * Controller helper; redirects the user to another page 93 | */ 94 | public function redirect($url, $httpStatus = 302) { 95 | header('Location: ' . $url, true, $httpStatus); 96 | // In case the client does not follow location headers 97 | die("You have been redirected"); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /MVC/Log/Adapter/File.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright (c) 2009 Ross Masters. 8 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 9 | * @version 0.1 10 | */ 11 | 12 | namespace MVC\Log\Adapter; 13 | use MVC\Exception as Exception; 14 | use MVC\Log\Entry as Entry; 15 | 16 | /** 17 | * Plain-text log file 18 | * Stores logs in plain text - one entry per line. 19 | * @category MVC 20 | * @package Log 21 | * @copyright Copyright (c) 2009 Ross Masters. 22 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 23 | */ 24 | class File extends \MVC\Log\Adapter 25 | { 26 | /** 27 | * Whether to automatically create log files if they don't exist 28 | * @var bool 29 | */ 30 | private $autoCreate; 31 | 32 | /** 33 | * Default path for log files 34 | * @var string 35 | */ 36 | private static $logPath; 37 | 38 | /** 39 | * Constructor 40 | * @param string $name Name of log file 41 | * @param bool $autoCreate Whether to create the log file if it doesn't exist 42 | */ 43 | public function __construct($name, $autoCreate = true) { 44 | $this->autoCreate = (bool) $autoCreate; 45 | // Call parent constructor 46 | parent::__construct($name); 47 | } 48 | 49 | /** 50 | * Set the default log path 51 | * @param string $path Path to log files 52 | */ 53 | public static function setPath($path) { 54 | self::$logPath = $path; 55 | } 56 | 57 | /** 58 | * Open hook 59 | * Opens file and parses 60 | * @throws MVC\Exception If file not found 61 | */ 62 | protected function open() { 63 | // Set path 64 | $path = self::$logPath . "/$this->name"; 65 | // Create an empty file if file doesn't exist or throw an exception 66 | if (!file_exists($path)) { 67 | if ($this->autoCreate) { 68 | file_put_contents($path, ''); 69 | } else { 70 | throw new Exception("No log file found in '$path'", 0, false); 71 | } 72 | } 73 | 74 | // Get and trim the log file 75 | $log = file_get_contents($path); 76 | $log = trim($log); 77 | 78 | // Parse log file 79 | $entries = explode("\n", $log); 80 | foreach ($entries as $entry) { 81 | if (!empty($entry)) { 82 | $entry = trim($entry); 83 | // Use the Filter Entry class to read logs 84 | $this->entries[] = new Entry\Filter("[%logged] %message", $entry); 85 | } 86 | } 87 | } 88 | 89 | /** 90 | * Save method 91 | */ 92 | public function save() { 93 | $path = self::$logPath . "/$this->name"; 94 | $log = implode("\n", $this->entries); 95 | file_put_contents($path, $log); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /MVC/Db/Adapter.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright (c) 2009 Ross Masters. 8 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 9 | * @version 0.1 10 | */ 11 | 12 | namespace MVC\Db; 13 | use MVC\Exception as Exception; 14 | 15 | /** 16 | * Abstract database adapter 17 | * @category MVC 18 | * @package Db 19 | * @copyright Copyright (c) 2009 Ross Masters. 20 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 21 | */ 22 | abstract class Adapter { 23 | /** 24 | * Hostname 25 | * @var string 26 | */ 27 | protected $host; 28 | 29 | /** 30 | * Username 31 | * @var string 32 | */ 33 | protected $user; 34 | 35 | /** 36 | * Password for user 37 | * @var string 38 | */ 39 | protected $password; 40 | 41 | /** 42 | * Name of database 43 | * @var string 44 | */ 45 | protected $name; 46 | 47 | /** 48 | * Port of database server 49 | * @var string 50 | */ 51 | protected $port; 52 | 53 | /** 54 | * Socket for database server 55 | * @var string 56 | */ 57 | protected $socket; 58 | 59 | /** 60 | * Database handle/resource 61 | * @var mixed 62 | */ 63 | protected $handle; 64 | 65 | /** 66 | * Constructor 67 | * @param array $info Connection information 68 | * @throws Exception If missing required credentials 69 | */ 70 | public function __construct(array $info) { 71 | // Check required credentials are present 72 | if (!isset($info['host'], $info['user'], $info['password'], $info['name'])) { 73 | throw new Exception("A host, user, password and database must be specified."); 74 | } 75 | 76 | // Assign credentials 77 | $this->host = $info['host']; 78 | $this->user = $info['user']; 79 | $this->password = $info['password']; 80 | $this->name = $info['name']; 81 | if (isset($info['port'])) { 82 | $this->port = $info['port']; 83 | } 84 | if (isset($info['socket'])) { 85 | $this->socket = $info['socket']; 86 | } 87 | 88 | // Call connect hook 89 | $this->connect(); 90 | } 91 | 92 | /** 93 | * Connect to the database 94 | */ 95 | abstract protected function connect(); 96 | 97 | /** 98 | * Create a prepared statement 99 | * @param string $query SQL query to prepare 100 | */ 101 | abstract public function prepare($query); 102 | 103 | /** 104 | * Return the last error message 105 | * @return string Error message 106 | */ 107 | abstract public function error(); 108 | 109 | /** 110 | * Return the last error code 111 | * @return int Error code 112 | */ 113 | abstract public function errno(); 114 | } 115 | -------------------------------------------------------------------------------- /MVC/Db.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright (c) 2009 Ross Masters. 8 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 9 | * @version 0.1 10 | */ 11 | 12 | namespace MVC; 13 | 14 | /** 15 | * MVC database factory 16 | * Creates and handles database adapters. 17 | * @category MVC 18 | * @package Db 19 | * @copyright Copyright (c) 2009 Ross Masters. 20 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 21 | */ 22 | class Db 23 | { 24 | /** 25 | * The default database adapter 26 | * @var MVC\Db\Adapter|null 27 | */ 28 | private static $defaultAdapter; 29 | 30 | /** 31 | * Factory method to create an adapter 32 | * @param string $adapter Adapter name 33 | * @param array $info Connection information 34 | * @param bool $autoDefault Automatically set the new adapter as the default database adapter 35 | * @return MVC\Db\Adapter The new database adapter 36 | * @throws MVC\Exception If connection information missing 37 | * @throws MVC\Exception If adapter not available 38 | */ 39 | public static function create($adapter, array $info, $autoDefault = true) { 40 | // Throw an exception if the required connection info is missing 41 | if (!isset($info['host'], $info['user'], $info['password'], $info['name'])) { 42 | throw new Exception("A host, user, password and database must be specified."); 43 | } 44 | 45 | // Normalise the adapter name 46 | $adapter = ucfirst(strtolower($adapter)); 47 | 48 | // Check the adapter is available in MVC\Db\Adapter 49 | $adapterFull = "MVC\Db\Adapter\\$adapter"; 50 | if (!class_exists($adapterFull)) { 51 | throw new Exception("The '$adapter' database adapter is not loaded."); 52 | } 53 | 54 | // Instantiate the adapter 55 | $dbAdapter = new $adapterFull($info); 56 | 57 | // Automatically set the new adapter as the default adapter 58 | if ($autoDefault) { 59 | self::setDefaultAdapter($dbAdapter); 60 | } 61 | 62 | return $dbAdapter; 63 | } 64 | 65 | /** 66 | * Create a prepared statement using the default adapter 67 | * @param string $query Query string to use 68 | * @return The adapter's prepared statement 69 | */ 70 | public static function prepare($query) { 71 | return self::$defaultAdapter->prepare($query); 72 | } 73 | 74 | /** 75 | * Set the default database adapter to a new adapter 76 | * @param MVC\Db\Adapter $database Adapter 77 | */ 78 | public static function setDefaultAdapter(Db\Adapter $database) { 79 | self::$defaultAdapter = $database; 80 | } 81 | 82 | /** 83 | * Fetch the default adapter 84 | * @return MVC\Db\Adapter 85 | * @throw MVC\Exception If no adapter set 86 | */ 87 | public static function getDefaultAdapter() { 88 | if (self::$defaultAdapter == null) { 89 | throw new Exception("No default database adapter set."); 90 | } 91 | return self::$defaultAdapter; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /MVC/Router/Route.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright (c) 2009 Ross Masters. 8 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 9 | * @version 0.1 10 | */ 11 | 12 | namespace MVC\Router; 13 | 14 | /** 15 | * Special-case route 16 | * Routes based on matches with a pattern (also matches variables). 17 | * @category MVC 18 | * @package Router 19 | * @copyright Copyright (c) 2009 Ross Masters. 20 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 21 | */ 22 | class Route 23 | { 24 | /** 25 | * Route URI to map against 26 | * @var string 27 | */ 28 | private $route; 29 | 30 | /** 31 | * Variables found in the route 32 | * @var array 33 | */ 34 | private $variables = array(); 35 | 36 | /** 37 | * Parts of the route to match against 38 | * @var array 39 | */ 40 | private $parts = array(); 41 | 42 | /** 43 | * Constructor 44 | * @param string $route Route URI 45 | */ 46 | public function __construct($route) { 47 | $route = trim($route, '/ '); 48 | $this->route = $route; 49 | 50 | $this->parseRoute(); 51 | } 52 | 53 | /** 54 | * Parse the route URI 55 | */ 56 | private function parseRoute() { 57 | // Explode it by forward-slashes 58 | foreach (explode('/', $this->route) as $pos => $part) { 59 | // If te part is a variable 60 | if (substr($part, 0, 1) == ':' && substr($part, 1, 1) != ':') { 61 | // Collect the name and save it's position 62 | $var = substr($part, 1); 63 | $this->variables[$pos] = $var; 64 | $this->parts[$pos] = $var; 65 | } else { 66 | // Collect the part 67 | $this->parts[$pos] = $part; 68 | } 69 | } 70 | } 71 | 72 | /** 73 | * Match route against a URI 74 | * @param string $path URI to match from 75 | * @return false|array False if no match, otherwise matched variables 76 | */ 77 | public function match($path) { 78 | $values = array(); 79 | $path = trim($path, '/ '); 80 | $path = explode('/', $path); 81 | 82 | // Reset numerical indexes 83 | $path = array_values($path); 84 | 85 | foreach ($path as $pos => $pathPart) { 86 | // No match if path is longer than route 87 | if (!array_key_exists($pos, $this->parts)) { 88 | return false; 89 | } 90 | 91 | $var = (isset($this->variables[$pos])) ? $this->variables[$pos] : null; 92 | $routePart = $this->parts[$pos]; 93 | 94 | // If the part isn't a variable compare it directly 95 | if ($var == null && $routePart != $pathPart) { 96 | return false; 97 | } 98 | 99 | // If the part is a variable store it 100 | if ($var != null) { 101 | $values[$var] = $pathPart; 102 | } 103 | } 104 | 105 | return $values; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /MVC/Pagination.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright (c) 2009 Ross Masters. 8 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 9 | * @version 0.1 10 | */ 11 | 12 | namespace MVC; 13 | 14 | /** 15 | * Pagination class 16 | * Provides a simple pagination interface for selecting groups of records. 17 | * @category MVC 18 | * @package Pagination 19 | * @copyright Copyright (c) 2009 Ross Masters. 20 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 21 | */ 22 | class Pagination 23 | { 24 | /** 25 | * Total records available in the table 26 | * @var int 27 | */ 28 | private $total; 29 | 30 | /** 31 | * Records to return per set (or page) 32 | * @var int 33 | */ 34 | private $perSet; 35 | 36 | /** 37 | * Number of the current set (1+) (or page) 38 | * @var int 39 | */ 40 | private $setNumber; 41 | 42 | /** 43 | * Sets available based on total and perSet 44 | * @var int 45 | */ 46 | private $sets; 47 | 48 | /** 49 | * Constructor 50 | * @param int $setNumber Current set number (or page) 51 | * @param int $perSet Number of records to return per set 52 | * @param int $total Total records in table 53 | */ 54 | public function __construct($setNumber, $perSet, $total) { 55 | $this->perSet = (int) $perSet; 56 | $this->total = (int) $total; 57 | $this->sets = (int) ceil($this->total / $perSet); 58 | 59 | $this->setNumber = $this->changeSet((int) $setNumber); 60 | } 61 | 62 | /** 63 | * Change the set to another number 64 | * @param int $setNumber New set number 65 | */ 66 | public function changeSet($setNumber) { 67 | // Minimum set number 68 | if ($setNumber < 0) { 69 | $setNumber = 1; 70 | } 71 | 72 | // Maximum set number 73 | if ($setNumber > $this->sets) { 74 | $setNumber = $this->sets; 75 | } 76 | 77 | return $setNumber; 78 | } 79 | 80 | /** 81 | * Return the number of records to return 82 | * If there are more than Pagination::perSet records return 83 | * maximum per set otherwise return the amount of records 84 | * left to fetch. 85 | * @return int Number of records 86 | */ 87 | public function getCount() { 88 | // Remainder of records 89 | $rem = $this->total - ($this->perSet * ($this->setNumber - 1)); 90 | // Return maximum number of records if greater than perSet 91 | if ($rem > $this->perSet) { 92 | return $this->perSet; 93 | } 94 | // Otherwise return number of records (lt perSet) 95 | return $rem; 96 | } 97 | 98 | /** 99 | * Get the offset (records onwards from 0) 100 | * @return int Offset 101 | */ 102 | public function getOffset() { 103 | $offset = ($this->perSet * ($this->setNumber - 1)); 104 | return ($offset < 1) ? 0 : $offset; 105 | } 106 | 107 | /** 108 | * Get the current set number 109 | * @return int Current set number 110 | */ 111 | public function getSetNumber() { 112 | return $this->setNumber; 113 | } 114 | 115 | /** 116 | * Get total number of sets available 117 | * return int Sets available 118 | */ 119 | public function getSets() { 120 | return $this->sets; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /MVC/Registry.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright (c) 2009 Ross Masters. 8 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 9 | * @version 0.1 10 | */ 11 | 12 | namespace MVC; 13 | 14 | /** 15 | * An alternative to PHP globals. Variables are stored in a simple 16 | * key/value store. 17 | * @category MVC 18 | * @package Registry 19 | * @copyright Copyright (c) 2009 Ross Masters. 20 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 21 | */ 22 | class Registry 23 | { 24 | /** 25 | * Singleton instance 26 | * @var Registry|null 27 | */ 28 | private static $instance; 29 | 30 | /** 31 | * Variable array 32 | * @var array 33 | */ 34 | private $variables = array(); 35 | 36 | /** 37 | * Get the singleton instance 38 | * @return Loader 39 | */ 40 | public static function instance() { 41 | if (self::$instance == null) { 42 | self::$instance = new self; 43 | } 44 | return self::$instance; 45 | } 46 | 47 | /** 48 | * Retrieve a variable from the store 49 | * @param string $name Key variable stored under 50 | * @return mixed|null Null if key not found 51 | */ 52 | public function __get($name) { 53 | // Check key exists 54 | if (!array_key_exists($name, $this->variables)) { 55 | return null; 56 | } 57 | 58 | return $this->variables[$name]; 59 | } 60 | 61 | /** 62 | * Store a variable 63 | * @param string $name Key to store under 64 | * @param mixed $value Value to store 65 | */ 66 | public function __set($name, $value) { 67 | // Simply store the value, overwriting any previous value 68 | $this->variables[$name] = $value; 69 | } 70 | 71 | /** 72 | * Check whether a variable has been stored 73 | * @param string $name Key to check under 74 | * @return bool Whether a variable has been stored 75 | */ 76 | public function __isset($name) { 77 | return isset($this->variables[$name]); 78 | } 79 | 80 | /** 81 | * Remove a variable from the store 82 | * @param string $name Key to remove under 83 | */ 84 | public function __unset($name) { 85 | unset($this->variables[$name]); 86 | } 87 | 88 | /** 89 | * Statically get variables from the store 90 | * @param string $name Key variable stored under 91 | * @return mixed|null Null if key not found 92 | */ 93 | public static function get($name) { 94 | $instance = self::instance(); 95 | return $instance->$name; 96 | } 97 | 98 | /** 99 | * Statically store variables in the store 100 | * @param string $name Key to store under 101 | * @param mixed $value Value to store 102 | */ 103 | public static function set($name, $value) { 104 | $instance = self::instance(); 105 | $instance->$name = $value; 106 | } 107 | 108 | /** 109 | * Statically check whether a variable has been stored 110 | * @param string $name Key to check under 111 | * @return bool Whether a variable has been stored 112 | */ 113 | public static function stored($name) { 114 | $instance = self::instance(); 115 | return isset($instance->$name); 116 | } 117 | 118 | /** 119 | * Statically remove a variable from the store 120 | * @param string $name Key to remove under 121 | */ 122 | public static function remove($name) { 123 | $instance = self::instance(); 124 | unset($instance->$name); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /MVC/Router.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright (c) 2009 Ross Masters. 8 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 9 | * @version 0.1 10 | */ 11 | 12 | namespace MVC; 13 | 14 | /** 15 | * URI router 16 | * Routes URIs using URL-based maps to controllers and actions 17 | * @category MVC 18 | * @package Router 19 | * @copyright Copyright (c) 2009 Ross Masters. 20 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 21 | */ 22 | class Router 23 | { 24 | /** 25 | * URI to route from 26 | * @var string 27 | */ 28 | private $uri; 29 | 30 | /** 31 | * Array of special maps for specific URIs 32 | * @var array 33 | */ 34 | public $routes = array(); 35 | 36 | /** 37 | * Data determined from examing the URI 38 | * @var array 39 | */ 40 | private $request = array(); 41 | 42 | /** 43 | * Constructor 44 | * @param string $uri URI to route from 45 | */ 46 | public function __construct($uri) { 47 | $this->uri = $uri; 48 | } 49 | 50 | /** 51 | * Initiate the router - using added routes and the default router 52 | */ 53 | public function getRoute() { 54 | $match = false; 55 | $this->request = array( 56 | 'controller' => '', 57 | 'action' => '', 58 | 'params' => array() 59 | ); 60 | 61 | // If special routes have been added try to match the uri with them 62 | if (count($this->routes) != 0) { 63 | foreach ($this->routes as $name => $route) { 64 | // If a match is found break out of the loop 65 | $variables = $route['route']->match($this->uri); 66 | if(is_array($variables)) { 67 | $match = $name; 68 | break; 69 | } 70 | } 71 | } 72 | 73 | // Use the default router if no match is found 74 | if ($match) { 75 | $info = $this->routes[$name]['info']; 76 | $this->request['controller'] = (isset($info['controller'])) ? $info['controller'] : MVC\MVC::DEFAULT_CONTROLLER; 77 | $this->request['action'] = (isset($info['action'])) ? $info['action'] : MVC\MVC::DEFAULT_ACTION; 78 | $this->request['params'] = (isset($info['params'])) ? $info['params'] : array(); 79 | $this->request['params'] = array_merge($this->request['params'], $variables); 80 | } else { 81 | $route = new Router\DefaultRoute($this->uri); 82 | $this->request = $route->getRequest(); 83 | } 84 | 85 | // Merge in GET params with superglobal, giving preference to those from the URI 86 | if (count($this->request['params']) != 0) { 87 | $_GET = array_merge($_GET, $this->request['params']); 88 | } 89 | } 90 | 91 | /** 92 | * Add a special route for the router to match against 93 | * @param string $name Name of the route 94 | * @param MVC\Router\Route $route Route object to match against 95 | * @param array $info Information (controller/action/params) about the route 96 | * @throws MVC\Exception If route name already used 97 | */ 98 | public function addRoute($name, Router\Route $route, array $info) { 99 | // Check name is unique 100 | if (array_key_exists($name, $this->routes)) { 101 | throw new Exception("Route already exists with name '$name'."); 102 | } 103 | 104 | $this->routes[$name] = array('route' => $route, 'info' => $info); 105 | } 106 | 107 | /** 108 | * Get the controller name (after calling getRoute()) 109 | * @return string Controller name 110 | */ 111 | public function getController() { 112 | return $this->request['controller']; 113 | } 114 | 115 | /** 116 | * Get the action name (after calling getRoute()) 117 | * @return string Action name 118 | */ 119 | public function getAction() { 120 | return $this->request['action']; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /MVC/View.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright (c) 2009 Ross Masters. 8 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 9 | * @version 0.1 10 | */ 11 | 12 | namespace MVC; 13 | 14 | /** 15 | * Load a view (template) and pass variables to it, while using PHP 16 | * as musch as possible for 'templating' features. 17 | * @category MVC 18 | * @package View 19 | * @copyright Copyright (c) 2009 Ross Masters. 20 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 21 | */ 22 | class View 23 | { 24 | /** 25 | * Path to view script 26 | * @var string 27 | */ 28 | private $path; 29 | 30 | /** 31 | * Template variable store 32 | * @var array 33 | */ 34 | private static $variables = array(); 35 | 36 | /** 37 | * Registry instance 38 | * @var Registry 39 | */ 40 | private static $registry; 41 | 42 | /** 43 | * Constructor 44 | * @param string $name Name of view to load 45 | */ 46 | public function __construct($name) { 47 | $this->path = APPLICATION_PATH . '/views/' . $name; 48 | $this->checkPath(); 49 | } 50 | 51 | /** 52 | * Set the registry instance 53 | * @param MVC\Registry $registry Registry object 54 | */ 55 | public static function setRegistry(Registry $registry) { 56 | self::$registry = $registry; 57 | } 58 | 59 | /** 60 | * Check the path of the view to check it exists 61 | * @throws MVC\Exception If view does not exist 62 | */ 63 | private function checkPath() { 64 | if (!file_exists($this->path)) { 65 | throw new Exception("View '" . basename($this->path) . "' not found in '$this->path'."); 66 | } 67 | } 68 | 69 | /** 70 | * Template variable setter 71 | * @param string $name Property name 72 | * @param string $value Property value 73 | */ 74 | public function __set($name, $value) { 75 | self::$variables[$name] = $value; 76 | } 77 | 78 | /** 79 | * Template variable getter 80 | * @param string $name Property name 81 | * @return mixed Property value 82 | */ 83 | public function __get($name) { 84 | return self::$variables[$name]; 85 | } 86 | 87 | /** 88 | * Check if a template variable exists 89 | * @param string $name Property name 90 | * @return bool True if property set 91 | */ 92 | public function __isset($name) { 93 | return isset(self::$variables[$name]); 94 | } 95 | 96 | /** 97 | * Remove a property from the store 98 | * @param string $name Property name 99 | */ 100 | public function __unset($name) { 101 | unset(self::$variables[$name]); 102 | } 103 | 104 | /** 105 | * Return the template variables 106 | * @return array 107 | */ 108 | public function getVars() { 109 | return self::$variables; 110 | } 111 | 112 | /** 113 | * Output the template 114 | * If development mode is enabled include comments noting the 115 | * start and end of templates. 116 | * Extract the template variables locally and include the view 117 | * executing any embedded PHP. 118 | * Finally flush output to the browser to speed up page delivery. 119 | */ 120 | public function output() { 121 | if (APPLICATION_ENV == 'development') { 122 | echo "\n"; 123 | } 124 | 125 | // define variables in the local scope 126 | extract(self::$variables); 127 | include $this->path; 128 | 129 | if (APPLICATION_ENV == 'development') { 130 | echo "\n"; 131 | } 132 | 133 | flush(); 134 | } 135 | 136 | /** 137 | * Output the template as a string 138 | * @return string View contents 139 | * @see MVC\View::output() 140 | */ 141 | public function __toString() { 142 | $this->output(); 143 | $contents = ob_get_contents(); 144 | ob_clean(); 145 | return $contents; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /MVC/Router/DefaultRoute.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright (c) 2009 Ross Masters. 8 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 9 | * @version 0.1 10 | */ 11 | 12 | namespace MVC\Router; 13 | 14 | /** 15 | * Default routing class 16 | * Routes URIs if there is no match with other routes. 17 | * @category MVC 18 | * @package Router 19 | * @copyright Copyright (c) 2009 Ross Masters. 20 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 21 | */ 22 | class DefaultRoute 23 | { 24 | /** 25 | * URI to route from 26 | * @var string 27 | */ 28 | private $uri; 29 | 30 | /** 31 | * Variables determined from URI 32 | * @var string 33 | */ 34 | private $request; 35 | 36 | /** 37 | * Constructor 38 | * @param string $uri URI to route from 39 | */ 40 | public function __construct($uri) { 41 | $this->uri = $uri; 42 | $this->parseUri(); 43 | } 44 | 45 | /** 46 | * Parse the URI to read params from it 47 | */ 48 | private function parseUri() { 49 | // Split the URI and clean it up, removing empty parts 50 | $this->uri = trim($this->uri, '/ '); 51 | $parts = array(); 52 | if (!empty($this->uri)) { 53 | $parts = explode('/', $this->uri); 54 | } 55 | 56 | // Reset numerical indexes 57 | $parts = array_values($parts); 58 | 59 | // These are the defaults we'll assume in case of missing params 60 | $this->request['controller'] = \MVC\MVC::DEFAULT_CONTROLLER; 61 | $this->request['action'] = \MVC\MVC::DEFAULT_ACTION; 62 | $this->request['params'] = array(); 63 | 64 | // Based on the number of parts we can detect controllers and 65 | // actions. If there are some missing we rely on the defaults 66 | $partCount = count($parts); 67 | switch ($partCount) { 68 | case 0: 69 | // nothing given so assume the default names 70 | break; 71 | case 1: 72 | // only one item, we'll assume it's the controller 73 | $this->request['controller'] = $parts[0]; 74 | break; 75 | case 2: 76 | // two items, assumably controller and action 77 | $this->request['controller'] = $parts[0]; 78 | $this->request['action'] = $parts[1]; 79 | break; 80 | case 3: 81 | // three items, one controller and one key/value pair 82 | // of GET parameters 83 | $this->request['controller'] = $parts[0]; 84 | $this->request['params'][$parts[1]] = $parts[2]; 85 | break; 86 | default: 87 | // For everything onwards we follow a pattern: 88 | // The first element is always the controller 89 | // If there's an even number of parts we'll include 90 | // an action as well, if not we'll assume the default 91 | // action. For all remaining elements we'll assume 92 | // they are GET parameters. 93 | 94 | $this->request['controller'] = array_shift($parts); 95 | 96 | // If the number of parts is even we have an action: 97 | if (($partCount % 2) == 0) { 98 | $this->request['action'] = array_shift($parts); 99 | } 100 | 101 | // Reset numerical indexes 102 | $parts = array_values($parts); 103 | 104 | // Loop through remaining elements, assigning to 105 | // key/value pairs 106 | for ($i = 0; $i < count($parts); $i++) { 107 | $this->request['params'][$parts[$i]] = $parts[$i++]; 108 | } 109 | break; 110 | } 111 | 112 | // Tidy up naming 113 | $this->request['controller'] = ucfirst(strtolower($this->request['controller'])); 114 | $this->request['action'] = strtolower($this->request['action']); 115 | } 116 | 117 | /** 118 | * Return the request array 119 | * @return array URL parts 120 | */ 121 | public function getRequest() { 122 | return $this->request; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /MVC/Loader.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright (c) 2009 Ross Masters. 8 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 9 | * @version 0.1 10 | */ 11 | 12 | namespace MVC; 13 | 14 | /** 15 | * Manages paths to libraries and provides an autoloader to load 16 | * classes and interfaces from them. 17 | * @category MVC 18 | * @package Loader 19 | * @copyright Copyright (c) 2009 Ross Masters. 20 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 21 | */ 22 | class Loader 23 | { 24 | /** 25 | * Singleton instance 26 | * @var Loader|null 27 | */ 28 | private static $instance; 29 | 30 | /** 31 | * Default library location 32 | * @var string|null 33 | */ 34 | private $defaultLibraryPath; 35 | 36 | /** 37 | * Paths for specific libraries 38 | * @var array 39 | */ 40 | private $paths = array(); 41 | 42 | /** 43 | * Get the singleton instance 44 | * @return Loader 45 | */ 46 | private static function instance() { 47 | if (self::$instance == null) { 48 | self::$instance = new self; 49 | } 50 | return self::$instance; 51 | } 52 | 53 | /** 54 | * Autoloader callback 55 | * The autoloader looks for classes in Loader::defaultLibraryPath 56 | * and in specified library paths using namespaces as a directory 57 | * structure. For example: 58 | * \MVCWeb\Loader should be \defaultLibraryPath\MVCWeb\Loader.php 59 | * And so on with sub-namespaces. 60 | * Use with spl_register_autoload(). 61 | * @param string $name Name of class/interface to load 62 | * @throws MVC\Exception If the expected location couldn't be found 63 | * @throws MVC\Exception If the class/interface wasn't found in the location 64 | */ 65 | public static function autoload($name) { 66 | $instance = self::instance(); 67 | 68 | // Split the class name by namespaces 69 | $nameParts = explode('\\', $name); 70 | // Remove empty keys 71 | array_map('trim', $nameParts); 72 | foreach ($nameParts as $k => $part) { 73 | if (empty($part)) { 74 | unset($nameParts[$k]); 75 | } 76 | } 77 | $nameParts = array_values($nameParts); 78 | 79 | if (array_key_exists($nameParts[0], $instance->paths)) { 80 | // If the class is of a recognised namespace redirect to its known path 81 | $path = $instance->paths[array_shift($nameParts)] . '/' . implode('/', $nameParts) . '.php'; 82 | } elseif (count($nameParts) == 1) { 83 | // If the class is in the global namespace assume it's located in APPLICATION_PATH 84 | $path = APPLICATION_PATH . "/$name.php"; 85 | } else { 86 | $path = $instance->defaultLibraryPath . '/' . implode('/', $nameParts) . '.php'; 87 | } 88 | 89 | // Check file exists 90 | if (!file_exists($path)) { 91 | throw new Exception("Couldn't find '$path' to load '$name'."); 92 | } 93 | 94 | require_once $path; 95 | 96 | // Check class/interface has been declared 97 | if (!class_exists($name) && !interface_exists($name)) { 98 | throw new Exception("Attempted to load '$name' but it wasn't declared in '$path'."); 99 | } 100 | } 101 | 102 | /** 103 | * Adds a lookup path for classes in a namespace 104 | * @param string $namespace Namespace for path 105 | * @param string $path Path to look in 106 | * @throws MVC\Exception If the path didn't exist 107 | */ 108 | public static function addPath($namespace, $path) { 109 | $instance = self::instance(); 110 | if (!is_dir($path)) { 111 | throw new Exception("The path given ('$path') does not exist."); 112 | } 113 | $instance->paths[$namespace] = $path; 114 | } 115 | 116 | /** 117 | * Sets the default path to libraries to look for classes 118 | * @param string $path Where libraries are stored by default 119 | * @throws MVC\Exception If the path didn't exist 120 | */ 121 | public static function setLibraryPath($path) { 122 | $instance = self::instance(); 123 | if (!is_dir($path)) { 124 | throw new Exception("The path given ('$path') does not exist."); 125 | } 126 | $instance->defaultLibraryPath = $path; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /MVC/User.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright (c) 2009 Ross Masters. 8 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 9 | * @version 0.1 10 | */ 11 | 12 | namespace MVC; 13 | 14 | /** 15 | * User class 16 | * Used to track user information and authenticate them. 17 | * @category MVC 18 | * @package User 19 | * @copyright Copyright (c) 2009 Ross Masters. 20 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 21 | * @todo Different adapters 22 | */ 23 | class User 24 | { 25 | /** 26 | * User information retrieved from database 27 | * @var array 28 | */ 29 | private $info = array(); 30 | 31 | /** 32 | * Salts used in password hashing method 33 | * @var array 34 | * @see MVC\User::setSalts() 35 | * @see MVC\User::password() 36 | */ 37 | private static $salts; 38 | 39 | /** 40 | * Constructor 41 | * @param int $userId User to retrieve info from 42 | */ 43 | public function __construct($userId = null) { 44 | if ($userId != null) { 45 | if (!$this->getInfo($userId)) { 46 | return false; 47 | } 48 | } 49 | } 50 | 51 | /** 52 | * Get information on a user 53 | * @param int $userId User to retrieve information on 54 | * @return false|void False if no user found, otherwise info added to object 55 | */ 56 | public function getInfo($userId) { 57 | // Query the database for information 58 | $check = \MVC\Db::prepare('SELECT id, username, email, registered FROM users WHERE id = ? LIMIT 1'); 59 | $check->bind_param('i', $userId); 60 | $check->execute(); 61 | $check->store_result(); 62 | // Return false if no user found 63 | if ($check->num_rows == 0) { 64 | $check->close(); 65 | return false; 66 | } 67 | 68 | // Store the information 69 | $check->bind_result( 70 | $this->info['id'], 71 | $this->info['username'], 72 | $this->info['email'], 73 | $this->info['registered'] 74 | ); 75 | $check->fetch(); 76 | $check->close(); 77 | } 78 | 79 | /** 80 | * Overloaded setter 81 | * Sets properties, but only once 82 | * @param string $name Name of property to set 83 | * @param mixed $value Value to set property to 84 | * @see MVC\User::authenticate() 85 | */ 86 | public function __set($name, $value) { 87 | if (isset($this->$name)) { 88 | throw new Exception("Couldn't set property '$name' since it was already set to '$this->$name'."); 89 | } 90 | $this->$name = $value; 91 | } 92 | 93 | /** 94 | * Overloaded getter for user information 95 | * @param string $name Property name 96 | * @return mixed|null 97 | */ 98 | public function __get($name) { 99 | return $this->info[$name]; 100 | } 101 | 102 | /** 103 | * Staticly authenticate the user by checing a username/password match 104 | * @param string $username Username to check 105 | * @param string $password Coupling password 106 | * @return false|MVC\User False if no match, user instance if they do 107 | */ 108 | public static function authenticate($username, $password) { 109 | // Prepare the password 110 | $password = self::password($password); 111 | // Retrieve data from the database 112 | $check = \MVC\Db::prepare('SELECT id, username, email, registered FROM users WHERE username = ? AND password = ? LIMIT 1'); 113 | $check->bind_param('ss', $username, $password); 114 | $check->execute(); 115 | $check->store_result(); 116 | // Return false if no match found 117 | if ($check->num_rows == 0) { 118 | $check->close(); 119 | return false; 120 | } 121 | 122 | // Setup a new user instance 123 | $user = new self; 124 | $check->bind_result( 125 | $userId, 126 | $userUsername, 127 | $userEmail, 128 | $userRegistered 129 | ); 130 | $check->fetch(); 131 | $check->close(); 132 | 133 | $user->id = $userId; 134 | $user->username = $userUsername; 135 | $user->email = $userEmail; 136 | $user->registered = $userRegistered; 137 | return $user; 138 | } 139 | 140 | /** 141 | * Make a hash of the password so it is unreadable should the database be accessed 142 | * Salt with 2 user-defined salts 143 | * @param string $password Password to hash 144 | * @return string Hashed password 145 | * @see MVC\User::setSalts() 146 | */ 147 | public static function password($password) { 148 | if (!isset(self::$salts[1], self::$salts[0])) { 149 | throw new Exception("No salts defined for password hashing."); 150 | } 151 | 152 | return sha1(self::$salts[1] . $password . self::$salts[0]); 153 | } 154 | 155 | /** 156 | * Set the salts used in the password hashing method 157 | * @param string $salt1 First salt 158 | * @param string $salt2 Second salt 159 | * @see MVC\User::password() 160 | */ 161 | public static function setSalts($salt1, $salt2) { 162 | self::$salts = array($salt1, $salt2); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /MVC/User/Session.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright (c) 2009 Ross Masters. 8 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 9 | * @version 0.1 10 | */ 11 | 12 | namespace MVC\User; 13 | use MVC\Db as Db; 14 | use MVC\User as User; 15 | 16 | /** 17 | * User session handler 18 | * Creates, resumes and closes user sessions. Prevents session hijacking. 19 | * @category MVC 20 | * @package User 21 | * @copyright Copyright (c) 2009 Ross Masters. 22 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 23 | */ 24 | class Session 25 | { 26 | /** 27 | * Salts for key generation 28 | * @var array 29 | */ 30 | private static $salts; 31 | 32 | /** 33 | * Create a new user session 34 | * @param MVC\User $user User object to create session for 35 | * @return MVC\User Modified user instance 36 | */ 37 | public static function create(\MVC\User $user) { 38 | // Prepare database params 39 | $userId = $user->id; 40 | $sessionKey = self::generateSessionKey($user->id); 41 | $logoutKey = self::generateLogoutKey($user->id); 42 | $expiry = 1; // 1 day 43 | // Prepare insert 44 | $session = Db::prepare('INSERT INTO sessions (user, created, expires, session_key, logout_key) VALUES (?, NOW(), ADDDATE(NOW(), ?), ?, ?)'); 45 | $session->bind_param('iiss', $userId, $expiry, $sessionKey, $logoutKey); 46 | $session->execute(); 47 | $session->close(); 48 | 49 | // Update user object with keys 50 | $user->sessionKey = $sessionKey; 51 | $user->logoutKey = $logoutKey; 52 | 53 | return $user; 54 | } 55 | 56 | /** 57 | * Set salts for session key and logout key 58 | * @param string $sessionKey Salt for session key 59 | * @param string $logoutKey Salt for logout key 60 | */ 61 | public static function setSalts($sessionKey, $logoutKey) { 62 | self::$salts = array('session_key' => $sessionKey, 63 | 'logout_key' => $logoutKey); 64 | } 65 | 66 | /** 67 | * Generate a session key using a user-defined salt 68 | * @param int $userId User id session for 69 | * @return string Session key 70 | */ 71 | private static function generateSessionKey($userId) { 72 | return sha1(self::$salts['session_key'] . time() . $userId); 73 | } 74 | 75 | /** 76 | * Generate a logout key using a user-defined salt 77 | * @param int $userId User id session for 78 | * @return string Logout key 79 | */ 80 | private static function generateLogoutKey($userId) { 81 | return sha1(self::$salts['logout_key'] . time() . $userId); 82 | } 83 | 84 | /** 85 | * Resume a user session 86 | * @param int $userId User id to resume 87 | * @param string $sessionKey Session key matched with user 88 | * @return false|MVC\User False if no session found or no user found, otherwise user object 89 | */ 90 | public static function resume($userId, $sessionKey) { 91 | // Check a session exists 92 | if (!$session = self::exists($userId, $sessionKey)) { 93 | return false; 94 | } 95 | 96 | // Create a user instance 97 | $user = new User($userId); 98 | // Return false if user not found 99 | if (!$user) { 100 | return false; 101 | } 102 | // Set session details with user 103 | $user->sessionKey = $session['session_key']; 104 | $user->logoutKey = $session['logout_key']; 105 | return $user; 106 | } 107 | 108 | /** 109 | * Close a user session 110 | * @param MVC\User $user User instance 111 | * @return bool True if session closed successfully 112 | */ 113 | public static function close(User $user) { 114 | // Prepare params 115 | $userId = $user->id; 116 | $sessionKey = $user->sessionKey; 117 | $logoutKey = $user->logoutKey; 118 | 119 | // Prepare delete query 120 | $delete = Db::prepare('DELETE FROM sessions WHERE user = ? AND session_key = ? AND logout_key = ? LIMIT 1'); 121 | $delete->bind_param('iss', $userId, $sessionKey, $logoutKey); 122 | $delete->execute(); 123 | $rows = $delete->affected_rows; 124 | $delete->close(); 125 | 126 | // If no session found return false 127 | // Shouldn't happen unless this method allowed to be called by non-authed users 128 | if ($rows == 0) { 129 | return false; 130 | } 131 | return true; 132 | } 133 | 134 | /** 135 | * Check whether the session exists 136 | * @param int $userId User id for session 137 | * @param string $sessionKey Associated session key 138 | * @return false|array False if session didn't exist, otherwise array of keys 139 | */ 140 | private static function exists($userId, $sessionKey) { 141 | // Prepare check query 142 | $query = 'SELECT session_key, logout_key FROM sessions WHERE user = ? AND session_key = ? AND expires > NOW() LIMIT 1'; 143 | $check = Db::prepare($query); 144 | $check->bind_param('is', $userId, $sessionKey); 145 | $check->execute(); 146 | $check->store_result(); 147 | // Return false if session didn't exist 148 | if ($check->num_rows == 0) { 149 | $check->close(); 150 | return false; 151 | } 152 | 153 | // Return session and logout keys 154 | $return = array(); 155 | $check->bind_result($return['session_key'], $return['logout_key']); 156 | $check->fetch(); 157 | $check->close(); 158 | 159 | return $return; 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /MVC/Config/Ini.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright (c) 2009 Ross Masters. 8 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 9 | * @version 0.1 10 | */ 11 | 12 | namespace MVC\Config; 13 | use MVC\Exception as Exception; 14 | 15 | /** 16 | * Configuration adapter for ini files 17 | * Extends Config to read and compile to ini files 18 | * @category MVC 19 | * @package Config 20 | * @copyright Copyright (c) 2009 Ross Masters. 21 | * @license http://wiki.github.com/rmasters/php-mvc/license New BSD License 22 | */ 23 | class Ini extends \MVC\Config 24 | { 25 | /** 26 | * Path to the file 27 | * @var string 28 | */ 29 | private $path; 30 | 31 | /** 32 | * File data parsed from the original file 33 | * @var array 34 | */ 35 | private $file; 36 | 37 | /** 38 | * Location of config files 39 | * @var string 40 | */ 41 | private static $configPath; 42 | 43 | /** 44 | * Initialisation hook 45 | * Loads the file and replaces user-defined PHP constants within it 46 | */ 47 | protected function init() { 48 | $this->load(); 49 | } 50 | 51 | /** 52 | * Loads the ini file 53 | * @throws MVC\Exception If file not found 54 | */ 55 | private function load() { 56 | // Set path, depending on default path being set 57 | if (self::$configPath != null) { 58 | $this->path = self::$configPath . "/$this->name"; 59 | } else { 60 | $this->path = $this->name; 61 | } 62 | 63 | // Throw an exception if the path couldn't be loaded 64 | if (!file_exists($this->path)) { 65 | throw new Exception("Config file '$this->name' not found in '$this->path'."); 66 | } 67 | 68 | // Load in and parse the ini file 69 | $file = parse_ini_file($this->path); 70 | 71 | /** 72 | * Loop through each key and split it into an array based on 73 | * period syntax: 74 | * database.user = ross 75 | * Equates to: 76 | * Array ( 77 | * 'database' => Array ( 78 | * 'user' => 'ross' 79 | * )) 80 | */ 81 | foreach ($file as $key => $value) { 82 | // split '.' delimited keys as arrays 83 | if (strpos($key, '.')) { 84 | // Explode based on periods and keep the last element as the final key 85 | $keys = explode('.', $key); 86 | $lastKey = array_pop($keys); 87 | // Set a reference to the base array for easier appending 88 | $ref =& $file; 89 | // For each key 90 | for ($i = 0; $i < count($keys); $i++) { 91 | // If no key exists make a new array at $ref 92 | if (!array_key_exists($keys[$i], $ref)) { 93 | $ref[$keys[$i]] = array(); 94 | } 95 | // And update $ref to the new array 96 | $ref =& $ref[$keys[$i]]; 97 | } 98 | // Finally set the last key and value at $ref 99 | $ref[$lastKey] = $value; 100 | // And remove the old key 101 | unset($file[$key]); 102 | } 103 | } 104 | 105 | // Replace constants with values 106 | $this->file = $this->replaceConstants($file); 107 | } 108 | 109 | /** 110 | * Find and replace user constants in the ini file 111 | */ 112 | private function replaceConstants(array $data) { 113 | // Get an array of constants grouped into ext/php/user etc. 114 | $constants = get_defined_constants(true); 115 | 116 | // Find and replace in every value 117 | foreach ($data as $key => $value) { 118 | $data[$key] = str_replace( 119 | array_keys($constants['user']), 120 | array_values($constants['user']), 121 | $value 122 | ); 123 | } 124 | 125 | return $data; 126 | } 127 | 128 | /** 129 | * Set values in the config instance 130 | * These don't take effect on the source file without save()ing first. 131 | * @param string $name Name of property 132 | * @param mixed $value Value to set property to 133 | */ 134 | public function __set($name, $value) { 135 | $this->file[$name] = $value; 136 | } 137 | 138 | /** 139 | * Get values from the config instance 140 | * @param string $name Name of property 141 | * @return mixed|null Value of property 142 | */ 143 | public function __get($name) { 144 | return $this->file[$name]; 145 | } 146 | 147 | /** 148 | * Check properties have been set 149 | * @param string $name Name of property 150 | * @return bool True if property set 151 | */ 152 | public function __isset($name) { 153 | return isset($this->file[$name]); 154 | } 155 | 156 | /** 157 | * Remove properties from the instance 158 | * @param string $name Name of property 159 | */ 160 | public function __unset($name) { 161 | unset($this->file[$name]); 162 | } 163 | 164 | /** 165 | * Save changes made to the instance 166 | * @throws MVC\Exception If attempted to save without making the instance writable 167 | */ 168 | public function save() { 169 | // If the instance wasn't made writable when instatiated 170 | if (!$this->writable) { 171 | throw new Exception("You must instantiate the config instance with \$writable set to true to save."); 172 | } 173 | 174 | // Compile the values 175 | $configString = $this->compile($this->file); 176 | 177 | // Save the string to file 178 | file_put_contents($this->path, $configString); 179 | } 180 | 181 | /** 182 | * Compile a data array into ini form 183 | * @param array $data Data to compile 184 | * @param string $configString String to append to 185 | * @return string Compiled string 186 | */ 187 | private function compile(array $data, $configString = '') { 188 | foreach ($data as $key => $value) { 189 | // impossible to differentiate between sections and array-style values 190 | // so only sections - no array-style values 191 | if (is_array($value)) { 192 | $configString = $this->compile($value); 193 | } else { 194 | // Simple append the value 195 | if (is_string($value)) { 196 | $value = "\"$value\""; 197 | } 198 | $configString .= "$key = $value\n"; 199 | } 200 | } 201 | return $configString; 202 | } 203 | 204 | /** 205 | * Set a default path for config files to be located 206 | * @param string $configPath Default path 207 | */ 208 | public static function setConfigPath($configPath) { 209 | self::$configPath = $configPath; 210 | } 211 | } 212 | --------------------------------------------------------------------------------