Quellcode durchsuchen

Converted to PSR-0 namespace and class conventions

- include the default PSR-0 autoloader
- split all classes to individual files
- use namespaces for all classes: Memcache_UI[\\(Core|Page)]
Frederic G. MARAND vor 12 Jahren
Ursprung
Commit
8127541541

+ 144 - 0
Memcache_UI/Core/Autoloader.inc

@@ -0,0 +1,144 @@
+<?php
+/**
+ * @file
+ * Reference PSR-0 autoloader
+ *
+ * @link
+ * https://gist.github.com/221634
+ */
+
+/**
+ * SplClassLoader implementation that implements the technical interoperability
+ * standards for PHP 5.3 namespaces and class names.
+ *
+ * http://groups.google.com/group/php-standards/web/final-proposal
+ *
+ *     // Example which loads classes for the Doctrine Common package in the
+ *     // Doctrine\Common namespace.
+ *     $classLoader = new SplClassLoader('Doctrine\Common', '/path/to/doctrine');
+ *     $classLoader->register();
+ *
+ * @author Jonathan H. Wage <jonwage@gmail.com>
+ * @author Roman S. Borschel <roman@code-factory.org>
+ * @author Matthew Weier O'Phinney <matthew@zend.com>
+ * @author Kris Wallsmith <kris.wallsmith@gmail.com>
+ * @author Fabien Potencier <fabien.potencier@symfony-project.org>
+ */
+class SplClassLoader
+{
+    private $_fileExtension = '.php';
+    private $_namespace;
+    private $_includePath;
+    private $_namespaceSeparator = '\\';
+
+    /**
+     * Creates a new <tt>SplClassLoader</tt> that loads classes of the
+     * specified namespace.
+     *
+     * @param string $ns The namespace to use.
+     */
+    public function __construct($ns = null, $includePath = null)
+    {
+        $this->_namespace = $ns;
+        $this->_includePath = $includePath;
+    }
+
+    /**
+     * Sets the namespace separator used by classes in the namespace of this class loader.
+     *
+     * @param string $sep The separator to use.
+     */
+    public function setNamespaceSeparator($sep)
+    {
+        $this->_namespaceSeparator = $sep;
+    }
+
+    /**
+     * Gets the namespace seperator used by classes in the namespace of this class loader.
+     *
+     * @return void
+     */
+    public function getNamespaceSeparator()
+    {
+        return $this->_namespaceSeparator;
+    }
+
+    /**
+     * Sets the base include path for all class files in the namespace of this class loader.
+     *
+     * @param string $includePath
+     */
+    public function setIncludePath($includePath)
+    {
+        $this->_includePath = $includePath;
+    }
+
+    /**
+     * Gets the base include path for all class files in the namespace of this class loader.
+     *
+     * @return string $includePath
+     */
+    public function getIncludePath()
+    {
+        return $this->_includePath;
+    }
+
+    /**
+     * Sets the file extension of class files in the namespace of this class loader.
+     *
+     * @param string $fileExtension
+     */
+    public function setFileExtension($fileExtension)
+    {
+        $this->_fileExtension = $fileExtension;
+    }
+
+    /**
+     * Gets the file extension of class files in the namespace of this class loader.
+     *
+     * @return string $fileExtension
+     */
+    public function getFileExtension()
+    {
+        return $this->_fileExtension;
+    }
+
+    /**
+     * Installs this class loader on the SPL autoload stack.
+     */
+    public function register()
+    {
+        spl_autoload_register(array($this, 'loadClass'));
+    }
+
+    /**
+     * Uninstalls this class loader from the SPL autoloader stack.
+     */
+    public function unregister()
+    {
+        spl_autoload_unregister(array($this, 'loadClass'));
+    }
+
+    /**
+     * Loads the given class or interface.
+     *
+     * @param string $className The name of the class to load.
+     * @return void
+     */
+    public function loadClass($className)
+    {
+        if (null === $this->_namespace || $this->_namespace.$this->_namespaceSeparator === substr($className, 0, strlen($this->_namespace.$this->_namespaceSeparator))) {
+            $fileName = '';
+            $namespace = '';
+            if (false !== ($lastNsPos = strripos($className, $this->_namespaceSeparator))) {
+                $namespace = substr($className, 0, $lastNsPos);
+                $className = substr($className, $lastNsPos + 1);
+                $fileName = str_replace($this->_namespaceSeparator, DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
+            }
+            $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . $this->_fileExtension;
+
+            $required = ($this->_includePath !== null ? $this->_includePath . DIRECTORY_SEPARATOR : '') . $fileName;
+            require $required;
+        }
+    }
+}

+ 209 - 0
Memcache_UI/Core/Context.inc

@@ -0,0 +1,209 @@
+<?php
+namespace Memcache_UI\Core {
+  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);
+        }
+      }
+    }
+  }
+}

