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 = '
' . print_r($this, TRUE) . '
'; 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('
' . print_r($text, TRUE) . '
', $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 .= "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 Harun Yayli'), ))), '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 '
';
    echo $e->getMessage() . PHP_EOL;
    echo $e->getTraceAsString();
    echo "
"; } } main();