commit 2f86d0e78f72745634e3d9e0dde1c74d05e07f2b Author: Samuel Ryan Date: Sat Aug 11 22:34:20 2012 +0100 initial commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..412eeda --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ebd21a --- /dev/null +++ b/.gitignore @@ -0,0 +1,163 @@ +################# +## Eclipse +################# + +*.pydevproject +.project +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.vspscc +.builds +*.dotCover + +## TODO: If you have NuGet Package Restore enabled, uncomment this +#packages/ + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp + +# ReSharper is a .NET coding add-in +_ReSharper* + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish + +# Others +[Bb]in +[Oo]bj +sql +TestResults +*.Cache +ClientBin +stylecop.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML + + + +############ +## Windows +############ + +# Windows image file caches +Thumbs.db + +# Folder config file +Desktop.ini + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +#Translations +*.mo + +#Mr Developer +.mr.developer.cfg + +# Mac crap +.DS_Store diff --git a/README.markdown b/README.markdown new file mode 100644 index 0000000..ec7f91b --- /dev/null +++ b/README.markdown @@ -0,0 +1,51 @@ +httpstatus.es is an easy to use http status code reference site. An individual +code can be accessed via httpstatus.es/{status_code}. + +Each individual code is stored in a file specific to the class it is a part of. +For example 404 is stored in 4.json, along with all other 4xx codes. + +I have yet to decide on the criteria that a status code must meet to be included. +I am unsure if including vendor specific status codes is sensible (eg: Twitter +and Facebook API have their own status codes) or if it's confusing. For now I +will accept any that have some form of notability, long term I would like to +have a toggle for whether to show "real" only, or to include "vendor specific". + +If you have any feedback please supply it via the github issue system. + +(note: I am not 100% happy with the current code store format ({class}.json) +however I've been unable to formulate a better idea that allows for the class +information (summary) to be stored with the codes. I am open to suggestions, but +it must remain a flat file solution to allow new codes and changes to the codes +via git) + +## json format + +```json +{ + "class":{ + "title":"1xx Informational" + }, + "codes":{ + "100":{ + "title":"Continue", + "summary":"Client should continue with request", + "descriptions":{ + "wiki":{ + "body":"Wikipedia description", + "link":"http:\/\/en.wikipedia.org\/wiki\/List_of_HTTP_status_codes#100" + }, + "ietf":{ + "body":"IETF Description", + "link":"http:\/\/www.ietf.org\/rfc\/rfc2616.txt" + } + } + } + } +} +``` + +## notes + +httpstatus.es is built with [klein.php](https://raw.github.com/chriso/klein.php) +and designed by myself. This is my first attempt at open sourcing anything that +I have made. \ No newline at end of file diff --git a/assets/css/style.css b/assets/css/style.css new file mode 100644 index 0000000..db36865 --- /dev/null +++ b/assets/css/style.css @@ -0,0 +1,166 @@ +*{ + padding:0; + margin:0; +} + +body{ + color:#556270; + font-family:Arial, Helvetica, sans-serif; + font-size:12px; + font-style:normal; + font-variant:normal; + font-weight:normal; + line-height:20px; + background-color:#FFF; +} + +a{ + font-weight:bold; + text-decoration:none; + color:#D6156C; +} + +a:hover{ + color:#F56991; +} + +/* + general layout +*/ + +#wrapper{ + margin:40px; + width:600px; +} + +.header{ + font-size:22px; + font-weight:bold; + margin-bottom:10px; +} + +.header .status_title{ + color:#BBB; + text-transform:uppercase; + line-height:24px; +} + +.share_buttons{ + width:auto; + float:right; +} + +.share_buttons .share_button{ + float:left; + display:block; +} + +#return{ + font-size:14px; +} + +#intro{ + font-family:Georgia; + font-size:14px; + line-height:24px; +} + +#intro p{ + margin-bottom:10px; +} + +#intro a{ + text-decoration:none; + color:#AAA; +} + +#desc{ + font-family:Georgia; + font-size:14px; + line-height:24px; +} + +#desc p{ + margin-bottom:10px; +} + +#desc .reference{ + font-size:13px; +} + +#statuses{ + margin-top:20px; +} + +.status_list{ + margin-bottom:20px; +} + +.status_list .head{ + border-bottom:2px solid #EEE; + margin-bottom:10px; + /* color:#D6156C; */ + padding-bottom:2px; +} + +.status_list .head .title{ + font-weight:bold; + font-size:14px; + float:left; + text-transform:lowercase; +} + +.status_list .head .description{ + font-style:italic; + float:right; +} + +.status_list .status{ + +} + +.status_list .status{ + width:180px; + padding-right:20px; + height:70px; + float:left; + overflow:hidden; +} + +.status_list .status .st{ + font-size:14px; + font-weight:bold; +} + +.status_list .status .description{ + font-size:11px; + height:46px; + overflow:hidden; + width:160px; +} + +#code_references{ + margin:20px 0; +} + +.info_list{ + font-size:14px; +} + +.info_list .info_item .title{ + width:200px; + float:left; +} + +.info_list .info_item .value{ + float:left; +} + +#footer{ + margin-top:10px; + font-size:11px; +} + +.clear{ + clear:both; +} \ No newline at end of file diff --git a/helper.php b/helper.php new file mode 100644 index 0000000..528287c --- /dev/null +++ b/helper.php @@ -0,0 +1,8 @@ +'; + print_r($array); + echo ''; + } \ No newline at end of file diff --git a/httpstatuses.php b/httpstatuses.php new file mode 100644 index 0000000..c9f0b53 --- /dev/null +++ b/httpstatuses.php @@ -0,0 +1,30 @@ +render('views/index.php', array("class_list" => $class_list)); + }); + + respond('/generator', function($request, $response) { + // generator here + }); + + respond('/[i:id]', function($request, $response) { + $status_code = $request->param('id'); + $code = Httpstatuses::status($status_code); + + if(!$code) + { + $response->code(404); + $response->render('views/404.php'); + } + + $response->render('views/status_code.php', $code); + }); + + respond('404', function ($request, $response) { + $response->render('views/404.php'); + }); + + dispatch(); \ No newline at end of file diff --git a/klein.php b/klein.php new file mode 100644 index 0000000..fa4b10a --- /dev/null +++ b/klein.php @@ -0,0 +1,839 @@ + (MIT License) +# http://github.com/chriso/klein.php + +$__routes = array(); +$__namespace = null; + +//Add a route callback +function respond($method, $route = '*', $callback = null) { + global $__routes, $__namespace; + $count_match = true; + if (is_callable($method)) { + $callback = $method; + $method = $route = null; + $count_match = false; + } elseif (is_callable($route)) { + $callback = $route; + $route = $method; + $method = null; + } + + if( $__namespace && $route[0] === '@' || ( $route[0] === '!' && $route[1] === '@' ) ) { + if( $route[0] === '!' ) { + $negate = true; + $route = substr( $route, 2 ); + } else { + $negate = false; + $route = substr( $route, 1 ); + } + + // regex anchored to front of string + if( $route[0] === '^' ) { + $route = substr( $route, 1 ); + } else { + $route = '.*' . $route; + } + + if( $negate ) { + $route = '@^' . $__namespace . '(?!' . $route . ')'; + } else { + $route = '@^' . $__namespace . $route; + } + } + + // empty route with namespace is a match-all + elseif( $__namespace && ( null == $route || '*' === $route ) ) { + $route = '@^' . $__namespace . '(/|$)'; + } else { + $route = $__namespace . $route; + } + + $__routes[] = array($method, $route, $callback, $count_match); + return $callback; +} + +//Each route defined inside $routes will be in the $namespace +function with($namespace, $routes) { + global $__namespace; + $previous = $__namespace; + $__namespace .= $namespace; + if (is_callable($routes)) { + $routes(); + } else { + require_once $routes; + } + $__namespace = $previous; +} + +function startSession() { + if (session_id() === '') { + session_start(); + } +} + +//Dispatch the request to the approriate route(s) +function dispatch($uri = null, $req_method = null, array $params = null, $capture = false) { + global $__routes; + + //Pass $request, $response, and a blank object for sharing scope through each callback + $request = new _Request; + $response = new _Response; + $app = new _App; + + //Get/parse the request URI and method + if (null === $uri) { + $uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/'; + } + if (false !== strpos($uri, '?')) { + $uri = strstr($uri, '?', true); + } + if (null === $req_method) { + $req_method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET'; + + //For legacy servers, override the HTTP method with the X-HTTP-Method-Override + //header or _method parameter + if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) { + $req_method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']; + } else if (isset($_REQUEST['_method'])) { + $req_method = $_REQUEST['_method']; + } + } + + //Force request_order to be GP + //http://www.mail-archive.com/internals@lists.php.net/msg33119.html + $_REQUEST = array_merge($_GET, $_POST); + if (null !== $params) { + $_REQUEST = array_merge($_REQUEST, $params); + } + + $matched = 0; + $apc = function_exists('apc_fetch'); + + ob_start(); + + foreach ($__routes as $handler) { + list($method, $_route, $callback, $count_match) = $handler; + + //Was a method specified? If so, check it against the current request method + if (is_array($method)) { + $method_match = false; + foreach ($method as $test) { + if (strcasecmp($req_method, $test) === 0) { + $method_match = true; + continue; + } + } + if (false === $method_match) { + continue; + } + } elseif (null !== $method && strcasecmp($req_method, $method) !== 0) { + continue; + } + + //! is used to negate a match + if (isset($_route[0]) && $_route[0] === '!') { + $negate = true; + $i = 1; + } else { + $negate = false; + $i = 0; + } + + //Check for a wildcard (match all) + if ($_route === '*' || null == $_route) { + $match = true; + + //Easily handle 404's + } elseif ($_route === '404' && !$matched) { + $callback($request, $response, $app, $matched); + ++$matched; + + //@ is used to specify custom regex + } elseif (isset($_route[$i]) && $_route[$i] === '@') { + $match = preg_match('`' . substr($_route, $i + 1) . '`', $uri, $params); + + //Compiling and matching regular expressions is relatively + //expensive, so try and match by a substring first + } else { + $route = null; + $regex = false; + $j = 0; + $n = isset($_route[$i]) ? $_route[$i] : null; + + //Find the longest non-regex substring and match it against the URI + while (true) { + if (!isset($_route[$i])) { + break; + } elseif (false === $regex) { + $c = $n; + $regex = $c === '[' || $c === '(' || $c === '.'; + if (false === $regex && false !== isset($_route[$i+1])) { + $n = $_route[$i + 1]; + $regex = $n === '?' || $n === '+' || $n === '*' || $n === '{'; + } + if (false === $regex && $c !== '/' && (!isset($uri[$j]) || $c !== $uri[$j])) { + continue 2; + } + $j++; + } + $route .= $_route[$i++]; + } + + //Check if there's a cached regex string + if (false !== $apc) { + $regex = apc_fetch("route:$route"); + if (false === $regex) { + $regex = compile_route($route); + apc_store("route:$route", $regex); + } + } else { + $regex = compile_route($route); + } + + $match = preg_match($regex, $uri, $params); + } + + if (isset($match) && $match ^ $negate) { + if (null !== $params) { + $_REQUEST = array_merge($_REQUEST, $params); + } + try { + $callback($request, $response, $app, $matched); + } catch (Exception $e) { + $response->error($e); + } + if ($_route !== '*' && $_route !== null) { + $count_match && ++$matched; + } + } + } + if (!$matched) { + $response->code(404); + } + if ($capture) { + return ob_get_clean(); + } elseif ($response->chunked) { + $response->chunk(); + } else { + ob_end_flush(); + } +} + +//Compiles a route string to a regular expression +function compile_route($route) { + if (preg_match_all('`(/|\.|)\[([^:\]]*+)(?::([^:\]]*+))?\](\?|)`', $route, $matches, PREG_SET_ORDER)) { + $match_types = array( + 'i' => '[0-9]++', + 'a' => '[0-9A-Za-z]++', + 'h' => '[0-9A-Fa-f]++', + '*' => '.+?', + '**' => '.++', + '' => '[^/]++' + ); + foreach ($matches as $match) { + list($block, $pre, $type, $param, $optional) = $match; + + if (isset($match_types[$type])) { + $type = $match_types[$type]; + } + if ($pre === '.') { + $pre = '\.'; + } + //Older versions of PCRE require the 'P' in (?P) + $pattern = '(?:' + . ($pre !== '' ? $pre : null) + . '(' + . ($param !== '' ? "?P<$param>" : null) + . $type + . '))' + . ($optional !== '' ? '?' : null); + + $route = str_replace($block, $pattern, $route); + } + } + return "`^$route$`"; +} + +class _Request { + + protected $_id = null; + + //HTTP headers helper + static $_headers = null; + + //Returns all parameters (GET, POST, named) that match the mask + public function params($mask = null) { + $params = $_REQUEST; + if (null !== $mask) { + if (!is_array($mask)) { + $mask = func_get_args(); + } + $params = array_intersect_key($params, array_flip($mask)); + //Make sure each key in $mask has at least a null value + foreach ($mask as $key) { + if (!isset($params[$key])) { + $params[$key] = null; + } + } + } + return $params; + } + + //Return a request parameter, or $default if it doesn't exist + public function param($key, $default = null) { + return isset($_REQUEST[$key]) && $_REQUEST[$key] !== '' ? $_REQUEST[$key] : $default; + } + + public function __isset($param) { + return isset($_REQUEST[$param]); + } + + public function __get($param) { + return isset($_REQUEST[$param]) ? $_REQUEST[$param] : null; + } + + public function __set($param, $value) { + $_REQUEST[$param] = $value; + } + + public function __unset($param) { + unset($_REQUEST[$param]); + } + + //Is the request secure? If $required then redirect to the secure version of the URL + public function isSecure($required = false) { + $secure = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']; + if (!$secure && $required) { + $url = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + self::$_headers->header('Location: ' . $url); + } + return $secure; + } + + //Gets a request header + public function header($key, $default = null) { + $key = 'HTTP_' . strtoupper(str_replace('-','_', $key)); + return isset($_SERVER[$key]) ? $_SERVER[$key] : $default; + } + + //Gets a request cookie + public function cookie($key, $default = null) { + return isset($_COOKIE[$key]) ? $_COOKIE[$key] : $default; + } + + //Gets the request method, or checks it against $is - e.g. method('post') => true + public function method($is = null) { + $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET'; + if (null !== $is) { + return strcasecmp($method, $is) === 0; + } + return $method; + } + + //Start a validator chain for the specified parameter + public function validate($param, $err = null) { + return new _Validator($this->param($param), $err); + } + + //Gets a unique ID for the request + public function id() { + if (null === $this->_id) { + $this->_id = sha1(mt_rand() . microtime(true) . mt_rand()); + } + return $this->_id; + } + + //Gets a session variable associated with the request + public function session($key, $default = null) { + startSession(); + return isset($_SESSION[$key]) ? $_SESSION[$key] : $default; + } + + //Gets the request IP address + public function ip() { + return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null; + } + + //Gets the request user agent + public function userAgent() { + return isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null; + } + + //Gets the request URI + public function uri() { + return isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/'; + } +} + +class _Response extends StdClass { + + public $chunked = false; + protected $_errorCallbacks = array(); + protected $_layout = null; + protected $_view = null; + protected $_code = 200; + + static $_headers = null; + + //Enable response chunking. See: http://bit.ly/hg3gHb + public function chunk($str = null) { + if (false === $this->chunked) { + $this->chunked = true; + self::$_headers->header('Transfer-encoding: chunked'); + flush(); + } + if (null !== $str) { + printf("%x\r\n", strlen($str)); + echo "$str\r\n"; + flush(); + } elseif (($ob_length = ob_get_length()) > 0) { + printf("%x\r\n", $ob_length); + ob_flush(); + echo "\r\n"; + flush(); + } + } + + //Sets a response header + public function header($key, $value = null) { + self::$_headers->header($key, $value); + } + + //Sets a response cookie + public function cookie($key, $value = '', $expiry = null, $path = '/', + $domain = null, $secure = false, $httponly = false) { + if (null === $expiry) { + $expiry = time() + (3600 * 24 * 30); + } + return setcookie($key, $value, $expiry, $path, $domain, $secure, $httponly); + } + + //Stores a flash message of $type + public function flash($msg, $type = 'info', $params = null) { + startSession(); + if (is_array($type)) { + $params = $type; + $type = 'info'; + } + if (!isset($_SESSION['__flashes'])) { + $_SESSION['__flashes'] = array($type => array()); + } elseif (!isset($_SESSION['__flashes'][$type])) { + $_SESSION['__flashes'][$type] = array(); + } + $_SESSION['__flashes'][$type][] = $this->markdown($msg, $params); + } + + //Support basic markdown syntax + public function markdown($str, $args = null) { + $args = func_get_args(); + $md = array( + '/\[([^\]]++)\]\(([^\)]++)\)/' => '$1', + '/\*\*([^\*]++)\*\*/' => '$1', + '/\*([^\*]++)\*/' => '$1' + ); + $str = array_shift($args); + if (is_array($args[0])) { + $args = $args[0]; + } + foreach ($args as &$arg) { + $arg = htmlentities($arg, ENT_QUOTES); + } + return vsprintf(preg_replace(array_keys($md), $md, $str), $args); + } + + //Tell the browser not to cache the response + public function noCache() { + $this->header("Pragma: no-cache"); + $this->header('Cache-Control: no-store, no-cache'); + } + + //Sends a file + public function file($path, $filename = null, $mimetype = null) { + $this->discard(); + $this->noCache(); + set_time_limit(1200); + if (null === $filename) { + $filename = basename($path); + } + if (null === $mimetype) { + $mimetype = finfo_file(finfo_open(FILEINFO_MIME_TYPE), $path); + } + $this->header('Content-type: ' . $mimetype); + $this->header('Content-length: ' . filesize($path)); + $this->header('Content-Disposition: attachment; filename="'.$filename.'"'); + readfile($path); + } + + //Sends an object as json or jsonp by providing the padding prefix + public function json($object, $jsonp_prefix = null) { + $this->discard(); + $this->noCache(); + set_time_limit(1200); + $json = json_encode($object); + if (null !== $jsonp_prefix) { + header('Content-Type: text/javascript'); // should ideally be application/json-p once adopted + echo "$jsonp_prefix($json);"; + } else { + header('Content-Type: application/json'); + echo $json; + } + } + + //Sends a HTTP response code + public function code($code = null) { + if(null !== $code) { + $this->_code = $code; + $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0'; + $this->header("$protocol $code"); + } + return $this->_code; + } + + //Redirects the request to another URL + public function redirect($url, $code = 302) { + $this->code($code); + $this->header("Location: $url"); + exit; + } + + //Redirects the request to the current URL + public function refresh() { + $this->redirect(isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/'); + } + + //Redirects the request back to the referrer + public function back() { + if (isset($_SERVER['HTTP_REFERER'])) { + $this->redirect($_SERVER['HTTP_REFERER']); + } + $this->refresh(); + } + + //Sets response properties/helpers + public function set($key, $value = null) { + if (!is_array($key)) { + return $this->$key = $value; + } + foreach ($key as $k => $value) { + $this->$k = $value; + } + } + + //Adds to or modifies the current query string + public function query($key, $value = null) { + $query = array(); + if (isset($_SERVER['QUERY_STRING'])) { + parse_str($_SERVER['QUERY_STRING'], $query); + } + if (is_array($key)) { + $query = array_merge($query, $key); + } else { + $query[$key] = $value; + } + + $request_uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/'; + if (strpos($request_uri, '?') !== false) { + $request_uri = strstr($request_uri, '?', true); + } + return $request_uri . (!empty($query) ? '?' . http_build_query($query) : null); + } + + //Set the view layout + public function layout($layout) { + $this->_layout = $layout; + } + + //Renders the current view + public function yield() { + require $this->_view; + } + + //Renders a view + optional layout + public function render($view, array $data = array()) { + $original_view = $this->_view; + + if (!empty($data)) { + $this->set($data); + } + $this->_view = $view; + if (null === $this->_layout) { + $this->yield(); + } else { + require $this->_layout; + } + if (false !== $this->chunked) { + $this->chunk(); + } + + // restore state for parent render() + $this->_view = $original_view; + } + + // Renders a view without a layout + public function partial($view, array $data = array()) { + $layout = $this->_layout; + $this->_layout = null; + $this->render($view, $data); + $this->_layout = $layout; + } + + //Sets a session variable + public function session($key, $value = null) { + startSession(); + return $_SESSION[$key] = $value; + } + + //Adds an error callback to the stack of error handlers + public function onError($callback) { + $this->_errorCallbacks[] = $callback; + } + + //Routes an exception through the error callbacks + public function error(Exception $err) { + $type = get_class($err); + $msg = $err->getMessage(); + + if (count($this->_errorCallbacks) > 0) { + foreach (array_reverse($this->_errorCallbacks) as $callback) { + if (is_callable($callback)) { + if ($callback($this, $msg, $type)) { + return; + } + } else { + $this->flash($err); + $this->redirect($callback); + } + } + } else { + $this->code(500); + throw new ErrorException($err); + } + } + + //Returns an escaped request paramater + public function param($param, $default = null) { + return isset($_REQUEST[$param]) ? htmlentities($_REQUEST[$param], ENT_QUOTES) : $default; + } + + //Returns and clears all flashes of optional $type + public function flashes($type = null) { + startSession(); + if (!isset($_SESSION['__flashes'])) { + return array(); + } + if (null === $type) { + $flashes = $_SESSION['__flashes']; + unset($_SESSION['__flashes']); + } elseif (null !== $type) { + $flashes = array(); + if (isset($_SESSION['__flashes'][$type])) { + $flashes = $_SESSION['__flashes'][$type]; + unset($_SESSION['__flashes'][$type]); + } + } + return $flashes; + } + + //Escapes a string + public function escape($str) { + return htmlentities($str, ENT_QUOTES); + } + + //Discards the current output buffer + public function discard() { + return ob_end_clean(); + } + + //Flushes the current output buffer + public function flush() { + ob_end_flush(); + } + + //Return the current output buffer as a string + public function buffer() { + return ob_get_contents(); + } + + //Dump a variable + public function dump($obj) { + if (is_array($obj) || is_object($obj)) { + $obj = print_r($obj, true); + } + echo '
' .  htmlentities($obj, ENT_QUOTES) . "