+ 62 - 0
Memcache_UI/Core/Element.inc

@@ -0,0 +1,62 @@
+<?php
+namespace Memcache_UI\Core {
+
+  /**
+   * 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;
+    }
+  }
+}

+ 6 - 0
Memcache_UI/Core/GraphicsContext.inc

@@ -0,0 +1,6 @@
+<?php
+namespace Memcache_UI\Core {
+  class GraphicsContext {
+    protected $palette = array();
+  }
+}

+ 184 - 0
Memcache_UI/Core/Page.inc

@@ -0,0 +1,184 @@
+<?php
+namespace Memcache_UI\Core {
+  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
+      if (method_exists($this, 'build')) {
+        $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;
+    }
+
+  }
+}

+ 113 - 0
Memcache_UI/Core/Router.inc

@@ -0,0 +1,113 @@
+<?php
+namespace Memcache_UI\Core {
+  class Router {
+    function __construct(Context $context) {
+      $this->context = $context;
+    }
+
+    function getInfo() {
+      $ret = array(
+        '^server/(\w+)/flush/(\w+)$' => array(
+          'page class' => 'ServerFlush',
+          'page arguments' => array('$1', '$2'),
+          'title callback' => 'title_server',
+          'title arguments' => 'page arguments',
+        ),
+        '^server/(\w+)/flush$' => array(
+          'page class' => 'ServerFlush',
+          'page arguments' => array('$1'),
+          'title callback' => 'title_server',
+          'title arguments' => 'page arguments',
+        ),
+
+        '^server/(\w+)/slab/(\d+)$' => array(
+          'page class' => 'SlabView',
+          'page arguments' => array('$1', '$2'),
+          'title callback' => 'title_slab',
+          'title arguments' => 'page arguments',
+        ),
+
+        '^server/(\w+)/key/(.+)/delete/(\w+)$' => array(
+          'page class' => 'VariableDeleteConfirm',
+          'page arguments' => array('$1', '$2', '$3'),
+          'title callback' => 'title_variable',
+          'title arguments' => 'page arguments',
+        ),
+        '^server/(\w+)/key/(.+)/delete$' => array(
+          'page class' => 'VariableDeleteForm',
+          'page arguments' => array('$1', '$2'),
+          'title callback' => 'title_variable',
+          'title arguments' => 'page arguments',
+        ),
+
+        '^server/(\w+)/key/(.+)/dump$' => array(
+          'page class' => 'VariableViewPhp',
+          'page arguments' => array('$1', '$2'),
+          'title callback' => 'title_variable',
+          'title arguments' => 'page arguments',
+        ),
+        '^server/(\w+)/key/(.+)/php$' => array(
+          'page class' => 'VariableViewPhp',
+          'page arguments' => array('$1', '$2'),
+          'title callback' => 'title_variable',
+          'title arguments' => 'page arguments',
+        ),
+        '^server/(\w+)/key/(.+)$' => array(
+          'page class' => 'VariableViewText',
+          'page arguments' => array('$1', '$2'),
+          'title callback' => 'title_variable',
+          'title arguments' => 'page arguments',
+        ),
+
+        '^slabs$' => array(
+          'page class' => 'SlabOverview',
+          'title' => 'Slabs per server',
+        ),
+
+        '' => array( // Catch-all regex
+          'page class' => '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);
+        $info['page class'] = 'Memcache_UI\Page\\' . $info['page class'];
+        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 (isset($info['title arguments']) && $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;
+    }
+  }
+}

+ 26 - 0
Memcache_UI/Page/Base.inc

@@ -0,0 +1,26 @@
+<?php
+
+namespace Memcache_UI\Page {
+
+  use Memcache_UI\Core\Element;
+
+  /**
+   * Provide common layout elements to child classes.
+   *
+   * - header
+   */
+  class Base extends \Memcache_UI\Core\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'
+      );
+    }
+  }
+}

