diff options
Diffstat (limited to 'lpf')
-rw-r--r-- | lpf/ext/HTTP_Accept.class.php | 659 | ||||
-rw-r--r-- | lpf/ext/README.txt | 11 | ||||
-rw-r--r-- | lpf/lib/Controller.class.php | 30 | ||||
-rw-r--r-- | lpf/lib/Mime.class.php | 45 | ||||
-rw-r--r-- | lpf/lib/Model.class.php | 9 | ||||
-rw-r--r-- | lpf/lib/Router.class.php | 110 | ||||
-rw-r--r-- | lpf/lib/Singleton.class.php | 12 | ||||
-rw-r--r-- | lpf/lib/View.class.php | 135 | ||||
-rw-r--r-- | lpf/views/pages/no-conf.html.php | 8 |
9 files changed, 1019 insertions, 0 deletions
diff --git a/lpf/ext/HTTP_Accept.class.php b/lpf/ext/HTTP_Accept.class.php new file mode 100644 index 0000000..5efaa5d --- /dev/null +++ b/lpf/ext/HTTP_Accept.class.php @@ -0,0 +1,659 @@ +<?php + +/** + * HTTP_Accept class for dealing with the HTTP 'Accept' header + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to the MIT License. + * The full text of this license is available at the following URL: + * http://www.opensource.org/licenses/mit-license.php + * + * @category HTTP + * @package HTTP_Accept + * @author Kevin Locke <kwl7@cornell.edu> + * @copyright 2007 Kevin Locke + * @license http://www.opensource.org/licenses/mit-license.php MIT License + * @version SVN: $Id: HTTP_Accept.php 22 2007-10-06 18:46:45Z kevin $ + * @link http://pear.php.net/package/HTTP_Accept + */ + +/** + * HTTP_Accept class for dealing with the HTTP 'Accept' header + * + * This class is intended to be used to parse the HTTP Accept header into + * usable information and provide a simple API for dealing with that + * information. + * + * The parsing of this class is designed to follow RFC 2616 to the letter, + * any deviations from that standard are bugs and should be reported to the + * maintainer. + * + * Often the class will be used very simply as + * <code> + * <?php + * $accept = new HTTP_Accept($_SERVER['HTTP_ACCEPT']); + * if ($accept->getQuality("image/png") > $accept->getQuality("image/jpeg")) + * // Send PNG image + * else + * // Send JPEG image + * ?> + * </code> + * + * However, for browsers which do not accurately describe their preferences, + * it may be necessary to check if a MIME Type is explicitly listed in their + * Accept header, in addition to being preferred to another type + * + * <code> + * <?php + * if ($accept->isMatchExact("application/xhtml+xml")) + * // Client specifically asked for this type at some quality level + * ?> + * </code> + * + * + * @category HTTP + * @package HTTP_Accept + * @access public + * @link http://pear.php.net/package/HTTP_Accept + */ +class HTTP_Accept +{ + /** + * Array of types and their associated parameters, extensions, and quality + * factors, as represented in the Accept: header. + * Indexed by [type][subtype][index], + * and contains 'PARAMS', 'QUALITY', and 'EXTENSIONS' keys for the + * parameter set, quality factor, and extensions set respectively. + * Note: Since type, subtype, and parameters are case-insensitive + * (RFC 2045 5.1) they are stored as lower-case. + * + * @var array + * @access private + */ + var $acceptedtypes = array(); + + /** + * Regular expression to match a token, as defined in RFC 2045 + * + * @var string + * @access private + */ + var $_matchtoken = '(?:[^[:cntrl:]()<>@,;:\\\\"\/\[\]?={} \t]+)'; + + /** + * Regular expression to match a quoted string, as defined in RFC 2045 + * + * @var string + * @access private + */ + var $_matchqstring = '(?:"[^\\\\"]*(?:\\\\.[^\\\\"]*)*")'; + + /** + * Constructs a new HTTP_Accept object + * + * Initializes the HTTP_Accept class with a given accept string + * or creates a new (empty) HTTP_Accept object if no string is given + * + * Note: The behavior is a little strange here to accomodate + * missing headers (to be interpreted as accept all) as well as + * new empty objects which should accept nothing. This means that + * HTTP_Accept("") will be different than HTTP_Accept() + * + * @access public + * @return object HTTP_Accept + * @param string $acceptstring The value of an Accept: header + * Will often be $_SERVER['HTTP_ACCEPT'] + * Note: If get_magic_quotes_gpc is on, + * run stripslashes() on the string first + */ + function HTTP_Accept() + { + if (func_num_args() == 0) { + // User wishes to create empty HTTP_Accept object + $this->acceptedtypes = array( + '*' => array( + '*' => array ( + 0 => array( + 'PARAMS' => array(), + 'QUALITY' => 0, + 'EXTENSIONS' => array() + ) + ) + ) + ); + return; + } + + $acceptstring = trim(func_get_arg(0)); + if (empty($acceptstring)) { + // Accept header empty or not sent, interpret as "*/*" + $this->acceptedtypes = array( + '*' => array( + '*' => array ( + 0 => array( + 'PARAMS' => array(), + 'QUALITY' => 1, + 'EXTENSIONS' => array() + ) + ) + ) + ); + return; + } + + $matches = preg_match_all( + '/\s*('.$this->_matchtoken.')\/' . // typegroup/ + '('.$this->_matchtoken.')' . // subtype + '((?:\s*;\s*'.$this->_matchtoken.'\s*' . // parameter + '(?:=\s*' . // optional =value + '(?:'.$this->_matchqstring.'|'.$this->_matchtoken.'))?)*)/', // value + $acceptstring, $acceptedtypes, + PREG_SET_ORDER); + + if ($matches == 0) { + // Malformed Accept header + $this->acceptedtypes = array( + '*' => array( + '*' => array ( + 0 => array( + 'PARAMS' => array(), + 'QUALITY' => 1, + 'EXTENSIONS' => array() + ) + ) + ) + ); + return; + } + + foreach ($acceptedtypes as $accepted) { + $typefamily = strtolower($accepted[1]); + $subtype = strtolower($accepted[2]); + + // */subtype is invalid according to grammar in section 14.1 + // so we ignore it + if ($typefamily == '*' && $subtype != '*') + continue; + + // Parse all arguments of the form "key=value" + $matches = preg_match_all('/;\s*('.$this->_matchtoken.')\s*' . + '(?:=\s*' . + '('.$this->_matchqstring.'|'. + $this->_matchtoken.'))?/', + $accepted[3], $args, + PREG_SET_ORDER); + + $params = array(); + $quality = -1; + $extensions = array(); + foreach ($args as $arg) { + array_shift($arg); + if (!empty($arg[1])) { + // Strip quotes (Note: Can't use trim() in case "text\"") + $len = strlen($arg[1]); + if ($arg[1][0] == '"' && $arg[1][$len-1] == '"' + && $len > 1) { + $arg[1] = substr($arg[1], 1, $len-2); + $arg[1] = stripslashes($arg[1]); + } + } else if (!isset($arg[1])) { + $arg[1] = null; + } + + // Everything before q=# is a parameter, after is an extension + if ($quality >= 0) { + $extensions[$arg[0]] = $arg[1]; + } else if ($arg[0] == 'q') { + $quality = (float)$arg[1]; + + if ($quality < 0) + $quality = 0; + else if ($quality > 1) + $quality = 1; + } else { + $arg[0] = strtolower($arg[0]); + // Values required for parameters, + // assume empty-string for missing values + if (isset($arg[1])) + $params[$arg[0]] = $arg[1]; + else + $params[$arg[0]] = ""; + } + } + + if ($quality < 0) + $quality = 1; + else if ($quality == 0) + continue; + + if (!isset($this->acceptedtypes[$typefamily])) + $this->acceptedtypes[$typefamily] = array(); + if (!isset($this->acceptedtypes[$typefamily][$subtype])) + $this->acceptedtypes[$typefamily][$subtype] = array(); + + $this->acceptedtypes[$typefamily][$subtype][] = + array('PARAMS' => $params, + 'QUALITY' => $quality, + 'EXTENSIONS' => $extensions); + } + + if (!isset($this->acceptedtypes['*'])) + $this->acceptedtypes['*'] = array(); + if (!isset($this->acceptedtypes['*']['*'])) + $this->acceptedtypes['*']['*'] = array( + 0 => array( + 'PARAMS' => array(), + 'QUALITY' => 0, + 'EXTENSIONS' => array() + ) + ); + } + + /** + * Gets the accepted quality factor for a given MIME Type + * + * Note: If there are multiple best matches + * (e.g. "text/html;level=4;charset=utf-8" matching both "text/html;level=4" + * and "text/html;charset=utf-8"), it returns the lowest quality factor as + * a conservative estimate. Further, if the ambiguity is between parameters + * and extensions (e.g. "text/html;level=4;q=1;ext=foo" matching both + * "text/html;level=4" and "text/html;q=1;ext=foo") the parameters take + * precidence. + * + * Usage Note: If the quality factor for all supported media types is 0, + * RFC 2616 specifies that applications SHOULD send an HTTP 406 (not + * acceptable) response. + * + * @access public + * @return double the quality value for the given MIME Type + * Quality values are in the range [0,1] where 0 means + * "not accepted" and 1 is "perfect quality". + * @param string $mimetype The MIME Type to query ("text/html") + * @param array $params Parameters of Type to query ([level => 4]) + * @param array $extensions Extension parameters to query + */ + function getQuality($mimetype, $params = array(), $extensions = array()) + { + $type = explode("/", $mimetype); + $supertype = strtolower($type[0]); + $subtype = strtolower($type[1]); + + if ($params == null) + $params = array(); + if ($extensions == null) + $extensions = array(); + + if (empty($this->acceptedtypes[$supertype])) { + if ($supertype == '*') + return 0; + else + return $this->getQuality("*/*", $params, $extensions); + } + + if (empty($this->acceptedtypes[$supertype][$subtype])) { + if ($subtype == '*') + return $this->getQuality("*/*", $params, $extensions); + else + return $this->getQuality("$supertype/*", $params, $extensions); + } + + $params = array_change_key_case($params, CASE_LOWER); + + $matches = $this->_findBestMatchIndices($supertype, $subtype, + $params, $extensions); + + if (count($matches) == 0) { + if ($subtype != '*') + return $this->getQuality("$supertype/*", $params, $extensions); + else if ($supertype != '*') + return $this->getQuality("*/*", $params, $extensions); + else + return 0; + } + + $minquality = 1; + foreach ($matches as $match) + if ($this->acceptedtypes[$supertype][$subtype][$match]['QUALITY'] < $minquality) + $minquality = $this->acceptedtypes[$supertype][$subtype][$match]['QUALITY']; + + return $minquality; + } + + /** + * Determines if there is an exact match for the specified MIME Type + * + * @access public + * @return boolean true if there is an exact match to the given + * values, false otherwise. + * @param string $mimetype The MIME Type to query (e.g. "text/html") + * @param array $params Parameters of Type to query (e.g. [level => 4]) + * @param array $extensions Extension parameters to query + */ + function isMatchExact($mimetype, $params = array(), $extensions = array()) + { + $type = explode("/", $mimetype); + $supertype = strtolower($type[0]); + $subtype = strtolower($type[1]); + + if ($params == null) + $params = array(); + if ($extensions == null) + $extensions = array(); + + return $this->_findExactMatchIndex($supertype, $subtype, + $params, $extensions) >= 0; + } + + /** + * Gets a list of all MIME Types explicitly accepted, sorted by quality + * + * @access public + * @return array list of MIME Types explicitly accepted, sorted + * in decreasing order of quality factor + */ + function getTypes() + { + $qvalues = array(); + $types = array(); + foreach ($this->acceptedtypes as $typefamily => $subtypes) { + if ($typefamily == '*') + continue; + + foreach ($subtypes as $subtype => $variants) { + if ($subtype == '*') + continue; + + $maxquality = 0; + foreach ($variants as $variant) + if ($variant['QUALITY'] > $maxquality) + $maxquality = $variant['QUALITY']; + + if ($maxquality > 0) { + $qvalues[] = $maxquality; + $types[] = $typefamily.'/'.$subtype; + } + } + } + + array_multisort($qvalues, SORT_DESC, SORT_NUMERIC, + $types, SORT_DESC, SORT_STRING); + + return $types; + } + + /** + * Gets the parameter sets for a given mime type, sorted by quality. + * Only parameter sets where the extensions set is empty will be returned. + * + * @access public + * @return array list of sets of name=>value parameter pairs + * in decreasing order of quality factor + * @param string $mimetype The MIME Type to query ("text/html") + */ + function getParameterSets($mimetype) + { + $type = explode("/", $mimetype); + $supertype = strtolower($type[0]); + $subtype = strtolower($type[1]); + + if (!isset($this->acceptedtypes[$supertype]) + || !isset($this->acceptedtypes[$supertype][$subtype])) + return array(); + + $qvalues = array(); + $paramsets = array(); + foreach ($this->acceptedtypes[$supertype][$subtype] as $acceptedtype) { + if (count($acceptedtype['EXTENSIONS']) == 0) { + $qvalues[] = $acceptedtype['QUALITY']; + $paramsets[] = $acceptedtype['PARAMS']; + } + } + + array_multisort($qvalues, SORT_DESC, SORT_NUMERIC, + $paramsets, SORT_DESC, SORT_STRING); + + return $paramsets; + } + + /** + * Gets the extension sets for a given mime type, sorted by quality. + * Only extension sets where the parameter set is empty will be returned. + * + * @access public + * @return array list of sets of name=>value extension pairs + * in decreasing order of quality factor + * @param string $mimetype The MIME Type to query ("text/html") + */ + function getExtensionSets($mimetype) + { + $type = explode("/", $mimetype); + $supertype = strtolower($type[0]); + $subtype = strtolower($type[1]); + + if (!isset($this->acceptedtypes[$supertype]) + || !isset($this->acceptedtypes[$supertype][$subtype])) + return array(); + + $qvalues = array(); + $extensionsets = array(); + foreach ($this->acceptedtypes[$supertype][$subtype] as $acceptedtype) { + if (count($acceptedtype['PARAMS']) == 0) { + $qvalues[] = $acceptedtype['QUALITY']; + $extensionsets[] = $acceptedtype['EXTENSIONS']; + } + } + + array_multisort($qvalues, SORT_DESC, SORT_NUMERIC, + $extensionsets, SORT_DESC, SORT_STRING); + + return $extensionsets; + } + + /** + * Adds a type to the set of accepted types + * + * @access public + * @param string $mimetype The MIME Type to add (e.g. "text/html") + * @param double $quality The quality value for the given MIME Type + * Quality values are in the range [0,1] where + * 0 means "not accepted" and 1 is + * "perfect quality". + * @param array $params Parameters of the type to add (e.g. [level => 4]) + * @param array $extensions Extension parameters of the type to add + */ + function addType($mimetype, $quality = 1, + $params = array(), $extensions = array()) + { + $type = explode("/", $mimetype); + $supertype = strtolower($type[0]); + $subtype = strtolower($type[1]); + + if ($params == null) + $params = array(); + if ($extensions == null) + $extensions = array(); + + $index = $this->_findExactMatchIndex($supertype, $subtype, $params, $extensions); + + if ($index >= 0) { + $this->acceptedtypes[$supertype][$subtype][$index]['QUALITY'] = $quality; + } else { + if (!isset($this->acceptedtypes[$supertype])) + $this->acceptedtypes[$supertype] = array(); + if (!isset($this->acceptedtypes[$supertype][$subtype])) + $this->acceptedtypes[$supertype][$subtype] = array(); + + $this->acceptedtypes[$supertype][$subtype][] = + array('PARAMS' => $params, + 'QUALITY' => $quality, + 'EXTENSIONS' => $extensions); + } + } + + /** + * Removes a type from the set of accepted types + * + * @access public + * @param string $mimetype The MIME Type to remove (e.g. "text/html") + * @param array $params Parameters of the type to remove (e.g. [level => 4]) + * @param array $extensions Extension parameters of the type to remove + */ + function removeType($mimetype, $params = array(), $extensions = array()) + { + $type = explode("/", $mimetype); + $supertype = strtolower($type[0]); + $subtype = strtolower($type[1]); + + if ($params == null) + $params = array(); + if ($extensions == null) + $extensions = array(); + + $index = $this->_findExactMatchIndex($supertype, $subtype, $params, $extensions); + + if ($index >= 0) { + $this->acceptedtypes[$supertype][$subtype] = + array_merge(array_slice($this->acceptedtypes[$supertype][$subtype], + 0, $index), + array_slice($this->acceptedtypes[$supertype][$subtype], + $index+1)); + } + } + + /** + * Gets a string representation suitable for use in an HTTP Accept header + * + * @access public + * @return string a string representation of this object + */ + function __toString() + { + $accepted = array(); + $qvalues = array(); + foreach ($this->acceptedtypes as $supertype => $subtypes) { + foreach ($subtypes as $subtype => $entries) { + foreach ($entries as $entry) { + $accepted[] = array('TYPE' => "$supertype/$subtype", + 'QUALITY' => $entry['QUALITY'], + 'PARAMS' => $entry['PARAMS'], + 'EXTENSIONS' => $entry['EXTENSIONS']); + $qvalues[] = $entry['QUALITY']; + } + } + } + + array_multisort($qvalues, SORT_DESC, SORT_NUMERIC, + $accepted); + + $str = ""; + foreach ($accepted as $accept) { + // Skip the catchall value if it is 0, since this is implied + if ($accept['TYPE'] == '*/*' && + $accept['QUALITY'] == 0 && + count($accept['PARAMS']) == 0 && + count($accept['EXTENSIONS'] == 0)) + continue; + + $str = $str.$accept['TYPE'].';'; + + foreach ($accept['PARAMS'] as $param => $value) { + if (preg_match('/^'.$this->_matchtoken.'$/', $value)) + $str = $str.$param.'='.$value.';'; + else + $str = $str.$param.'="'.addcslashes($value,'"\\').'";'; + } + + if ($accept['QUALITY'] < 1 || !empty($accept['EXTENSIONS'])) + $str = $str.'q='.$accept['QUALITY'].';'; + + foreach ($accept['EXTENSIONS'] as $extension => $value) { + if (preg_match('/^'.$this->_matchtoken.'$/', $value)) + $str = $str.$extension.'='.$value.';'; + else + $str = $str.$extension.'="'.addcslashes($value,'"\\').'";'; + } + + $str[strlen($str)-1] = ','; + } + + return rtrim($str, ','); + } + + /** + * Finds the index of an exact match for the specified MIME Type + * + * @access private + * @return int the index of an exact match if found, + * -1 otherwise + * @param string $supertype The general MIME Type to find (e.g. "text") + * @param string $subtype The MIME subtype to find (e.g. "html") + * @param array $params Parameters of Type to find ([level => 4]) + * @param array $extensions Extension parameters to find + */ + function _findExactMatchIndex($supertype, $subtype, $params, $extensions) + { + if (empty($this->acceptedtypes[$supertype]) + || empty($this->acceptedtypes[$supertype][$subtype])) + return -1; + + $params = array_change_key_case($params, CASE_LOWER); + + $parammatches = array(); + foreach ($this->acceptedtypes[$supertype][$subtype] as $index => $typematch) + if ($typematch['PARAMS'] == $params + && $typematch['EXTENSIONS'] == $extensions) + return $index; + + return -1; + } + + /** + * Finds the indices of the best matches for the specified MIME Type + * + * A "match" in this context is an exact type match and no extraneous + * matches for parameters or extensions (so the best match for + * "text/html;level=4" may be "text/html" but not the other way around). + * + * "Best" is interpreted as the entries that match the most + * parameters and extensions (the sum of the number of matches) + * + * @access private + * @return array an array of the indices of the best matches + * (empty if no matches) + * @param string $supertype The general MIME Type to find (e.g. "text") + * @param string $subtype The MIME subtype to find (e.g. "html") + * @param array $params Parameters of Type to find ([level => 4]) + * @param array $extensions Extension parameters to find + */ + function _findBestMatchIndices($supertype, $subtype, $params, $extensions) + { + $bestmatches = array(); + $bestlength = 0; + + if (empty($this->acceptedtypes[$supertype]) + || empty($this->acceptedtypes[$supertype][$subtype])) + return $bestmatches; + + foreach ($this->acceptedtypes[$supertype][$subtype] as $index => $typematch) { + if (count(array_diff_assoc($typematch['PARAMS'], $params)) == 0 + && count(array_diff_assoc($typematch['EXTENSIONS'], + $extensions)) == 0) { + $length = count($typematch['PARAMS']) + + count($typematch['EXTENSIONS']); + + if ($length > $bestlength) { + $bestmatches = array($index); + $bestlength = $length; + } else if ($length == $bestlength) { + $bestmatches[] = $index; + } + } + } + + return $bestmatches; + } +} + +// vim: set ts=4 sts=4 sw=4 et: +?> diff --git a/lpf/ext/README.txt b/lpf/ext/README.txt new file mode 100644 index 0000000..c8eddb6 --- /dev/null +++ b/lpf/ext/README.txt @@ -0,0 +1,11 @@ +These are class files that I've gathered from around the internet. + +I've renamed the files to follow a standard scheme. + +This is where each file came from: + +My Name : Original Name : From +HTTP_Accept.class.php : HTTP_Accept.php : http://kevinlocke.name/programs/http_accept.php + +~ Luke Shumaker <http://lukeshu.ath.cx> +Happy Hacking! diff --git a/lpf/lib/Controller.class.php b/lpf/lib/Controller.class.php new file mode 100644 index 0000000..05736ee --- /dev/null +++ b/lpf/lib/Controller.class.php @@ -0,0 +1,30 @@ +<?php + +class Controller { + /** + * Show a $view, in the most appropriate format (according to file + * extension and HTTP Accept header). Pass the array $vars to the view. + */ + protected function showView($view, $vars=null) { + if ($vars===null) { $vars = array(); } + + $obj = new View($view); + $obj->show($vars); + } + + // Here be default handlers //////////////////////////////////////////// + + public function index($routed, $remainder) { + header('Content-type: text/plain'); + echo " == Generic Controller Index == \n\n"; + $routed_str = implode('/', $routed); + $remainder_str = implode('/', $remainder); + echo "Full path: $routed_str/$remainder_str\n"; + echo "Controller path: $routed_str\n"; + echo "Remainder path: $remainder_str\n"; + } + public function http404($routed, $remainder) { + $this->showView('http404', array('routed'=>$routed, + 'remainder'=>$remainder)); + } +} diff --git a/lpf/lib/Mime.class.php b/lpf/lib/Mime.class.php new file mode 100644 index 0000000..f37c1eb --- /dev/null +++ b/lpf/lib/Mime.class.php @@ -0,0 +1,45 @@ +<?php + +class Mime { + public static $mimes = array( + 'csv' => array( + 'text/csv', 'text/x-csv', + 'text/x-comma-separated-values', + 'text/comma-separated-values', + 'application/csv', + 'application/excel', 'application/vnd.msexcel'), + 'xhtml' => array('text/html', 'application/xhtml+xml'), + 'html' => array('text/html', 'application/xhtml+xml'), + 'bmp' => 'image/bmp', + 'gif' => 'image/gif', + 'jpeg' => array('image/jpeg', 'image/pjpeg'), + 'jpg' => array('image/jpeg', 'image/pjpeg'), + 'jpe' => array('image/jpeg', 'image/pjpeg'), + 'png' => array('image/png', 'image/x-png'), + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'css' => 'text/css', + 'htm' => 'text/html', + 'txt' => 'text/plain', + 'json' => array('application/json', 'text/json') + ); + + public static function ext2mime($ext) { + $mimes = self::$mimes; + $mime = $mimes[$ext]; + if (!is_array($mime)) $mime = array($mime); + return $mime; + } + public static function mime2ext($my_mime) { + $ret = array(); + foreach (self::mimes as $ext => $mime) { + if (is_array($mime)) { + $match = in_array($my_mime, $mime); + } else { + $match = $my_mime==$mime; + } + if ($match) $ret[] = $ext; + } + return $ret; + } +}
\ No newline at end of file diff --git a/lpf/lib/Model.class.php b/lpf/lib/Model.class.php new file mode 100644 index 0000000..0cce525 --- /dev/null +++ b/lpf/lib/Model.class.php @@ -0,0 +1,9 @@ +<?php +require_once('Database.class.php'); + +abstract class Model { + protected $db; + public function __construct() { + $this->db = Database::getInstance(); + } +} diff --git a/lpf/lib/Router.class.php b/lpf/lib/Router.class.php new file mode 100644 index 0000000..238e3f8 --- /dev/null +++ b/lpf/lib/Router.class.php @@ -0,0 +1,110 @@ +<?php + +require_once('Controller.class.php'); + +class Router { + /** + * Array mapping URIs to controllers. + * A controller may register itself either by using + * Router::register($URI, $controller[, $function]); + * or by adding itself to the $ROUTER global. + * + * The default here just gives us a 404 handler. + */ + private $routes = array('/*' => 'Http404'); + + /** + * Instantiate a router that looks for controllers in $controllerpath. + */ + public function Router($controllerpath) { + // create a $ROUTES global that can be used to set up our + // $this->routes. + global $ROUTES; + $ROUTES = $this->routes; + + // Split $controllerpath into directories, and load the + // controllers in each. + $dirs = explode(PATH_SEPARATOR, $controllerpath); + foreach ($dirs as $dir) { + // Find all files in $dir with the ext `.class.php' + $files = glob($dir.'/*.class.php'); + foreach ($files as $file) { + // and include them + require_once($file); + } + } + + $this->routes = $ROUTES; + unset($ROUTES); + } + + /** + * Route the page at the relative URL $page to the appropriate + * controller, and call the appropriate function. + */ + public function route($page) { + $parts = explode('/', $page); + $length = count($parts); // the # of segments in $controllerpart + + // if $page ends in "/", strip that off + if ($parts[$length-1]=='') { + array_pop($parts); + $length--; + } + + $controllerpart = implode('/', $parts); + + // Keep shortening $controllerpart until it matches something in + // $this->routes. The shortest it will ever become is '/*'. + // If no key exists for '/*', that's an infinite loop. + // Fortunately, the default value of $this->routes directs '/*' + // to the Http404 controller. + while(!isset($this->routes[$controllerpart])) { + $some_parts = array_slice($parts, 0, $length); + $controllerpart = implode('/', $some_parts).'/*'; + $length--; + } + $length++; + + // Figure what function to call on what controller + // Grammar Nazi Warning: `what' or `which'? + $controller = $this->routes[$controllerpart]; + if (strpos($controller, '->')===false) { + // imply function + $function = $parts[$length]; + } else { + preg_match('/(.*)->(.*)/', $controller, $matches); + $controller = $matches[1]; + $function = $matches[2]; + } + + // Default to the `index' function, provided by all controllers + if ($function=='') { + $function = 'index'; + } + + // We will pass these arrays to the function. + $routed = array_slice($parts, 0, $length); + $remainder = array_slice($parts, $length); + + // Finally, run the controller + $obj = new $controller(); + if (in_array($function, get_class_methods($obj))) { + call_user_func(array($obj, $function), + $routed, $remainder); + } else { + $obj->http404($routed, $remainder); + } + } + + /** + * This is to allow controllers to register themselves to the router. + * If $function=='', then the function will be determined by the segment + * to the right of the last segment in $path + */ + public static function register($path, $controller, $function='') { + $str = $controller.(($function=='')?'':'->'.$function); + global $ROUTES; + $ROUTES[$path] = $str; + } +}
\ No newline at end of file diff --git a/lpf/lib/Singleton.class.php b/lpf/lib/Singleton.class.php new file mode 100644 index 0000000..2f8c74f --- /dev/null +++ b/lpf/lib/Singleton.class.php @@ -0,0 +1,12 @@ +<?php + +abstract class Singleton { + private static $instances = array(); + public static function getInstance() { + $class = get_called_class(); + if (!isset(self::$instances[$class])) { + self::$instances[$class] = new $class; + } + return self::$instances[$class]; + } +} diff --git a/lpf/lib/View.class.php b/lpf/lib/View.class.php new file mode 100644 index 0000000..d7a21d3 --- /dev/null +++ b/lpf/lib/View.class.php @@ -0,0 +1,135 @@ +<?php + +require_once('Mime.class.php'); +require_once('HTTP_Accept.class.php'); + +/** + * A class to handle loading and evaluating the appropriateness of different + * views. + * + * Depends on the state of the following variables/constants: + * PAGE_EXT + * VIEWPATH + * $_SERVER['HTTP_ACCEPT'] + */ +class View { + private $extensions = array(); + private $ext = ''; + private $view = ''; + + /** + * Return the filename of the view file with extension $ext + */ + private static function filename($view, $ext) { + return VIEWPATH."/pages/$view.$ext.php"; + } + + /** + * Choose between $extensions, which all have equal quality. + */ + private function chooseBetweenEquals($extensions) { + if (count($extensions)<1) return false; + $pref = array('html'); + foreach ($pref as $ext) { + if (in_array($ext, $extensions)) return $ext; + } + return $extensions[0]; // XXX: Worst. Solution. Ever. + } + + /** + * Find the best available extension to use, based on file extension and + * HTTP 'Accept' headers. + */ + private function chooseExtension($view) { + // Make a list of candidate extensions for this view. + $files = glob(self::filename($view, '*')); + $this->extensions = array(); + $regex = '@'.preg_quote(VIEWPATH,'@').'[^.]*\.(.*)\.php$@'; + foreach ($files as $file) { + $ext = preg_replace($regex, '$1', $file); + $this->extensions[] = $ext; + } + if (count($this->extensions)<1) return false; + + if (PAGE_EXT != '') { + // First, do a check if we can just return requested + // file extension: + if (in_array(PAGE_EXT, $this->extensions)) { + return PAGE_EXT; + } + + // Check for other extensions with the same mime type as + // the requested: + $accept_str = implode(', ', Mime::ext2mime(PAGE_EXT)); + $extensions = $this->parseAccept($view, $accept_str); + if ($ext = $this->chooseBetweenEquals($extensions)) return $ext; + } + + // Check for other extensions based on HTTP 'Accept' headers: + $accept_str = $_SERVER['HTTP_ACCEPT']; + $extensions = $this->parseAccept($view, $accept_str); + if ($ext = $this->chooseBetweenEquals($extensions)) return $ext; + + // Well, all the good options failed, so let's see what we've + // got left: + $extensions = $this->extensions; + return $this->chooseBetweenEquals($extensions); + + } + + private function parseAccept($view, $accept_str) { + // $prefs is a associative array where the key is the file + // extension, and the value is how much we like that extension. + // Higher numbers are better. + $prefs = array(); + + $accept = new HTTP_Accept($accept_str); + + // Loop through the candidate views, and record how much we + // like each. + foreach ($this->extensions as $ext) { + $mimes = Mime::ext2mime($ext); + foreach ($mimes as $mime) { + $quality = $accept->getQuality($mime); + if (isset($prefs[$ext])) { + $quality = max($prefs[$ext], $quality); + } + $prefs[$ext] = $quality; + } + } + + // Create an array of all extensions tied for the top quality. + $ret = array(); + $top_quality = 0.001;// minimum acceptable according to RFC 2616 + foreach ($prefs as $ext => $quality) { + if ($quality > $top_quality) { + $top_quality = $quality; + $ret = array(); + } + if ($quality == $top_quality) { + $ret[] = $ext; + } + } + return $ret; + } + + public function __construct($view) { + $this->ext = $this->chooseExtension($view); + $this->view = $view; + } + + public function show($vars) { + $file = self::filename($this->view, $this->ext); + $mimes = Mime::ext2mime($this->ext); + + header('Content-type: '.$mimes[0]); + + require_once(VIEWPATH.'/Template.class.php'); + $vars['template'] = new Template(); + + global $VARS; + $VARS = $vars; + include($file); + unset($VARS); + } +} diff --git a/lpf/views/pages/no-conf.html.php b/lpf/views/pages/no-conf.html.php new file mode 100644 index 0000000..1f4e3d3 --- /dev/null +++ b/lpf/views/pages/no-conf.html.php @@ -0,0 +1,8 @@ +<?php global $VARS; +$t = $VARS['template']; + +$t->header('Message Manager'); +$t->paragraph('Awe shiz, dude, conf.php doesn\'t exist, you '. + 'need to go through the '. + '<a href="installer">installer</a>.'); +$t->footer(); |