\n"; + } + + //Allow callbacks to be assigned as properties and called like normal methods + public function __call($method, $args) { + if (!isset($this->$method) || !is_callable($this->$method)) { + throw new ErrorException("Unknown method $method()"); + } + $callback = $this->$method; + switch (count($args)) { + case 1: return $callback($args[0]); + case 2: return $callback($args[0], $args[1]); + case 3: return $callback($args[0], $args[1], $args[2]); + case 4: return $callback($args[0], $args[1], $args[2], $args[3]); + default: return call_user_func_array($callback, $args); + } + } +} + +function addValidator($method, $callback) { + _Validator::$_methods[strtolower($method)] = $callback; +} + +class ValidatorException extends Exception {} + +class _Validator { + + public static $_methods = array(); + + protected $_str = null; + protected $_err = null; + + //Sets up the validator chain with the string and optional error message + public function __construct($str, $err = null) { + $this->_str = $str; + $this->_err = $err; + if (empty(static::$_defaultAdded)) { + static::addDefault(); + } + } + + //Adds default validators on first use. See README for usage details + public static function addDefault() { + static::$_methods['null'] = function($str) { + return $str === null || $str === ''; + }; + static::$_methods['len'] = function($str, $min, $max = null) { + $len = strlen($str); + return null === $max ? $len === $min : $len >= $min && $len <= $max; + }; + static::$_methods['int'] = function($str) { + return (string)$str === ((string)(int)$str); + }; + static::$_methods['float'] = function($str) { + return (string)$str === ((string)(float)$str); + }; + static::$_methods['email'] = function($str) { + return filter_var($str, FILTER_VALIDATE_EMAIL) !== false; + }; + static::$_methods['url'] = function($str) { + return filter_var($str, FILTER_VALIDATE_URL) !== false; + }; + static::$_methods['ip'] = function($str) { + return filter_var($str, FILTER_VALIDATE_IP) !== false; + }; + static::$_methods['alnum'] = function($str) { + return ctype_alnum($str); + }; + static::$_methods['alpha'] = function($str) { + return ctype_alpha($str); + }; + static::$_methods['contains'] = function($str, $needle) { + return strpos($str, $needle) !== false; + }; + static::$_methods['regex'] = function($str, $pattern) { + return preg_match($pattern, $str); + }; + static::$_methods['chars'] = function($str, $chars) { + return preg_match("`^[$chars]++$`i", $str); + }; + } + + public function __call($method, $args) { + $reverse = false; + $validator = $method; + $method_substr = substr($method, 0, 2); + + if ($method_substr === 'is') { //is<$validator>() + $validator = substr($method, 2); + } elseif ($method_substr === 'no') { //not<$validator>() + $validator = substr($method, 3); + $reverse = true; + } + $validator = strtolower($validator); + + if (!$validator || !isset(static::$_methods[$validator])) { + throw new ErrorException("Unknown method $method()"); + } + $validator = static::$_methods[$validator]; + array_unshift($args, $this->_str); + + switch (count($args)) { + case 1: $result = $validator($args[0]); break; + case 2: $result = $validator($args[0], $args[1]); break; + case 3: $result = $validator($args[0], $args[1], $args[2]); break; + case 4: $result = $validator($args[0], $args[1], $args[2], $args[3]); break; + default: $result = call_user_func_array($validator, $args); break; + } + + $result = (bool)($result ^ $reverse); + if (false === $this->_err) { + return $result; + } elseif (false === $result) { + throw new ValidatorException($this->_err); + } + return $this; + } +} + +class _App { + + protected $services = array(); + + //Check for a lazy service + public function __get($name) { + if (!isset($this->services[$name])) { + throw new InvalidArgumentException("Unknown service $name"); + } + $service = $this->services[$name]; + return $service(); + } + + //Call a class property like a method + public function __call($method, $args) { + if (!isset($this->$method) || !is_callable($this->$method)) { + throw new ErrorException("Unknown method $method()"); + } + return call_user_func_array($this->$method, $args); + } + + //Register a lazy service + public function register($name, $closure) { + if (isset($this->services[$name])) { + throw new Exception("A service is already registered under $name"); + } + $this->services[$name] = function() use ($closure) { + static $instance; + if (null === $instance) { + $instance = $closure(); + } + return $instance; + }; + } +} + +class _Headers { + public function header($key, $value = null) { + header($this->_header($key, $value)); + } + + /** + * Output an HTTP header. If $value is null, $key is + * assume to be the HTTP response code, and the ":" + * separator will be omitted. + */ + public function _header($key, $value = null) { + if (null === $value ) { + return $key; + } + + $key = str_replace(' ', '-', ucwords(str_replace('-', ' ', $key))); + return "$key: $value"; + } +} + +_Request::$_headers = _Response::$_headers = new _Headers; + diff --git a/views/404.php b/views/404.php new file mode 100644 index 0000000..b3cfe63 --- /dev/null +++ b/views/404.php @@ -0,0 +1,45 @@ + + + + + (Actual) 404 — httpstatus.es + + + + + +
+
+ 404 / Page not found +
+
+