+ 12 - 0
Memcache_UI/Page/Main.inc

@@ -0,0 +1,12 @@
+<?php
+namespace Memcache_UI\Page {
+
+  use Memcache_UI\Core\Element;
+
+  class Main extends Base {
+    public function build() {
+      parent::build();
+      $this->setBody(new Element('p', NULL, 'Hello world'));
+    }
+  }
+}

+ 12 - 0
Memcache_UI/Page/ServerFlush.inc

@@ -0,0 +1,12 @@
+<?php
+namespace Memcache_UI\Page {
+
+  use Memcache_UI\Core\Element;
+
+  class ServerFlush extends Base {
+    public function build() {
+      parent::build();
+      $this->setBody(new Element('p', NULL, 'Flush server'));
+    }
+  }
+}

+ 12 - 0
Memcache_UI/Page/SlabOverview.inc

@@ -0,0 +1,12 @@
+<?php
+namespace Memcache_UI\Page {
+
+  use Memcache_UI\Core\Element;
+
+  class SlabOverview extends Base {
+    public function build() {
+      parent::build();
+      $this->setBody(new Element('p', NULL, 'Slab overview'));
+    }
+  }
+}

+ 12 - 0
Memcache_UI/Page/SlabView.inc

@@ -0,0 +1,12 @@
+<?php
+namespace Memcache_UI\Page {
+
+  use Memcache_UI\Core\Element;
+
+  class SlabView extends Base {
+    public function build() {
+      parent::build();
+      $this->setBody(new Element('p', NULL, 'Slab view'));
+    }
+  }
+}

+ 44 - 629
memcache_ui.php

@@ -1,651 +1,66 @@
 <?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;
+namespace Memcache_UI {
 
   /**
-   * User information: logged or not ?
+   * Wrapper around php tidy class.
    *
-   * @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
+   * @param string $html
    *
    * @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',
+  function applyTidy (&$html) {
+    $config = array(
+        'indent'          => TRUE,
+        'output-xhtml'    => TRUE,
+        'sort-attributes' => 'alpha',
+        'wrap'            => 200,
     );
-
-    return $ret;
+    $tidy = new tidy();
+    $tidy->parseString($html, $config, 'utf8');
+    $tidy->cleanRepair();
+    $html = (string) $tidy;
   }
 
-  public function initializeBody() {
-    foreach ($this->getRegions() as $region) {
-      $this->body[$region] = array();
-    }
-  }
+  function main() {
+    try {
+      ob_start();
+      //echo '<pre>';
 
-  public function initializeHead() {
-    $this->setHead(new Element('title', NULL, 'Memcache info'));
-  }
+      // Set-up autoloader: it cannot autoload itself.
+      $package = 'Memcache_UI';
+      require "$package/Core/Autoloader.inc";
+      $classLoader = new \SplClassLoader($package, dirname(__FILE__));
+      $classLoader->setFileExtension('.inc');
+      $classLoader->register();
 
-  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;
-  }
+      // Set up the context
+      $context = new Core\Context();
+      $context->setMessage("Dirname: [". $context->getBase() . "]", LOG_DEBUG);
+      $context->setMessage("Path: [". $context->getPath() . "]", LOG_DEBUG);
 
-  public function setHead($item) {
-    $this->head[] = $item;
-  }
-
-  public function setHeader($name, $value) {
-    $this->headers[$name] = $value;
-  }
+      // Obtain the routing information
+      $router = new Core\Router($context);
+      $item = $router->getRoute();
 
-}
+      $page = new $item['page class']($context, $item);
+      $page->emitHeaders();
+      echo $page;
 
-/**
- * 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'));
-  }
-}
+      $html = ob_get_clean();
 
-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;
+      // Filter it on output
+      if ($context->getTidy()) {
+        applyTidy($html);
       }
+      echo $html;
     }
-    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);
+    catch (Exception $e) {
+      echo '<pre>';
+      echo $e->getMessage() . PHP_EOL;
+      echo $e->getTraceAsString();
+      echo "</pre>";
     }
-    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();
 }
-
-main();