123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651 |
- <?php
- /**
- * Wrapper around php tidy class.
- *
- * @param string $html
- *
- * @return void
- */
- function applyTidy (&$html) {
- $config = array(
- 'indent' => TRUE,
- 'output-xhtml' => TRUE,
- 'sort-attributes' => 'alpha',
- 'wrap' => 200,
- );
- $tidy = new tidy();
- $tidy->parseString($html, $config, 'utf8');
- $tidy->cleanRepair();
- $html = (string) $tidy;
- }
- class Context {
- protected $logLevelClasses = NULL;
- /**
- * Base URL for the script.
- *
- * @var string
- */
- protected $base;
- /**
- * Graphics context
- *
- * @var GraphicsContext
- */
- protected $gc = NULL;
- /**
- * Logging level, as per RFC5424
- *
- * @link http://php.net/network.constants.php
- *
- * @var integer
- */
- protected $logLevel = NULL;
- /**
- * Messages for display.
- *
- * @var array
- */
- protected $messages = array();
- /**
- * Requested path: <$PHP_SELF>?q=a/b/c
- *
- * @var string
- */
- protected $path = NULL;
- /**
- * Tidy the output ?
- *
- * @var boolean
- */
- protected $tidy = NULL;
- /**
- * User information: logged or not ?
- *
- * @var boolean
- */
- protected $user = FALSE;
- function __construct() {
- $this->getTidy(); // Needed to check extension
- }
- function __destruct() {
- if (!empty($this->messages)) {
- $ret = (string) new Element('pre', array('class' => array('messages')),
- implode("\n", $this->getMessage(TRUE)));
- echo $ret;
- }
- }
- function __toString() {
- $ret = '<pre>' . print_r($this, TRUE) . '</pre>';
- return $ret;
- }
- /**
- * Get the base path where the script is located.
- *
- * This is helpful to locate other files using paths relative to the install.
- */
- public function getBase() {
- if (!isset($this->base)) {
- $this->base = dirname($_SERVER['SCRIPT_NAME']);
- if ($this->base == '/') {
- $this->base = '';
- }
- }
- return $this->base;
- }
- public function getLogLevel() {
- if (!isset($this->logLevel)) {
- $usLogLevel = NULL;
- foreach ($_GET as $key => $value) {
- if (strtolower($key) === 'loglevel') {
- $usLogLevel = (int) $value;
- break;
- }
- }
- if (!isset($usLogLevel)) {
- $usLogLevel = LOG_NOTICE;
- }
- if ($usLogLevel < LOG_EMERG) {
- $this->logLevel = LOG_EMERG;
- }
- elseif ($usLogLevel > LOG_DEBUG) {
- $this->logLevel = LOG_DEBUG;
- }
- else {
- $this->logLevel = $usLogLevel; // We now know it to be safe
- }
- }
- return $this->logLevel;
- }
- public function getLogLevelClass($logLevel) {
- if (!isset($this->logLevelClasses)) {
- $this->logLevelClasses = array(
- LOG_EMERG => 'error',
- LOG_ALERT => 'error',
- LOG_CRIT => 'error',
- LOG_ERR => 'error',
- LOG_WARNING => 'warning',
- LOG_NOTICE => 'warning',
- LOG_INFO => 'status',
- LOG_DEBUG => 'status',
- );
- }
- if ($logLevel < LOG_EMERG) {
- $logLevel = LOG_EMERG;
- }
- elseif ($logLevel > LOG_DEBUG) {
- $logLevel = LOG_DEBUG;
- }
- return $this->logLevelClasses[$logLevel];
- }
- public function getMessage($clear = FALSE) {
- $ret = $this->messages;
- if ($clear) {
- $this->messages = array();
- }
- return $ret;
- }
- /**
- * Return the requested path.
- *
- * @param string $path
- */
- public function getPath() {
- if (!isset($this->path)) {
- $this->path = empty($_GET['q']) ? '' : $_GET['q'];
- }
- return $this->path;
- }
- /**
- * Return the "tidy" status.
- *
- * Will only be TRUE if requested (possibly by default) and extension is
- * loaded. A warning will be generated if tidy is requested but extension is
- * not loaded.
- */
- public function getTidy() {
- static $notified = FALSE;
- if (!isset($this->tidy)) {
- $this->tidy = TRUE;
- foreach ($_GET as $key => $value) {
- if (strtolower($key) === 'tidy') {
- $this->tidy = !!$value;
- break;
- }
- }
- if (!$notified && $this->tidy && !extension_loaded('tidy')) {
- $this->setMessage(strtr('Extension @tidy requested but missing: skipping', array(
- '@tidy' => 'tidy',
- )), LOG_WARNING);
- $notified = TRUE;
- $this->tidy = FALSE;
- }
- }
- return $this->tidy;
- }
- /**
- * Add a message to the messages list if it is above the current logging level.
- *
- * @param string $text
- * @param integer $logLevel
- *
- * @return void
- */
- public function setMessage($text, $logLevel = LOG_NOTICE) {
- if ($logLevel <= $this->getlogLevel()) {
- if (is_array($text) || (is_object($text) && !method_exists($text, '__toString'))) {
- $this->messages[] = array('<pre>' . print_r($text, TRUE) . '</pre>', $logLevel);
- }
- else {
- $this->messages[] = array((string) $text, $logLevel);
- }
- }
- }
- }
- /**
- * A wrapper for XML elements.
- */
- class Element {
- public $attributes = array();
- public $name = NULL;
- public $new_line; // Add a new line after element
- public $value = NULL;
- public function __construct($name, $attr = NULL, $value = NULL) {
- $this->name = $name;
- $this->attributes = $attr;
- $this->value = $value;
- }
- /**
- * @link drupal7/includes/common.inc#check_plain()
- */
- public static function check_plain($text) {
- return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
- }
- /**
- * @link drupal7/includes/common.inc#drupal_attributes()
- */
- public static function getSerializedAttributes(array $attributes = array()) {
- foreach ($attributes as $attribute => &$data) {
- $data = implode(' ', (array) $data);
- $data = $attribute . '="' . self::check_plain($data) . '"';
- }
- return $attributes ? ' ' . implode(' ', $attributes) : '';
- }
- public function __toString() {
- $ret = '<'. $this->name;
- if (!empty($this->attributes) && is_array($this->attributes)) {
- $ret .= self::getSerializedAttributes($this->attributes);
- }
- if (empty($this->value)) {
- $ret .= ' />';
- }
- else {
- $ret .= '>';
- if ($this->value instanceof Element) {
- $ret .= (string) $this->value; // force __toString()
- }
- elseif (is_array($this->value)) {
- $ret .= implode("\n", $this->value);
- }
- else {
- $ret .= $this->value;
- }
- $ret .= "</$this->name>";
- }
- return $ret;
- }
- }
- class GraphicsContext {
- protected $palette = array();
- }
- abstract class Page extends Element {
- /**
- * The HTML body element of the page.
- *
- * @var array
- */
- protected $body;
- /**
- * The MIME content type with charset
- *
- * @var string
- */
- protected $contentType = 'text/html; charset=utf8';
- /**
- * @var Context
- */
- protected $context;
- /**
- * The HTML head element of the page.
- *
- * @var array
- */
- protected $head;
- /**
- * The array of HTTP headers
- *
- * @var array
- */
- protected $headers = array();
- /**
- * @var array
- */
- protected $finalized = array();
- /**
- * @var array
- */
- protected $styles;
- /**
- * Page constructor.
- *
- * @param Context $context
- * @param array $item
- * A router info array.
- *
- * @see Router::getInfo()
- */
- public function __construct(Context $context, array $item) {
- parent::__construct('html');
- $context->setMessage($item, LOG_DEBUG);
- $this->context = $context;
- $this->initializeHead();
- $this->initializeBody();
- // Will fail if not reimplemented as a concrete method in a child class
- $this->build();
- }
- public function __toString() {
- $this->finalizeHeader();
- $html = new Element('html', NULL, $this->getHead() . $this->getBody());
- return (string) $html;
- }
- public abstract function build();
- public function emitHeaders() {
- $this->finalizeHeader();
- foreach ($this->headers as $name => $value) {
- header("$name: $value");
- }
- }
- public function finalizeBody() {
- if (isset($this->finalized['body'])) {
- throw new Exception('Attempt to finalize already finalized body');
- }
- if ($message = $this->context->getMessage(TRUE)) {
- $items = array();
- foreach ($message as $row) {
- $items[] = new Element('li', array(
- 'class' => array($this->context->getLogLevelClass($row[1])),
- ), $row[0]);
- }
- $this->setBody(new Element('div', array('class' => array('messages')), $items), 'messages');
- }
- foreach ($this->getRegions() as $region) {
- $this->body[$region] = implode('', $this->body[$region]);
- }
- $this->finalized['body'] = TRUE;
- }
- public function finalizeHead() {
- if (isset($this->finalized['head'])) {
- throw new Exception('Attempt to finalize already finalized head');
- }
- $cssLink = new Element('link', array(
- 'rel' => 'stylesheet',
- 'type' => 'text/css',
- 'href' => $this->context->getBase() .'/memcache_ui.css',
- ));
- $this->setHead($cssLink);
- $this->finalized['head'] = TRUE;
- }
- public function finalizeHeader() {
- $contentType = $this->getHeader('content-type');
- if (empty($contentType)) {
- $this->setHeader('content-type', $this->contentType);
- }
- }
- public function getBody() {
- $this->finalizeBody();
- $body = new Element('body', NULL, $this->body);
- $ret = (string) $body;
- return $ret;
- }
- public function getHead() {
- $this->finalizeHead();
- $head = new Element('head', NULL, $this->head);
- $ret = (string) $head;
- return $ret;
- }
- public function getHeader($name) {
- }
- public function getRegions() {
- $ret = array(
- 'header',
- 'first sidebar',
- 'content top',
- 'messages',
- 'help',
- 'content',
- 'content bottom',
- 'second sidebar',
- 'footer',
- );
- return $ret;
- }
- public function initializeBody() {
- foreach ($this->getRegions() as $region) {
- $this->body[$region] = array();
- }
- }
- public function initializeHead() {
- $this->setHead(new Element('title', NULL, 'Memcache info'));
- }
- public function setBody($fragment, $region = 'content') {
- if (!in_array($region, $this->getRegions())) {
- $this->context->setMessage(strtr('Attempted to insert data in nonexistent region @region', array(
- '@region' => self::check_plain($region),
- )), LOG_WARNING);
- }
- $this->body[$region][] = $fragment;
- }
- public function setHead($item) {
- $this->head[] = $item;
- }
- public function setHeader($name, $value) {
- $this->headers[$name] = $value;
- }
- }
- /**
- * Provide common layout elements to child classes.
- *
- * - header
- */
- abstract class Page_Memcache_Base extends Page {
- function build() {
- $this->setBody(
- new Element('div', array('class' => array('head')),
- new Element('h1', array('class' => array('memcache')), array(
- new Element('span', array('class' => array('logo')),
- new Element('a', array('href' => 'http://pecl.php.net/package/memcache'), 'memcache')
- ),
- new Element('span', array('class' => array('nameinfo')), 'memcache_ui.php from an idea by <a href="http://livebookmark.net">Harun Yayli</a>'),
- ))),
- 'header'
- );
- }
- }
- class Page_Memcache_Main extends Page_Memcache_Base {
- function build() {
- parent::build();
- $this->setBody(new Element('p', NULL, 'Hello world'));
- }
- }
- class Page_Memcache_Server_Flush extends Page_Memcache_Base {
- function build() {
- parent::build();
- $this->setBody(new Element('p', NULL, 'Flush server'));
- }
- }
- class Page_Memcache_Slab_Overview extends Page_Memcache_Base {
- function build() {
- parent::build();
- $this->setBody(new Element('p', NULL, 'Slabs'));
- }
- }
- class Router {
- function __construct(Context $context) {
- $this->context = $context;
- }
- function getInfo() {
- $ret = array(
- '^server/(\w+)/flush/(\w+)$' => array(
- 'page class' => 'page_server_flush',
- 'page arguments' => array('$1', '$2'),
- 'title callback' => 'title_server',
- 'title arguments' => 'page arguments',
- ),
- '^server/(\w+)/flush$' => array(
- 'page class' => 'Page_Memcache_Server_Flush',
- 'page arguments' => array('$1'),
- 'title callback' => 'title_server',
- 'title arguments' => 'page arguments',
- ),
- '^server/(\w+)/slab/(\d+)$' => array(
- 'page class' => 'page_slab_view',
- 'page arguments' => array('$1', '$2'),
- 'title callback' => 'title_slab',
- 'title arguments' => 'page arguments',
- ),
- '^server/(\w+)/key/(.+)/delete/(\w+)$' => array(
- 'page class' => 'page_variable_delete_confirm',
- 'page arguments' => array('$1', '$2', '$3'),
- 'title callback' => 'title_variable',
- 'title arguments' => 'page arguments',
- ),
- '^server/(\w+)/key/(.+)/delete$' => array(
- 'page class' => 'page_variable_delete_form',
- 'page arguments' => array('$1', '$2'),
- 'title callback' => 'title_variable',
- 'title arguments' => 'page arguments',
- ),
- '^server/(\w+)/key/(.+)/dump$' => array(
- 'page class' => 'page_variable_view_php',
- 'page arguments' => array('$1', '$2'),
- 'title callback' => 'title_variable',
- 'title arguments' => 'page arguments',
- ),
- '^server/(\w+)/key/(.+)/php$' => array(
- 'page class' => 'page_variable_view_php',
- 'page arguments' => array('$1', '$2'),
- 'title callback' => 'title_variable',
- 'title arguments' => 'page arguments',
- ),
- '^server/(\w+)/key/(.+)$' => array(
- 'page class' => 'page_variable_view_text',
- 'page arguments' => array('$1', '$2'),
- 'title callback' => 'title_variable',
- 'title arguments' => 'page arguments',
- ),
- '^slabs$' => array(
- 'page class' => 'Page_Memcache_Slab_Overview',
- 'title' => 'Slabs per server',
- ),
- '' => array( // Catch-all regex
- 'page class' => 'Page_Memcache_Main',
- 'title' => 'Overview',
- ),
- );
- return $ret;
- }
- function getRoute() {
- $found = FALSE;
- $path = $this->context->getPath();
- $matches = array();
- $infoDefaults = array(
- 'page arguments' => array(),
- );
- foreach ($this->getInfo() as $regex => $info) {
- $regex = "@$regex@";
- $count = preg_match($regex, $path, $matches);
- if ($count) {
- $found = TRUE;
- break;
- }
- }
- if ($found) {
- $this->context->setMessage("Menu hit on $regex", LOG_DEBUG);
- $info = array_merge($infoDefaults, $info);
- $this->context->setMessage("Info: ". print_r($info, TRUE), LOG_DEBUG);
- if (!empty($info['page arguments'])) {
- $regexes = array_fill(0, count($info['page arguments']), $regex);
- $paths = array_fill(0, count($info['page arguments']), $path);
- $info['page arguments'] = preg_replace($regexes, $info['page arguments'], $paths);
- }
- if ($info['title arguments'] == 'page arguments') {
- $info['title arguments'] = &$info['page arguments'];
- }
- $this->context->setMessage("Info resolved: ". print_r($info, TRUE), LOG_DEBUG);
- }
- else {
- $info = NULL;
- }
- return $info;
- }
- }
- function main() {
- try {
- ob_start();
- $context = new Context();
- $context->setMessage("Dirname: [". $context->getBase() . "]", LOG_DEBUG);
- $context->setMessage("Path: [". $context->getPath() . "]", LOG_DEBUG);
- $router = new Router($context);
- $item = $router->getRoute();
- $page = new $item['page class']($context, $item);
- $page->emitHeaders();
- echo $page;
- $html = ob_get_clean();
- if ($context->getTidy()) {
- applyTidy($html);
- }
- echo $html;
- }
- catch (Exception $e) {
- echo '<pre>';
- echo $e->getMessage() . PHP_EOL;
- echo $e->getTraceAsString();
- echo "</pre>";
- }
- }
- main();
|