+ No really, this is a 404. The page can't be found! There's + no education here, just a barren wasteland. +

+

+ If there should + be something here you can submit a new status code via the + + httpstatus.es repository + on github. +

+
+ +
+ + \ No newline at end of file diff --git a/views/index.php b/views/index.php new file mode 100644 index 0000000..5553303 --- /dev/null +++ b/views/index.php @@ -0,0 +1,71 @@ + + + + + HTTP Status Codes — httpstatus.es + + + + + +
+
+ httpstatus.es + +
+
+

+ Database of HTTP status codes with their IETF and Wikipedia descriptions. + Maintained by @citricsquid. + Submit changes/corrections/comments via twitter or + email. +

+
+
+ class_list as $id => $class) { ?> +
+
+
+
+
+
+ $info) { ?> +
+ +
+
+ +
+
+
+ +
+
+ + \ No newline at end of file diff --git a/views/status_code.php b/views/status_code.php new file mode 100644 index 0000000..73ab093 --- /dev/null +++ b/views/status_code.php @@ -0,0 +1,60 @@ + + + + + <?php echo $this->code; ?> — httpstatus.es + + + + + +
+
+ code; ?> / title; ?> +
+
+ descriptions as $type => $description) { ?> +

+ ““ — "> +

+ +
+ references) { ?> +
+
+ code; ?> / code references +
+
+ references as $reference) { ?> +
+
+ +
+
+ “” +
+
+
+ +
+
+
+ + +
+ + \ No newline at end of file