--- /dev/null
+# class http\Env extends http\Object
+
+The http\Env class provides static methods to manipulate and inspect the server's current request's HTTP environment.
+
+## Request startup
+
+The http\Env module overrides PHP's builtin POST data parser to be run also if
+the request method is not POST. Additionally it will handle
+```application/json``` payloads if ```ext/json``` is available. Successfully
+parsed JSON will be put right into the $_POST array.
+
--- /dev/null
+# class http\Env\Request extends http\Message
+
+## Constants:
+
+> None.
+
+## Properties:
+
+* ```protected $query = NULL```
+ The request's query parameters. ($_GET)
+* ```protected $form = NULL```
+ The request's form parameters. ($_POST)
+* ```protected $files = NULL```
+ The request's form uploads. ($_FILES)
--- /dev/null
+# static http\Message\Body http\Env::getRequestBody([string $body_class_name])
+
+Retreive the current HTTP request's body.
+
+## Parameters:
+
+* Optional ```string $body_class_name```
+ A user class extending http\Body.
+
+## Returns:
+
+* ```http\Message\Body``` instance representing the request body
+
+## Throws:
+
+* http\Exception
--- /dev/null
+# static mixed http\Env::getRequestHeader([string $header_name])
+
+Retrieve one or all headers of the current HTTP request.
+
+## Parameters:
+
+* Optional ```string $header_name```
+ The key of a header to retrieve.
+
+## Returns:
+
+* ```NULL```, if $header_name was not found
+* ```string```, the compound header when $header_name was found
+* ```array``` of all headers if $header_name was not specified
--- /dev/null
+# static int http\Env::getResponseCode()
+
+Get the HTTP response code to send.
+
+## Parameters:
+
+> None.
+
+## Returns:
+
+* ```int```, the HTTP response code.
+
--- /dev/null
+# static mixed http\Env::getResponseHeader([string $header_name])
+
+Get one or all HTTP response headers to be sent.
+
+## Parameters:
+
+* Optional ```string $header_name```
+ The name of the response header to retrieve.
+
+## Returns:
+
+* ```string```, the compound value of the response header to send
+* ```NULL```, if the header was not found
+* ```array```, of all response headers, if $header_name was not specified
--- /dev/null
+# static array http\Env::getResponseStatusForAllCodes()
+
+Retrieve a list of all known HTTP response status.
+
+## Parameters:
+
+> None.
+
+## Returns:
+
+* ```array``` mapping of the form \[
+ ...
+ ```int $code``` => ```string $status```
+ ...
+ \]
--- /dev/null
+# static string http\Env::getResponseStatusForCode(int $code)
+
+Retrieve the string representation of specified HTTP response code.
+
+## Parameters:
+
+* ```int $code```
+ The HTTP response code to get the string representation for.
+
+## Returns:
+
+* ```string```, the HTTP response status message
+* empty ```string```, if no message for this code was found
+
--- /dev/null
+# static string http\Env::negotiate(string $params, array $supported[, string $prim_typ_sep[, array &$result]])
+
+Generic negotiator. For specific client negotiation see http\Env::negotiateContentType() and related methods.
+
+> ***NOTE:*** The first elemement of $supported serves as a default if no operand matches.
+
+## Params:
+
+* ```string $params```
+ HTTP header parameter's value to negotiate.
+* ```array $supported```
+ List of supported negotiation operands.
+* Optional ```string $prim_typ_sep```
+ A "primary type separator", i.e. that would be a hyphen for content language negotiation (en-US, de-DE, etc.).
+* Optional reference ```array &$result```
+ Out parameter recording negotiation results.
+
+## Returns:
+
+* ```NULL```, if negotiation fails.
+* ```string```, the closest match negotiated, or the default (first entry of $supported).
--- /dev/null
+# static string http\Env::negotiateCharset(array $supported[, array &$result)
+
+Negotiate the client's preferred character set.
+
+> ***NOTE:*** The first elemement of $supported character sets serves as a default if no character set matches.
+
+## Params:
+
+* ```array $supported```
+ List of supported content character sets.
+* Optional reference ```array &$result```
+ Out parameter recording negotiation results.
+
+## Returns:
+
+* ```NULL```, if negotiation fails.
+* ```string```, the negotiated character set.
--- /dev/null
+# static string http\Env::negotiateContentType(array $supported[, array &$result)
+
+Negotiate the client's preferred MIME content type.
+
+> ***NOTE:*** The first elemement of $supported content types serves as a default if no content-type matches.
+
+## Params:
+
+* ```array $supported```
+ List of supported MIME content types.
+* Optional reference ```array &$result```
+ Out parameter recording negotiation results.
+
+## Returns:
+
+* ```NULL```, if negotiation fails.
+* ```string```, the negotiated content type.
+
+## Example:
+
+A client indicates his accepted MIME content types by sending an ```Accept```
+header. The static ```http\Env``` class provides a facility to negotiate the
+client's preferred content type:
+
+ <?php
+ $_SERVER["HTTP_ACCEPT"] = implode(",", array(
+ "text/html",
+ "text/plain",
+ "text/*;q=0.9",
+ "*/*;q=0.1",
+ "application/xml;q=0"
+ ));
+ $supported = array(
+ "text/html",
+ "text/plain",
+ "application/json",
+ "application/xml"
+ );
+ $preferred = http\Env::negotiateContentType($supported, $ranking);
+ var_dump($preferred, $ranking);
+ ?>
+
+Running this script should give the following output:
+
+ string(9) "text/html"
+ array(3) {
+ 'text/html' =>
+ float(0.99)
+ 'text/plain' =>
+ float(0.98)
+ 'application/json' =>
+ float(0.1)
+ }
+
+
+
--- /dev/null
+# static string http\Env::negotiateEncoding(array $supported[, array &$result)
+
+Negotiate the client's preferred encoding.
+
+> ***NOTE:*** The first elemement of $supported encodings serves as a default if no encoding matches.
+
+## Params:
+
+* ```array $supported```
+ List of supported content encodings.
+* Optional reference ```array &$result```
+ Out parameter recording negotiation results.
+
+## Returns:
+
+* ```NULL```, if negotiation fails.
+* ```string```, the negotiated encoding.
--- /dev/null
+# static string http\Env::negotiateLanguage(array $supported[, array &$result)
+
+Negotiate the client's preferred language.
+
+> ***NOTE:*** The first elemement of $supported languages serves as a default if no language matches.
+
+## Params:
+
+* ```array $supported```
+ List of supported content languages.
+* Optional reference ```array &$result```
+ Out parameter recording negotiation results.
+
+## Returns:
+
+* ```NULL```, if negotiation fails.
+* ```string```, the negotiated language.
--- /dev/null
+# static bool http\Env::setResponseCode(int $code)
+
+Set the HTTP response code to send.
+
+## Params:
+
+* ```int $code```
+ The HTTP response status code.
+
+## Returns:
+
+* ```bool``` Success.
--- /dev/null
+# static bool http\Env::setResponseHeader(string $header_name, [mixed $header_value = NULL[, int $response_code = 0[, bool $replace = true]]]
+
+Set a response header, either replacing a prior set header, or appending the new header value, depending on $replace.
+
+If no $header_value is specified, or $header_value is ```NULL```, then a previously set header with the same key will be deleted from the list.
+
+If $response_code is not ```0```, the response status code is updated accordingly.
+
+## Parameters:
+
+* ```string $header_name```, the name of the response header.
+* Optional ```mixed $header_value```, the header value.
+* Optional ```int $response_code```, any HTTP response status code to set.
+* Optional ```bool $replace```, whether to replace a previously set response header with the same name.
+
+## Returns:
+
+* ```bool``` Success.
--- /dev/null
+# class http\Header extends http\Object implements Serializable
+
+The http\Header class provides methods to manipulate, match, negotiate and serialize HTTP headers.
+
+## Constants:
+
+* ```MATCH_LOOSE```
+ None of the following match constraints applies.
+* ```MATCH_CASE```
+ Perform case sensitive matching.
+* ```MATCH_WORD```
+ Match only on word boundaries (according by CTYPE alpha-numeric).
+* ```MATCH_FULL```
+ Match the complete string.
+* ```MATCH_STRICT```
+ Case sensitively match the full string (same as ```MATCH_CASE|MATCH_FULL```).
+
+## Properties:
+
+* ```public $name = NULL```
+ The name of the HTTP header.
+* ```public $value = NULL```
+ The value of the HTTP header.
--- /dev/null
+# void http\Header::__construct([string $name[, mixed $value]])
+
+Create an http\Header instance for use of simple matching or negotiation. If the value of the header is an array it may be compounded to a single comma separated string.
+
+## Params:
+
+* Optional ```string $name```
+ The HTTP header name.
+* Optional ```mixed $value```
+ The value of the header.
+
+# Throws:
+
+* http\Exception
--- /dev/null
+# string http\Header::__toString()
+
+String cast handler. Alias of http\Header::serialize().
+
+## Params:
+
+> None.
+
+## Returns:
+
+* ```string```, the serialized form of the HTTP header (i.e. "Name: value").
--- /dev/null
+# http\Params http\Header::getParams([mixed $ps = ","[, mixed $as = ";"[, mixed $vs = "="[, int $flags = http\Params::PARSE_DEFAULT]]]])
+
+Create a parameter list out of the HTTP header value.
+
+## Params:
+
+* Optional ```mixed $ps```
+ The parameter separator(s).
+* Optional ```mixed $as```
+ The argument separator(s).
+* Optional ```mixed```
+ The value separator(s).
+* Optional ```int $flags```
+ The modus operandi. See http\Params constants.
+
+## Returns:
+
+* ```http\Params``` instance
--- /dev/null
+# bool http\Header::match(string $value[, int $flags = http\Header::MATCH_LOOSE])
+
+Match the HTTP header's value against provided $value according to $flags.
+
+## Params:
+
+* ```string $value```
+ The comparison value.
+* Optional ```int $flags```
+ The modus operandi. See http\Header constants.
+
+## Returns:
+
+* ```bool```, whether $value matches the header value according to $flags.
--- /dev/null
+# string http\Header::negotiate(array $supported[, array &$result])
+
+Negotiate the header's value against a list of supported values in $supported.
+Negotiation operation is adopted according to the header name, i.e. if the
+header being negotiated is ```Accept```, then a slash is used as primary type
+separator, and if the header is ```Accept-Language``` respectively, a hyphen is
+used instead.
+
+> ***NOTE:*** The first elemement of $supported serves as a default if no operand matches.
+
+## Params:
+
+* ```array $supported```
+ The list of supported values to negotiate.
+* Optional reference ```array &$result```
+ Out parameter recording the negotiation results.
+
+## Returns:
+
+* ```NULL```, if negotiation fails.
+* ```string```, the closest match negotiated, or the default (first entry of $supported).
--- /dev/null
+# static array http\Header::parse(string $header[, string $header_class = null])
+
+Parse HTTP headers.
+
+## Params:
+
+* ```string $header```
+ The complete string of headers.
+* Optional ```string $header_class```
+ A class extending http\Header.
+
+## Returns:
+
+* ```array``` of parsed headers, where the elements are instances of $header_class if specified.
--- /dev/null
+# string http\Header::serialize()
+
+Implements Serializable.
+
+## Params:
+
+> None.
+
+## Returns:
+
+* ```string```, serialized representation of HTTP header (i.e. "Name: value")
--- /dev/null
+# string http\Header::toString()
+
+Convenience method. Alias of http\Header::serialize().
+
+## Params:
+
+> None.
+
+## Returns:
+
+* ```string```, the serialized form of the HTTP header (i.e. "Name: value").
--- /dev/null
+# void http\Header::unserialize(string $serialized)
+
+Implements Serializable.
+
+## Params:
+
+* ```string $serialized```
+ The serialized HTTP header (i.e. "Name: value")
+
--- /dev/null
+# class http\Params extends http\Object implements ArrayAccess
+
+Parse, interpret and compose HTTP (header) parameters.
+
+## Constants:
+
+* ```DEF_PARAM_SEP```
+ The default parameter separator (",").
+* ```DEF_ARG_SEP```
+ The default argument separator (";").
+* ```DEF_VAL_SEP```
+ The default value separator ("=").
+* ```COOKIE_PARAM_SEP```
+ TBD
+* ```PARSE_RAW```
+ Do not interpret the parsed parameters.
+* ```PARSE_DEFAULT```
+ Interpret input as default formatted parameters.
+* ```PARSE_URLENCODED```
+ Urldecode single units of parameters, arguments and values.
+* ```PARSE_DIMENSION```
+ Parse sub dimensions indicated by square brackets.
+* ```PARSE_QUERY```
+ Parse URL query string (same as http\Params::PARSE_URLENCODED|http\Params::PARSE_DIMENSION).
+
+## Properties:
+
+* ```public $params = NULL```
+ The (parsed) parameters.
+* ```public $param_sep = http\Header::DEF_PARAM_SEP```
+ The parameter separator(s).
+* ```public $arg_sep = http\Header::DEF_ARG_SEP```
+ The argument separator(s).
+* ```public $val_sep = http\Header::DEF_VAL_SEP```
+ The value separator(s).
+* ```public $flags = http\Params::PARSE_DEFAULT```
+ The modus operandi of the parser. See http\Params::PARSE_* constants.
--- /dev/null
+# void http\Params::__construct([mixed $params = NULL[, mixed $ps = http\Params::DEF_PARAM_SEP[, mixed $as = http\Params::DEF_ARG_SEP[, mixed $vs = http\Params::DEF_VAL_SEP[, int $flags = http\Params::PARSE_DEFAULT]]]]])
+
+Instantiate a new HTTP (header) parameter set.
+
+## Params:
+
+* Optional ```mixed $params```
+ Pre-parsed parameters or a string to be parsed.
+* Optional ```mixed $ps```
+ The parameter separator(s).
+* Optional ```mixed $as```
+ The argument separator(s).
+* Optional ```mixed $vs```
+ The value separator(s).
+* Optional ```int $flags```
+ The modus operandi. See http\Params::PARSE_* constants.
+
+## Throws:
+
+* http\Exception
+
--- /dev/null
+# string http\Params::__toString()
+
+String cast handler. Alias of http\Params::toString().
+Returns a stringified version of the parameters.
+
+## Params:
+
+> None.
+
+## Returns:
+
+* ```string``` version of the parameters.
--- /dev/null
+# bool http\Params::offsetExists(string $name)
+
+Implements ArrayAccess.
+
+## Params:
+
+* ```string $name```
+ The offset to look after.
+
+## Returns:
+
+* ```bool``` Existence.
--- /dev/null
+# mixed http\Params::offsetGet(string $name)
+
+Implements ArrayAccess.
+
+## Params:
+
+* ```string $name```
+ The offset to retrieve.
+
+## Returns:
+
+* ```mixed```, contents at offset.
--- /dev/null
+# void http\Params::offsetSet(string $name, mixed $value)
+
+Implements ArrayAccess.
+
+## Params:
+
+* ```string $name```
+ The offset to modify.
+* ```mixed $value```
+ The value to set.
+
--- /dev/null
+# void http\Params::offsetUnset(string $name)
+
+Implements ArrayAccess.
+
+## Params:
+
+* ```string $name```
+ The offset to delete.
+
--- /dev/null
+# array http\Params::toArray()
+
+Convenience method that simply returns http\Params::$params.
+
+## Params:
+
+> None.
+
+## Returns:
+
+* ```array``` of paramters.
--- /dev/null
+# string http\Params::toString()
+
+Returns a stringified version of the parameters.
+
+## Params:
+
+> None.
+
+## Returns:
+
+* ```string``` version of the parameters.
--- /dev/null
+* {
+ font-size: 99.9%;
+}
+
+body {
+ font-family: monospace;
+ font-size: 1.5em;
+ margin: 0;
+ padding: 4em 0 0 0;
+}
+
+body>* {
+ margin-left: 2em;
+ margin-right: 2em;
+}
+
+code {
+ display: inline-block;
+ border-radius: 2px;
+ padding: 0px 2px 2px 2px;
+ background: #e0e0e0;
+ color: #606060;
+ box-shadow: 0 0 1px #999;
+}
+
+h1 {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ margin: 0;
+ padding: 1em;
+}
+p {
+ color: #3f3f3f;
+ margin: 1em;
+}
+
+blockquote {
+ padding: 1em;
+ border-radius: 4px;
+ max-width: 1000px;
+}
+blockquote, blockquote p {
+ color: #f0f0f0;
+}
+ul {
+ margin-bottom: 2em;
+}
+li {
+ margin-bottom: .5em;
+}
+a {
+ color: #2f4f4f;
+}
+
+.var {
+ color: #800000;
+}
+
+.invert, h1, blockquote, blockquote p {
+ background: #030303;
+ color: #f0f0f0;
+}
+
+.invert .var, h1 .var, blockquote .var {
+ color: #f4a460;
+}
+
+.invert a, h1 a, blockquote a {
+ color: #b0e0e6;
+}
+
--- /dev/null
+function type(s) {
+
+ // nothing
+ if (!s.match(/[a-zA-Z]/)) {
+ return;
+ }
+
+ switch (s) {
+ // types
+ case "void":
+ case "bool":
+ case "int":
+ case "float":
+ case "string":
+ case "array":
+ case "object":
+ case "callable":
+ return "<code>";
+
+ // keywords
+ case "class":
+ case "interface":
+ case "namespace":
+ case "extends":
+ case "implements":
+ case "public":
+ case "protected":
+ case "private":
+ case "static":
+ case "final":
+ case "abstract":
+ // phrases
+ case "Optional":
+ case "optional":
+ return "<em>";
+ }
+
+ var is_namespace, is_method;
+
+ if ((is_method = (s.indexOf("::") !== -1)) || (is_namespace = (s.indexOf("\\") !== -1))) {
+ return "<a href=\"/" + s.replace(/::|\\/g, "/") + (is_method ? ".md":"") + "\">";
+ }
+
+ switch (s.toLowerCase()) {
+ // variables
+ default:
+ if (s.substring(0,1) !== "$") {
+ break;
+ }
+ // special constants
+ case "null":
+ case "true":
+ case "false":
+ return "<span class=\"var\">";
+ }
+
+ // constants
+ if (s.toUpperCase() === s) {
+ return "<code>";
+ }
+}
+function node(s) {
+ //console.log("node", s);
+
+ var t;
+
+ if ((t = type(s))) {
+ return $(t).text(s);
+ }
+ return document.createTextNode(s);
+}
+function wrap(n) {
+ var $n = $(n)
+ var a = [];
+
+ $n.text().split(/([^a-zA-Z_\\\$:]+)/).forEach(function(v) {
+ a.push(node(v));
+ });
+ $n.replaceWith(a);
+}
+function walk(i, e) {
+ //console.log("walk", i, e);
+
+ e && $.each(e.childNodes, function(i, n) {
+ //console.log(n.nodeName);
+ switch (n.nodeName) {
+ case "A":
+ break;
+ case "#text":
+ wrap(n);
+ break;
+ default:
+ walk(n);
+ }
+ });
+}
+$(document).ready(function() {
+ //console.log("ready");
+
+ $("h1,h2,h3,h4,h5,h6,p,li,code").each(walk);
+});
--- /dev/null
+<?php
+
+error_reporting(E_ALL &~ E_DEPRECATED);
+
+define("OUTPUT", fopen("php://memory", "w+"));
+
+function cut(array $lines, array $specs) {
+ $delim = "[[:space:]]+";
+ $bytes = [];
+ $fields= [];
+
+ foreach ($specs as $spec => $value) {
+ switch ($spec) {
+ case "d":
+ $delim = $value;
+ break;
+ case "b":
+ $bytes = $value;
+ break;
+ case "f":
+ $fields = $value;
+ break;
+ }
+ }
+
+ $result = [];
+ if ($bytes) {
+ $func = "substr";
+ } else {
+ $func = function($a, $o = 0, $l = 0) {
+ return join(" ", array_slice($a, $o, $l ? $l+1 : count($a)-$o));
+ };
+ }
+ foreach ($lines as $line) {
+ if ($bytes) {
+ $spec = $bytes;
+ } else {
+ $line = split($delim, $line);
+ $spec = $fields;
+ }
+
+ if ($spec[0] == "-") {
+ $result[] = $func($line, 0, $spec[1]);
+ } elseif ($spec[1] == "-") {
+ if (empty($spec[2])) {
+ $result[] = $func($line, $spec[0]);
+ } else {
+ $result[] = $func($line, $spec[0], $spec[2]-$spec[0]);
+ }
+ } else {
+ $result[] = $line{$spec[0]};
+ }
+ }
+ return $result;
+}
+
+function head($file, $lines = 1) {
+ $ld = [];
+ if (($fd = fopen($file, "r"))) {
+ while ($lines--) {
+ $ld[] = fgets($fd);
+ }
+ }
+ return $ld;
+}
+
+function ns($file) {
+ return str_replace("/", "\\", str_replace("//", "/", trim($file, "/.")));
+}
+
+function urlpath($dir, $file) {
+ return (strlen($dir) ? $dir . "/" : "") . urlencode($file);
+}
+
+function ls($dir, $invert = false) {
+ fprintf(OUTPUT, "<ul>\n");
+ foreach (scandir($dir) as $file) {
+ $dir = trim($dir, "./");
+ $html = "";
+ if ($file === ".") {
+ continue;
+ } elseif ($file === "..") {
+ if ($dir === "" || $invert) {
+ continue;
+ }
+ $name = sprintf("namespace %s", ns(dirname($dir)));
+ } elseif (!$invert && is_dir("./$dir/$file")) {
+ $name = sprintf("namespace %s", ns("./$dir/$file"));
+ } elseif (!$invert && ctype_upper($file{0})) {
+ $name = join(" ", cut(head("./$dir/$file"), ["f"=>"1-2"]));
+ } elseif (!$invert || ctype_upper($file{0})) {
+ continue;
+ } else {
+ $name = ns($dir)."::".basename($file, ".md");
+ $html = "<p>".join(" ", cut(head("./$dir/$file"), ["f"=>"1-"]))."</p>";
+ }
+
+ fprintf(OUTPUT, "<li><a href=\"/%s\">%s</a>%s</li>\n",
+ urlpath($dir, $file),
+ htmlspecialchars($name),
+ $html);
+ }
+ fprintf(OUTPUT, "</ul>\n");
+}
+
+function ml($file) {
+ $pi = pathinfo($file);
+ if (ctype_upper($pi["filename"][0])) {
+ fprintf(OUTPUT, "<h2>Methods:</h2>\n");
+ $el = $pi["dirname"] . "/" . $pi["filename"];
+ ls($el, true);
+ }
+}
+
+function md($file) {
+ $r = fopen($file, "r");
+ $md = MarkdownDocument::createFromStream($r);
+ $md->compile();
+ $md->writeHtml(OUTPUT);
+ unset($md);
+ fclose($r);
+
+ // BS Markdown seeks around...
+ fseek(OUTPUT, 0, SEEK_END);
+
+ ml($file);
+}
+
+$r = new http\Env\Request;
+$u = new http\Url($r->getRequestUrl());
+$t = ["css"=>"text/css", "js"=>"application/javascript"];
+
+switch($u->path) {
+case "/index.js":
+case "/index.css":
+ $s = new http\Env\Response;
+ $s->setHeader("Content-type", $t[pathinfo($u->path, PATHINFO_EXTENSION)]);
+ $s->setBody(new http\Message\Body(fopen(basename($u->path), "r")));
+ $s->send();
+ exit;
+}
+
+if (is_dir(".".$u->path)) {
+ ls(".".$u->path);
+} else {
+ md(".".$u->path);
+}
+
+?>
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<title><?=$u->path?></title>
+<link rel="stylesheet" href="/index.css">
+</head>
+<body>
+<?php
+rewind(OUTPUT);
+fpassthru(OUTPUT);
+fclose(OUTPUT);
+?>
+<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
+<script src="/index.js"></script>
+</body>
+</html>