<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head><title>Network Setup</title><link rel="stylesheet" href="/style/" type="text/css"/><link rel="icon" href="/favicon.png" type="image/png"/><meta name="viewport" content="width=device-width,initial-scale=1"/></head><body><span style="position:fixed;bottom:0;right:0"><a href="?debug">π</a></span><h1>Network Setup</h1><br/><fieldset><legend>Description</legend><div class="info">Several services are available directly: <a href="http://nginx.org/" target="_blank" title="fast and stable HTTP server">NGINX</a> (<a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol" target="_blank">HTTP</a>), <a href="http://www.openssh.com/" target="_blank" title="SSH server and client with tunneling support">OpenSSH</a> (<a href="https://en.wikipedia.org/wiki/Secure_Shell" target="_blank">SSH</a>), <a href="http://www.tinc-vpn.org/" target="_blank" title="VPN that uses tunnelling and encryption to create a mesh network">tinc</a>, and <a href="https://shadowsocks.org/" target="_blank" title="secure socks5 proxy">ShadowSocks</a>.<br/><br/>All of these services are also availble on port 443 throuh port multiplexing using <a href="http://www.haproxy.org/" target="_blank" title="HTTP proxy with load balancing support">HAProxy</a>. This is useful for public places where access to ports other than 80 and 443 is blocked by a firewall.<br/><br/><a href="https://en.wikipedia.org/wiki/Transport_Layer_Security" target="_blank">SSL/TLS</a> termination is done by <a href="http://www.haproxy.org/" target="_blank" title="HTTP proxy with load balancing support">HAProxy</a>. Traffic is decrypted and redirected back to <a href="http://www.haproxy.org/" target="_blank" title="HTTP proxy with load balancing support">HAProxy</a> to be forwared to the correct service. This enables <a href="https://en.wikipedia.org/wiki/HTTPS" target="_blank">HTTPS</a> but also allows any of the other protocols to be tunneled through TLS to prevent nosy firewalls from snooping. There is a special redirect from <a href="http://www.haproxy.org/" target="_blank" title="HTTP proxy with load balancing support">HAProxy</a> to <a href="http://nginx.org/" target="_blank" title="fast and stable HTTP server">NGINX</a> to support <a href="https://en.wikipedia.org/wiki/HTTP/2" target="_blank">HTTP/2</a>.</div></fieldset><br/><div class="content"><fieldset><legend>Home setup</legend><img src="gfw.svg" alt="network layout"/></fieldset><br/><fieldset><legend>Tools</legend><div class="info"><table><tbody><tr><td><a href="http://www.openssh.com/" target="_blank">OpenSSH</a></td><td>SSH server and client with tunneling support</td></tr><tr><td><a href="http://nginx.org/" target="_blank">NGINX</a></td><td>fast and stable HTTP server</td></tr><tr><td><a href="http://www.haproxy.org/" target="_blank">HAProxy</a></td><td>HTTP proxy with load balancing support</td></tr><tr><td><a href="https://shadowsocks.org/" target="_blank">ShadowSocks</a></td><td>secure socks5 proxy</td></tr><tr><td><a href="http://www.tinc-vpn.org/" target="_blank">tinc</a></td><td>VPN that uses tunnelling and encryption to create a mesh network</td></tr></tbody></table></div></fieldset><br/><fieldset><legend>Examples</legend>SSH to port 443:<pre>ssh -p 443 marcoen.dev</pre>SSH to port 443 over TLS:<pre>ssh -o ProxyCommand="openssl s_client -connect %h:%p -quiet" -p 443 marcoen.dev</pre></fieldset></div></body></html>
<?php require_once('highlightdoc.php'); require_once('libs/style.php'); $title = 'Network Setup'; $doc = makeHighlightdoc($title); $headings = array( makeTag('h1', $title), ); $blocks = array(); $tools_data = array( array('name' => 'OpenSSH', 'url' => 'http://www.openssh.com/', 'desc' => 'SSH server and client with tunneling support'), array('name' => 'NGINX', 'url' => 'http://nginx.org/', 'desc' => 'fast and stable HTTP server'), //array('name' => 'OpenVPN', 'url' => 'https://openvpn.net/', 'desc' => 'popular VPN software'), array('name' => 'HAProxy', 'url' => 'http://www.haproxy.org/', 'desc' => 'HTTP proxy with load balancing support'), array('name' => 'ShadowSocks', 'url' => 'https://shadowsocks.org/', 'desc' => 'secure socks5 proxy'), array('name' => 'tinc', 'url' => 'http://www.tinc-vpn.org/', 'desc' => 'VPN that uses tunnelling and encryption to create a mesh network'), //array('name' => 'sslh', 'url' => 'http://www.rutschle.net/tech/sslh.shtml', 'desc' => 'port multiplexer that has built-in support for HTTP, TLS, SSH, OpenVPN, tinc and XMPP'), //array('name' => 'OpenSSL', 'url' => 'http://openssl.org/', 'desc' => 'Library and tools for SSL/TLS connections'), ); $tools = array(); foreach ($tools_data as $item) { $tools[strtolower($item['name'])] = makeExternalLink($item['url'], $item['name'])->set('title', $item['desc']); } $tools_table = makeTable($tools_data, array( function($item) { return makeExternalLink($item['url'], $item['name']); }, function($item) { return $item['desc']; }, )); $legend_items = array( array('color' => 'black', 'desc' => 'Initial setup'), array('color' => 'green', 'desc' => 'Added services'), array('color' => 'blue', 'desc' => 'Port 443 multiplexing'), array('color' => 'red', 'desc' => 'TLS tunneling'), ); $legend = makeTable($legend_items, array( function($item) { return makeSpan($item['color'])->add(makeStyleAttribute(makeStyle()->set('color', $item['color']))); }, function($item) { return $item['desc']; }, )); function makeWikiLink($name, $page = null) { return makeExternalLink('https://en.wikipedia.org/wiki/' . (is_null($page) ? $name : $page), $name); } $http2 = makeWikiLink('HTTP/2'); $ssl = makeWikiLink('SSL/TLS', 'Transport_Layer_Security'); $http = makeWikiLink('HTTP', 'Hypertext_Transfer_Protocol'); $https = makeWikiLink('HTTPS'); $ssh = makeWikiLink('SSH', 'Secure_Shell'); $services = array( makeSpaceList(array($tools['nginx'], makeBraces($http))), makeSpaceList(array($tools['openssh'], makeBraces($ssh))), //$tools['openvpn'], $tools['tinc'], $tools['shadowsocks'], ); $explanation = makeParagraphs(array( array(makeSpaceList(array( makeSentence(array('Several services are available directly:', makeAndList($services))), //makeSentence(array('Several services are available directly:', $tools_table)), ))), array(makeSpaceList(array( makeSentence(array('All of these services are also availble on port 443 throuh port multiplexing using ', $tools['haproxy'])), makeSentence(array('This is useful for public places where access to ports other than 80 and 443 is blocked by a firewall')), ))), array(makeSpaceList(array( makeSentence(array($ssl, 'termination is done by', $tools['haproxy'])), makeSentence(array('Traffic is decrypted and redirected back to', $tools['haproxy'], 'to be forwared to the correct service')), makeSentence(array('This enables', $https, 'but also allows any of the other protocols to be tunneled through TLS to prevent nosy firewalls from snooping')), makeSentence(array('There is a special redirect from', $tools['haproxy'], 'to', $tools['nginx'], 'to support', $http2)), ))), )); $haproxyConfig = <<<CONF frontend fe_https mode tcp bind :::446 ssl crt /etc/haproxy/certs alpn h2,http/1.1 npn h2,http/1.1 acl alpn_h2 ssl_fc_alpn -i h2 acl npn_h2 ssl_fc_npn -i h2 use_backend be_h2c if alpn_h2 or npn_h2 default_backend be_sslh backend be_h2c mode tcp server domain localhost:8002 backend be_sslh mode tcp server domain localhost:443 CONF; $headings[] = makeFieldset('Description')->add(makeDiv('info', $explanation)); $blocks[] = makeFieldset('Home setup')->add(/*$legend,*/ makeImg('gfw.svg', 'network layout')); $blocks[] = makeFieldset('Tools')->add(makeDiv('info', $tools_table)); //$blocks[] = makeFieldset('HAProxy config', makeTag('pre', $haproxyConfig)); //$blocks[] = makeFieldset('HAProxy config', makeTag('pre', file_get_contents('haproxy.cfg'))); $host = $_SERVER['HTTP_HOST']; $blocks[] = makeFieldset('Examples')->add(array( array('SSH to port 443:', makeTag('pre', 'ssh -p 443 ' . $host)),//$doc->makeHighlight('bash', 'ssh -p 443 ' . $host)), array('SSH to port 443 over TLS:', makeTag('pre', 'ssh -o ProxyCommand="openssl s_client -connect %h:%p -quiet" -p 443 ' . $host)),//$doc->makeHighlight('bash', 'ssh -o ProxyCommand="openssl s_client -connect %h:%p -quiet" -p 443 ' . $host)), )); $doc->body()-> add(makeLines(array( makeLines($headings), //makeLines($blocks), makeDiv('content', makeLines($blocks)), //makeDiv('content', array($headings, $blocks)), ))); $doc->render();
<?php require_once('basedoc.php'); require_once('highlight.php'); class highlightdoc extends basedoc { use highlight; } function makeHighlightdoc($title) { return new highlightdoc($title); }
<?php require_once('libs/htmldoc.php'); require_once('libs/html_utils.php'); require_once('libs/style.php'); require_once('libs/http.php'); require_once('debug.php'); class basedoc extends htmldoc { function __construct($title, $style = '/style/', $favicon = '/favicon.png') { parent::__construct($title); $this ->setLanguage('en') //->add(makeStyleAttribute(makeStyle()->set('background-color', '#000'))) ; $this->head() //->add(makeCSSLink('/normalize.css')) ->add(makeCSSLink($style)) ->add(makePngIconLink($favicon)) ->add(makeMeta('viewport', 'width=device-width,initial-scale=1')) //->add(makeHttpEquiv('Cache-control', 'public')) //->add(makeHttpEquiv('Cache-control', 'max-age=120')) ; $bottom_right = makeStyle()->set('position', 'fixed')->set('bottom', '0')->set('right', '0'); $this->body()->add(makeTag('span', makeLink('?debug', 'π'))->set('style', $bottom_right)); } public function render() { makeHttp() ->addHeader('Cache-Control', array('public, max-age=120')) ->render(makeDebuggable($this, 'xhtml5', 'xml')); } } function makeBasedoc($title, $style = '/style/', $favicon = '/favicon.png') { return new basedoc($title, $style, $favicon); }
<?php include_once('libs/xmldoc.php'); include_once('libs/html_utils.php'); class htmldoc extends xmldoc { private $_head; private $_body; public function __construct($title) { parent::__construct('html', 'application/xhtml+xml'); $this ->setDoctype('html') ->set('xmlns', 'http://www.w3.org/1999/xhtml') ->add($this->_head = makeTag('head')) ->add($this->_body = makeTag('body')) ; $this->_head //->add(makeTag('meta')->set('charset', 'UTF-8')) //->add(makeMeta('generator', 'MarcoenMedia')) ->add(makeTag('title', $title)) ; } public function setLanguage($language) { parent::setLanguage($language); $this->root()->set('lang', $language); return $this; } public function head() { return $this->_head; } public function body() { return $this->_body; } } function makeHtmldoc($title) { return new htmldoc($title); }
<?php include_once('libs/doc.php'); include_once('libs/xml.php'); include_once('libs/text.php'); class xmldoc extends doc { private $_root; private $_doctype; public function __construct($root, $mimetype = 'application/xml') { parent::__construct($mimetype); $this->_root = makeTag($root); } public function setDoctype($doctype) { $this->_doctype = $doctype; return $this; } public function setLanguage($language) { parent::setLanguage($language); $this->_root->set('xml:lang', $language); return $this; } public function root() { return $this->_root; } public function text() { $text = array(); $text[] = '<?xml version="1.0" encoding="UTF-8"?>'; if ($this->_doctype) { $text[] = '<!DOCTYPE ' . $this->_doctype . '>'; } $text[] = $this->_root->text(); return implode("\n", $text) . "\n"; } public function set($key, $value) { $this->_root->set($key, $value); return $this; } public function add($item) { $this->_root->add($item); return $this; } } function makeXmldoc($root) { return new xmldoc($root); }
<?php require_once('libs/text.php'); abstract class doc implements toText { private $_type; private $_encoding; private $_language; private $_name; public function __construct($type /*= 'application/octet-stream'*/, $encoding = 'UTF-8') { $this->_type = $type; $this->_encoding = $encoding; } public function setLanguage($language) { $this->_language = $language; return $this; } public function setName($name) { $this->_name = $name; return $this; } public function type() { return $this->_type; } public function encoding() { return $this->_encoding; } public function language() { return $this->_language; } public function name() { return $this->_name; } }
<?php interface toText { public function text(); } class text implements toText { private $_text; function __construct($text) { $this->_text = $text; } public function text() { return $this->_text; } } function makeText($value) { return new text($value); }
<?php require_once('libs/text.php'); interface container { public function isempty(); } abstract class xml implements toText { } class nodelist extends xml implements container { private $_children = array(); public function text() { $text = ''; foreach ($this->_children as $node) { if (!is_a($node, 'toText')) continue; $text .= is_a($node, 'xml') ? $node->text() : htmlspecialchars($node->text()); } return $text; } public function add($node) { switch (gettype($node)) { case 'object': if (is_a($node, 'xml') || is_a($node, 'toText')) { $this->_children[] = $node; break; } // throw error? break; case 'array': foreach ($node as $subnode) { $this->add($subnode); } break; case 'string': case 'double': case 'integer': $this->_children[] = makeText($node); break; default: // throw error? } return $this; } public function isempty() { return empty($this->_children); } } class xmlAttribute implements toText { private $_name; private $_value; function __construct($name, $value) { $this->_name = $name; $this->_value = $value; } public function name() { return $this->_name; } public function value() { return $this->_value; } public function text() { return $this->_name . '="' . htmlspecialchars($this->_text($this->_value)) . '"'; } private function _text($content) { switch(gettype($content)) { case 'array': $text = ''; foreach ($content as $element) { $text .= $this->_text($element); } return $text; case 'object': if (is_a($this->_value, 'toText')) { return $content->text(); } default: return $content; } } } class attributes implements toText, container { private $_attributes = array(); public function text() { $text = array(); foreach ($this->_attributes as $xmlAttribute) { $text[] = $xmlAttribute->text(); } return implode(' ', $text); } public function add(xmlAttribute $xmlAttribute) { $this->_attributes[$xmlAttribute->name()] = $xmlAttribute; return $this; } public function isempty() { return empty($this->_attributes); } } class tag extends xml { private $_children; private $_name; private $_attributes; function __construct(string $name) { $this->_name = $name; $this->_attributes = new attributes(); $this->_children = new nodelist(); } public function add() { foreach (func_get_args() as $node) { if (is_array($node)) { foreach ($node as $subnode) { $this->add($subnode); } } else if (is_a($node, 'xmlAttribute')) { $this->_attributes->add($node); } else { $this->_children->add($node); } } return $this; } public function set(string $name, $value = null) { $this->_attributes->add(new xmlAttribute($name, $value)); return $this; } public function attributes() { return $this->_attributes; } public function text() { return '<' . $this->_name . ($this->_attributes->isempty() ? '' : ' ' . $this->_attributes->text()) . ($this->_children->isempty() ? '/>' : '>' . $this->_children->text() . '</' . $this->_name . '>'); } } class raw extends xml { private $_text; function __construct(string $text) { $this->_text = $text; } public function text() { return $this->_text; } } function makeTag($name, $content = null) { $tag = new tag($name); if (!is_null($content)) { $tag->add($content); } return $tag; } function makeRaw($content) { return new raw($content); } function makeAttribute($name, $value) { return new xmlAttribute($name, $value); } function makeTaglist() { return new nodelist(); } function glueTags($glue, array $tags) { $glued = array(); if (empty($tags)) return; $glued[] = array_shift($tags); foreach($tags as $tag) { $glued[] = $glue; $glued[] = $tag; } return $glued; }
<?php require_once('libs/xml.php'); require_once('libs/css.php'); function makeMeta($name, $content) { return makeTag('meta')->set('name', $name)->set('content', $content); } function makeHttpEquiv($name, $content) { return makeTag('meta')->set('http-equiv', $name)->set('content', $content); } function makeHyperlink($path, $content = null) { return makeTag('a', $content)->set('href', $path); } function makeAnchor($id) { return makeTag('a')->set('id', $id); } function makeRelLink($rel, $uri) { return makeTag('link')->set('rel', $rel)->set('href', $uri); } function makeCSSLink($path) { return makeRelLink('stylesheet', $path)->set('type', 'text/css'); } function makeIconLink($path) { return makeRelLink('icon', $path); } function makePngIconLink($path) { return makeIconLink($path)->set('type', 'image/png'); } function makeFieldset($title = null, $content = null) { return makeTag('fieldset', array( is_null($title) ? null : makeTag('legend', $title), $content, )); } function makeDiv($class = null, $content = null) { return makeTag('div', array( is_null($class) ? null : makeAttribute('class', $class), $content, )); } function makeSpan($content = null) { return makeTag('span', $content); } function makeTableHead(array $items) { $addTh = function($tag, $item) { return $tag->add(makeTag('th', $item)); }; return makeTag('thead', array_reduce($items, $addTh, makeTag('tr'))); } function makeTableRow(array $items) { $addTd = function($tag, $item) { return $tag->add(makeTag('td', $item)); }; return array_reduce($items, $addTd, makeTag('tr')); } function makeTableRows(array $items, array $transforms) { $makeRow = function($item) use ($transforms) { $applyTransform = function($trans) use ($item) { return $trans($item); }; return makeTableRow(array_map($applyTransform, $transforms)); }; return array_map($makeRow, $items); } function makeTableBody(array $items, array $transforms) { return makeTag('tbody')->add(makeTableRows($items, $transforms)); } function makeComplexTable(array $items, array $transforms) { return makeTag('table', array( makeTableHead(array_column($transforms, 'title')), makeTableBody($items, array_column($transforms, 'transform')), )); } function makeTable(array $items, array $transforms) { return makeTag('table', makeTableBody($items, $transforms)); } function makeUrl($url, array $params, $anchor = null) { if (!empty($params)) { $encodeParams = function ($key, $value) { return $value === '' ? urlencode($key) : urlencode($key) . '=' . urlencode($value); }; $url .= '?' . implode('&', array_map($encodeParams, array_keys($params), $params)); } if (!is_null($anchor)) { $url .= '#' . $anchor; } return empty($url) ? '?' : $url; // link to '?' to clear current query parameters } function makeLink($baseUrl, $label = null, array $params = array(), $anchor = null) { return makeHyperlink(makeUrl($baseUrl, $params, $anchor), $label); } function makeAnchorLink($anchor, $label = null) { return makeLink('', $label, array(), $anchor); } function makeExternalLink($baseUrl, $label = null, array $params = array()) { return makeLink($baseUrl, $label, $params)->set('target', '_blank'); } function makeScript($content = null) { return makeTag('script', $content); } function makeScriptLink($url) { return makeScript()->set('src', $url); } function makeImg($src, $alt = null) { $tag = makeTag('img')->set('src', $src); if ($alt) { $tag->set('alt', $alt); } return $tag; } function makeListItems(array $items) { return array_map(function($item) { return makeTag('li', $item); }, $items); } function makeList(array $items) { return makeTag('ul', makeListItems($items)); } function makeNumberedList(array $items) { return makeTag('ol', makeListItems($items)); } function makeCssTag(css $css) { return makeTag('style') //->set('type', 'text/css') // not needed according to validator.w3.org and https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style ->add($css); } function makeStyleAttribute(style $style) { return makeAttribute('style', $style->text()); } function makeSpaceList(array $items) { return glueTags(' ', $items); } function makeCommaList(array $items) { return glueTags(', ', $items); } function makeSerialList($delim, array $items) { if (count($items) <= 1) { return $items; } if (count($items) == 2) { return makeSpaceList(array($items[0], $delim, $items[1])); } // With or without serial comma? https://en.wikipedia.org/wiki/Serial_comma $last = array_pop($items); //return makeSpaceList(array(makeCommaList($items), $delim, $last)); // no serial comma $items[] = makeSpaceList(array($delim, $last)); return makeCommaList($items); } function makeAndList(array $items) { return makeSerialList('and', $items); } function makeOrList(array $items) { return makeSerialList('or', $items); } function makeSentence(array $items) { return array(makeSpaceList($items), '.'); } function makeBraces($data) { return array('(', $data, ')'); } function makeLines(array $items) { return glueTags(makeTag('br'), $items); } function makeParagraphs(array $items) { $br = makeTag('br'); return glueTags(array($br, $br), $items); //return array_map(function($item) { return makeTag('p', $item); }, $items); } function makeQuote($content) { return array('“', $content, '”'); }
<?php require_once('libs/doc.php'); require_once('libs/style.php'); class css extends doc { private $_entries = array(); public function __construct() { parent::__construct('text/css'); } public function add($selector, $style) { if (!array_key_exists($selector, $this->_entries)) { $this->_entries[$selector] = makeStyle(); } $this->_entries[$selector]->add($style); return $this; } public function text() { $text = ''; foreach ($this->_entries as $selector => $style) { $text .= $selector . '{' . $style->text() . '}'; } return $text; } } function makeCss() { return new css(); }
<?php require_once('libs/text.php'); class styleAttribute implements toText { private $_name; private $_value; public function __construct($name, $value) { $this->_name = $name; $this->_value = $value; } public function name() { return $this->_name; } public function value() { return $this->_value; } public function text() { return $this->_name . ':' . $this->_value; } } class style implements toText { private $_attributes = array(); public function add($style) { switch (gettype($style)) { case 'object': switch (get_class($style)) { case 'style': $this->addStyle($style); break; case 'styleAttribute': $this->addAttribute($style); break; } // throw error? break; case 'array': foreach ($style as $child) { $this->add($child); } break; } return $this; } public function set($name, $value) { $this->add(new styleAttribute($name, $value)); return $this; } public function addStyle(style $style) { foreach ($style->_attributes as $attribute) { $this->addAttribute($attribute); } return $this; } public function addAttribute(styleAttribute $attribute) { $this->_attributes[$attribute->name()] = $attribute; return $this; } public function text() { return implode(';', array_map(function($attribute) { return $attribute->text(); }, $this->_attributes)); } } function makeStyle() { return new style(); }
<?php require_once('libs/doc.php'); class http { private $_headers; private function makeHeader($name, $params = null) { if (empty($params)) { return $name; } if (is_array($params)) { $params = implode(';', $params); } return $name . ':' . $params; } public function addHeader($name, $params) { $this->_headers[] = $this->makeHeader($name, $params); return $this; } private function addDocHeaders(doc $doc) { $this ->addHeader('Content-type', array( $doc->type(), 'charset=' . $doc->encoding(), )) ; if ($language = $doc->language()) { $this->addHeader('Content-Language', $language); } if ($name = $doc->name()) { $this->addHeader('Content-Disposition', array( 'attachment', //($inline ? 'inline' : 'attachment'), //'filename="' . $filename . '"', // unescaped, unsafe? //'filename=' . rawurlencode($filename), // url-encoded, for IE? 'filename*=UTF-8\'\'' . rawurlencode($name), // special UTF-8 format supported by modern browsers, but not cURL :/ )) ; } return $this; } public function render(doc $doc) { $content = $doc->text(); $etag = sha1($content); //header('etag:' . $etag); //header('x-compute-time-ms:' . round((microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 100000) / 100) ; $this ->addHeader('etag', $etag) ->addHeader('x-compute-time-ms', round((microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 100000) / 100) ->addDocHeaders($doc) ->addHeader('Access-Control-Allow-Origin', '*') //->addHeader('Cache-Control', 'public, max-age=120') //->addHeader('Cache-Control', 'public') //->addHeader('Pragma', 'cache') ; foreach ($this->_headers as $header) { header($header); } if (array_key_exists('HTTP_IF_NONE_MATCH', $_SERVER) && $_SERVER['HTTP_IF_NONE_MATCH'] == $etag) { header('HTTP/1.0 304 Not Modified'); return; } ob_start('ob_gzhandler'); echo $content; ob_end_flush(); } } function makeHttp() { return new http; }
<?php require_once('libs/doc.php'); function makeDebuggable(doc $doc, $title, $style = null) { $type = $doc->type(); if (!$style) $style = $type; if (array_key_exists('debug', $_GET)) { $_GET = array(); require_once('debugdoc.php'); $uri = makeUrl('http://' . $_SERVER['HTTP_HOST'] . $_SERVER['DOCUMENT_URI'], $_GET); return makeDebugdoc($title) ->addFileBlock($title, $style, $doc) ->addSources() ->addValidators($uri, $type) ->addServerVars() ->build() ->highlight() ; } return $doc; }
<?php require_once('libs/html_utils.php'); trait highlight { private $_highlight = false; public function makeHighlight($class, $content) { $this->_highlight = true; return makeTag('pre', makeTag('code', $content)->set('class', $class))->set('class', 'wrap'); } public function highlight() { if (!$this->_highlight) { return; } require_once('libs/css.php'); $css = makeCss() //->add('.hljs', makeStyle()->set('background', 'inherit')) //->add('.hljs', makeStyle()->set('color', 'Black')) ->add('.hljs-variable,.hljs-attr,.hljs-attribute', makeStyle()->set('color', 'DarkBlue')) ->add('.hljs-title,.hljs-selector-tag,.hljs-selector-class,.hljs-meta,.hljs-pi', makeStyle()->set('font-weight', 'bold')) ->add('.hljs-keyword,.hljs-name,.hljs-built_in', makeStyle()->set('color', 'DarkBlue')->set('font-weight', 'bold')) ->add('.hljs-value,.hljs-string,.hljs-number', makeStyle()->set('color', 'DarkRed')) ->add('.hljs-comment', makeStyle()->set('color', 'DarkSlateGray')->set('font-style', 'italic')) //->add('.hljs-function', makeStyle()->set('font-style', 'italic')) ->add('pre.wrap', makeStyle()->set('white-space', 'pre-wrap')) ; //$highlightLocation = '//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.8.0/'; //$highlightLocation = '//cdn.jsdelivr.net/highlight.js/8.8.0/'; //$highlightLocation = '//cdn.jsdelivr.net/highlight.js/9.1.0/'; $this->head()->add(array( //makeCSSLink($highlightLocation . 'styles/default.min.css'), makeCssTag($css), )); $this->body()->add(array( //makeScriptLink($highlightLocation . 'highlight.min.js'), makeScriptLink('/highlight.pack.js'), //makeScript('hljs.initHighlightingOnLoad();'), makeScriptLink('/highlight_init.js'), )); return $this; } }
<?php require_once('highlightdoc.php'); class debugdoc extends highlightdoc { protected $_debuggers = array(); function __construct($title) { parent::__construct($title, '/style-debug/'); } protected function addDebugger($id, $title, $tag) { $this->_debuggers[$id][] = array('id' => $id, 'title' => $title, 'tag' => $tag); return $this; } protected function makeFileBlock($title, $style, $content) { return makeFieldset($title, $this->makeHighlight($style, $content)); } public function addFileBlock($title, $style, $content) { return $this->addDebugger('file', $title, $this->makeFileBlock($title, $style, $content)); } protected function makeSources() { $blocks = array( makeFieldset('source files', $table = makeTag('table')), ); foreach(get_included_files() as $id => $file) { $anchor = 'source' . $id; $table->add(makeTableRow(array(makeAnchorLink($anchor, $file)))); $modified = date('Y-m-d H:i:s T', filemtime($file)); $blocks[] = array( makeAnchor($anchor), $this->makeFileBlock($file . ', ' . $modified, 'php', file_get_contents($file)), ); } return makeLines($blocks); } public function addSources() { return $this->addDebugger('sources', 'source files', $this->makeSources()); } private function makeValidators($uri, $type) { $validators = array(); //$validators[] = $type; if (in_array($type, array('text/css', 'application/xhtml+xml'))) { $validators[] = makeExternalLink('https://jigsaw.w3.org/css-validator/validator', 'W3C CSS Validator', array('uri' => $uri)); } if (in_array($type, array('application/atom+xml'))) { $validators[] = makeExternalLink('https://validator.w3.org/feed/check.cgi', 'W3C Feed Validator', array('url' => $uri)); } if (in_array($type, array('application/xhtml+xml'))) { $validators[] = makeExternalLink('https://validator.w3.org/nu/', 'Nu Html Checker', array('doc' => $uri)); $validators[] = makeExternalLink('https://validator.w3.org/i18n-checker/check', 'W3C Internationalization Checker', array('uri' => $uri)); //makeExternalLink('https://ssldecoder.org/', 'SSL Decoder', array('host' => $uri)), //makeExternalLink('https://validator.w3.org/checklink', 'Link Checker', array('uri' => $uri)), //makeExternalLink('https://validator.w3.org/mobile/check', 'Mobile Checker', array('docAddr' => $uri)), //makeExternalLink('https://html5.validator.nu/', 'html5.validator.nu', array('doc' => $uri)), } return empty($validators) ? null : makeFieldset('validators', makeTable($validators, array(function($tag) { return $tag; }))); } public function addValidators($uri, $type) { $validators = $this->makeValidators($uri, $type); return $validators ? $this->addDebugger('validators', 'validators', $validators) : $this; } public function addServerVars() { $title = 'server variables'; $tag = makeFieldset($title, $table = makeTag('table')); foreach ($_SERVER as $key => $value) { $table->add(makeTableRow(array(makeTag('strong', $key), $value))); } return $this->addDebugger('vars', $title, $tag); } public function build() { $flatten = array(); foreach ($this->_debuggers as $id => $debuggers) { if (count($debuggers) == 1) { $flatten[] = array('id' => $id, 'debugger' => $debuggers[0]); } else { foreach ($debuggers as $num => $debugger) { $flatten[] = array('id' => ($id . $num), 'debugger' => $debugger); } } } $menu = makeFieldset('debug', makeTable($flatten, array(function($debugger) { return makeLink('', $debugger['debugger']['title'], array(), $debugger['id']); }))); $blocks = array(); $blocks[] = $menu; foreach($flatten as $debugger) { $blocks[] = array( makeAnchor($debugger['id']), $debugger['debugger']['tag'], ); } $this->body()->add(makeLines($blocks)); return $this; } } function makeDebugdoc($title = 'debug') { return new debugdoc($title); }