memcache_ui.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. <?php
  2. /**
  3. * Wrapper around php tidy class.
  4. *
  5. * @param string $html
  6. *
  7. * @return void
  8. */
  9. function applyTidy (&$html) {
  10. $config = array(
  11. 'indent' => TRUE,
  12. 'output-xhtml' => TRUE,
  13. 'sort-attributes' => 'alpha',
  14. 'wrap' => 80,
  15. );
  16. $tidy = new tidy();
  17. $tidy->parseString($html, $config, 'utf8');
  18. $tidy->cleanRepair();
  19. $html = (string) $tidy;
  20. }
  21. class Context {
  22. protected $logLevelClasses = NULL;
  23. /**
  24. * Directory in which the current script is located.
  25. *
  26. * Follow symlinks if applicable, to obtain the actual implementation
  27. * directory, instead of just the main file symlink, in order to find the
  28. * other files.
  29. *
  30. * @var string
  31. */
  32. protected $dirname;
  33. /**
  34. * Graphics context
  35. *
  36. * @var GraphicsContext
  37. */
  38. protected $gc = NULL;
  39. /**
  40. * Logging level, as per RFC5424
  41. *
  42. * @link http://php.net/network.constants.php
  43. *
  44. * @var integer
  45. */
  46. protected $logLevel = NULL;
  47. /**
  48. * Messages for display.
  49. *
  50. * @var array
  51. */
  52. protected $messages = array();
  53. /**
  54. * Requested path: <$PHP_SELF>?q=a/b/c
  55. *
  56. * @var string
  57. */
  58. protected $path = NULL;
  59. /**
  60. * Tidy the output ?
  61. *
  62. * @var boolean
  63. */
  64. protected $tidy = NULL;
  65. /**
  66. * User information: logged or not ?
  67. *
  68. * @var boolean
  69. */
  70. protected $user = FALSE;
  71. function __destruct() {
  72. if (!empty($this->messages)) {
  73. $ret = (string) new Element('pre', array('class' => array('messages')),
  74. implode("\n", $this->getMessage(TRUE)));
  75. echo $ret;
  76. }
  77. }
  78. function __toString() {
  79. $ret = '<pre>' . print_r($this, TRUE) . '</pre>';
  80. return $ret;
  81. }
  82. public function getDirname() {
  83. if (!isset($this->dirname)) {
  84. $this->dirname = dirname(__FILE__);
  85. }
  86. return $this->dirname;
  87. }
  88. public function getLogLevel() {
  89. if (!isset($this->logLevel)) {
  90. $usLogLevel = NULL;
  91. foreach ($_GET as $key => $value) {
  92. if (strtolower($key) === 'loglevel') {
  93. $usLogLevel = (int) $value;
  94. break;
  95. }
  96. }
  97. if (!isset($usLogLevel)) {
  98. $usLogLevel = LOG_NOTICE;
  99. }
  100. if ($usLogLevel < LOG_EMERG) {
  101. $this->logLevel = LOG_EMERG;
  102. }
  103. elseif ($usLogLevel > LOG_DEBUG) {
  104. $this->logLevel = LOG_DEBUG;
  105. }
  106. else {
  107. $this->logLevel = $usLogLevel; // We now know it to be safe
  108. }
  109. }
  110. return $this->logLevel;
  111. }
  112. public function getLogLevelClass($logLevel) {
  113. if (!isset($this->logLevelClasses)) {
  114. $this->logLevelClasses = array(
  115. LOG_EMERG => 'error',
  116. LOG_ALERT => 'error',
  117. LOG_CRIT => 'error',
  118. LOG_ERR => 'error',
  119. LOG_WARNING => 'warning',
  120. LOG_NOTICE => 'warning',
  121. LOG_INFO => 'status',
  122. LOG_DEBUG => 'status',
  123. );
  124. }
  125. if ($logLevel < LOG_EMERG) {
  126. $logLevel = LOG_EMERG;
  127. }
  128. elseif ($logLevel > LOG_DEBUG) {
  129. $logLevel = LOG_DEBUG;
  130. }
  131. return $this->logLevelClasses[$logLevel];
  132. }
  133. public function getMessage($clear = FALSE) {
  134. $ret = $this->messages;
  135. if ($clear) {
  136. $this->messages = array();
  137. }
  138. return $ret;
  139. }
  140. /**
  141. * Return the requested path.
  142. *
  143. * @param string $path
  144. */
  145. public function getPath() {
  146. if (!isset($this->path)) {
  147. $this->path = empty($_GET['q']) ? '' : $_GET['q'];
  148. }
  149. return $this->path;
  150. }
  151. public function getTidy() {
  152. if (!isset($this->tidy)) {
  153. $this->tidy = TRUE;
  154. foreach ($_GET as $key => $value) {
  155. if (strtolower($key) === 'tidy') {
  156. $this->tidy = !!$value;
  157. break;
  158. }
  159. }
  160. }
  161. return $this->tidy;
  162. }
  163. /**
  164. * Add a message to the messages list if it is above the current logging level.
  165. *
  166. * @param string $text
  167. * @param integer $logLevel
  168. *
  169. * @return void
  170. */
  171. public function setMessage($text, $logLevel = LOG_NOTICE) {
  172. if ($logLevel <= $this->getlogLevel()) {
  173. if (is_array($text) || (is_object($text) && !method_exists($text, '__toString'))) {
  174. $this->messages[] = array(print_r($text, TRUE), $logLevel);
  175. }
  176. else {
  177. $this->messages[] = array((string) $text, $logLevel);
  178. }
  179. }
  180. }
  181. }
  182. /**
  183. * A wrapper for XML elements.
  184. */
  185. class Element {
  186. public $attributes = array();
  187. public $name = NULL;
  188. public $new_line; // Add a new line after element
  189. public $value = NULL;
  190. public function __construct($name, $attr = NULL, $value = NULL) {
  191. $this->name = $name;
  192. $this->attributes = $attr;
  193. $this->value = $value;
  194. }
  195. /**
  196. * @link drupal7/includes/common.inc#check_plain()
  197. */
  198. public static function check_plain($text) {
  199. return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
  200. }
  201. /**
  202. * @link drupal7/includes/common.inc#drupal_attributes()
  203. */
  204. public static function getSerializedAttributes(array $attributes = array()) {
  205. foreach ($attributes as $attribute => &$data) {
  206. $data = implode(' ', (array) $data);
  207. $data = $attribute . '="' . self::check_plain($data) . '"';
  208. }
  209. return $attributes ? ' ' . implode(' ', $attributes) : '';
  210. }
  211. public function __toString() {
  212. $ret = '<'. $this->name;
  213. if (!empty($this->attributes) && is_array($this->attributes)) {
  214. $ret .= self::getSerializedAttributes($this->attributes);
  215. }
  216. if (empty($this->value)) {
  217. $ret .= ' />';
  218. }
  219. else {
  220. $ret .= '>';
  221. if ($this->value instanceof Element) {
  222. $ret .= (string) $this->value; // force __toString()
  223. }
  224. elseif (is_array($this->value)) {
  225. $ret .= implode('', $this->value);
  226. }
  227. else {
  228. $ret .= $this->value;
  229. }
  230. $ret .= "</$this->name>";
  231. }
  232. return $ret;
  233. }
  234. }
  235. class GraphicsContext {
  236. protected $palette = array();
  237. }
  238. class Page extends Element {
  239. /**
  240. * The HTML body element of the page.
  241. *
  242. * @var array
  243. */
  244. protected $body;
  245. /**
  246. * @var Context
  247. */
  248. protected $context;
  249. /**
  250. * Ths HTML head element of the page.
  251. *
  252. * @var array
  253. */
  254. protected $head;
  255. /**
  256. * @var array
  257. */
  258. protected $finalized = array();
  259. /**
  260. * @var array
  261. */
  262. protected $styles;
  263. /**
  264. * Page constructor.
  265. *
  266. * @param Context $context
  267. * @param array $item
  268. * A router info array.
  269. *
  270. * @see Router::getInfo()
  271. */
  272. public function __construct(Context $context, array $item) {
  273. parent::__construct('html');
  274. $this->context = $context;
  275. }
  276. public function finalizeBody() {
  277. if (isset($this->finalized['body'])) {
  278. throw new Exception('Attempt to finalize already finalized body');
  279. }
  280. if ($message = $this->context->getMessage(TRUE)) {
  281. $items = array();
  282. foreach ($message as $row) {
  283. $items[] = new Element('li', array(
  284. 'class' => array($this->context->getLogLevelClass($row[1])),
  285. ), $row[0]);
  286. }
  287. $this->setBody(new Element('div', array('class' => array('messages')), $items));
  288. }
  289. $this->finalized['body'] = TRUE;
  290. }
  291. public function finalizeHead() {
  292. if (isset($this->finalized['head'])) {
  293. throw new Exception('Attempt to finalize already finalized head');
  294. }
  295. $cssLink = new Element('link', array(
  296. 'rel' => 'stylesheet',
  297. 'type' => 'text/css',
  298. 'href' => $this->context->getPath() .'/memcache_ui.css',
  299. ));
  300. $this->setHead($cssLink);
  301. $this->finalized['head'] = TRUE;
  302. }
  303. public function getBody() {
  304. $this->finalizeBody();
  305. $body = new Element('body', NULL, $this->body);
  306. $ret = (string) $body;
  307. return $ret;
  308. }
  309. public function getHead() {
  310. $this->finalizeHead();
  311. $head = new Element('head', NULL, $this->head);
  312. $ret = (string) $head;
  313. return $ret;
  314. }
  315. public function getHeader($name) {
  316. }
  317. public function render() {
  318. $html = new Element('html', NULL, $this->getHead() . $this->getBody());
  319. return (string) $html;
  320. }
  321. public function setBody($fragment) {
  322. $this->body[] = $fragment;
  323. }
  324. public function setHead($item) {
  325. $this->head[] = $item;
  326. }
  327. public function setHeader($name, $value) {
  328. }
  329. }
  330. class Page_Main extends Page {
  331. function finalizeBody() {
  332. $hello = new Element('p', NULL, 'Hello world');
  333. $this->setBody($hello);
  334. parent::finalizeBody();
  335. }
  336. }
  337. class Router {
  338. function __construct(Context $context) {
  339. $this->context = $context;
  340. }
  341. function getInfo() {
  342. $ret = array(
  343. '^server/(\w+)/flush/(\w+)$' => array(
  344. 'page class' => 'page_server_flush',
  345. 'page arguments' => array('$1', '$2'),
  346. 'title callback' => 'title_server',
  347. 'title arguments' => 'page arguments',
  348. ),
  349. '^server/(\w+)/flush$' => array(
  350. 'page class' => 'page_server_flush',
  351. 'page arguments' => array('$1'),
  352. 'title callback' => 'title_server',
  353. 'title arguments' => 'page arguments',
  354. ),
  355. '^server/(\w+)/slab/(\d+)$' => array(
  356. 'page class' => 'page_slab_view',
  357. 'page arguments' => array('$1', '$2'),
  358. 'title callback' => 'title_slab',
  359. 'title arguments' => 'page arguments',
  360. ),
  361. '^server/(\w+)/key/(.+)/delete/(\w+)$' => array(
  362. 'page class' => 'page_variable_delete_confirm',
  363. 'page arguments' => array('$1', '$2', '$3'),
  364. 'title callback' => 'title_variable',
  365. 'title arguments' => 'page arguments',
  366. ),
  367. '^server/(\w+)/key/(.+)/delete$' => array(
  368. 'page class' => 'page_variable_delete_form',
  369. 'page arguments' => array('$1', '$2'),
  370. 'title callback' => 'title_variable',
  371. 'title arguments' => 'page arguments',
  372. ),
  373. '^server/(\w+)/key/(.+)/dump$' => array(
  374. 'page class' => 'page_variable_view_php',
  375. 'page arguments' => array('$1', '$2'),
  376. 'title callback' => 'title_variable',
  377. 'title arguments' => 'page arguments',
  378. ),
  379. '^server/(\w+)/key/(.+)/php$' => array(
  380. 'page class' => 'page_variable_view_php',
  381. 'page arguments' => array('$1', '$2'),
  382. 'title callback' => 'title_variable',
  383. 'title arguments' => 'page arguments',
  384. ),
  385. '^server/(\w+)/key/(.+)$' => array(
  386. 'page class' => 'page_variable_view_text',
  387. 'page arguments' => array('$1', '$2'),
  388. 'title callback' => 'title_variable',
  389. 'title arguments' => 'page arguments',
  390. ),
  391. '^slabs$' => array(
  392. 'page class' => 'page_slab_overview',
  393. 'title' => 'Slabs per server',
  394. ),
  395. '^$' => array(
  396. 'page class' => 'Page_Main',
  397. 'title' => 'Overview',
  398. ),
  399. );
  400. return $ret;
  401. }
  402. function getRoute() {
  403. $found = FALSE;
  404. $path = $this->context->getPath();
  405. $matches = array();
  406. $infoDefaults = array(
  407. 'page arguments' => array(),
  408. );
  409. foreach ($this->getInfo() as $regex => $info) {
  410. $regex = "@$regex@";
  411. $count = preg_match($regex, $path, $matches);
  412. if ($count) {
  413. $found = TRUE;
  414. break;
  415. }
  416. }
  417. if ($found) {
  418. $this->context->setMessage("Found at $regex", LOG_DEBUG);
  419. $this->context->setMessage("Info: ". print_r($info, TRUE), LOG_DEBUG);
  420. $info = array_merge($infoDefaults, $info);
  421. if (!empty($info['page arguments'])) {
  422. $regexes = array_fill(0, count($info['page arguments']), $regex);
  423. $paths = array_fill(0, count($info['page arguments']), $path);
  424. $info['page arguments'] = preg_replace($regexes, $info['page arguments'], $paths);
  425. }
  426. }
  427. else {
  428. $info = NULL;
  429. }
  430. return $info;
  431. }
  432. }
  433. function main() {
  434. try {
  435. ob_start();
  436. $context = new Context();
  437. $context->setMessage("Dirname: [". $context->getDirname() . "]", LOG_DEBUG);
  438. $context->setMessage("Path: [". $context->getPath() . "]", LOG_DEBUG);
  439. $router = new Router($context);
  440. $item = $router->getRoute();
  441. $page = new $item['page class']($context, $item);
  442. // echo '<pre>'; var_dump($page);
  443. echo $page->render();
  444. $html = ob_get_clean();
  445. if ($context->getTidy()) {
  446. applyTidy($html);
  447. }
  448. echo $html;
  449. }
  450. catch (Exception $e) {
  451. echo '<pre>';
  452. echo $e->getMessage() . PHP_EOL;
  453. echo $e->getTraceAsString();
  454. echo "</pre>";
  455. }
  456. }
  457. main();