static generator
authorMichael Wallner <mike@php.net>
Fri, 13 Feb 2015 17:29:13 +0000 (18:29 +0100)
committerMichael Wallner <mike@php.net>
Fri, 13 Feb 2015 17:29:13 +0000 (18:29 +0100)
bin/cli-server
bin/gen-static [new file with mode: 0755]
mdref/Action.php
mdref/Generator.php [new file with mode: 0644]
mdref/Reference.php
public/.htaccess
public/index.css
public/index.js
views/layout.phtml
views/sidebar.phtml

index 05d700b8137ec7e1a7b67c177712c39494677787..30373f82904fd6e4ad2be357f003bdda8beba0d2 100755 (executable)
@@ -46,5 +46,5 @@ fi
 export REFPATH
 
 for p in {8000..8100}; do
-       php -S localhost:$p -t . index.php && break
+       ${PHP:-php} -S localhost:$p -t . index.php && break
 done
diff --git a/bin/gen-static b/bin/gen-static
new file mode 100755 (executable)
index 0000000..15e8609
--- /dev/null
@@ -0,0 +1,14 @@
+#!/usr/bin/env php
+<?php
+
+define("ROOT", dirname(__DIR__));
+define("REFS", getenv("REFPATH") ?: implode(PATH_SEPARATOR, glob(ROOT."/refs/*")));
+
+#ini_set("open_basedir", ROOT.":".REFS);
+
+$loader = require ROOT . "/vendor/autoload.php";
+/* @var $loader \Composer\Autoload\ClassLoader */
+$loader->add("mdref", ROOT);
+
+$g = new mdref\Generator(REFS, @$argv[1]);
+$g->run();
index dcd8fcbed025d24f4725c9b9c0976a2c19d8e2ba..3e318be34d4d45af301d5da2309fa35adcea7d90 100644 (file)
@@ -36,20 +36,8 @@ class Action extends Observer {
                $pld = new \stdClass;
                
                try {
-                       $pld->quick = function($string) {
-                               $md = \MarkdownDocument::createFromString($string);
-                               $md->compile(\MarkdownDocument::AUTOLINK);
-                               return $md->getHtml();
-                       };
-                       
-                       $pld->file = function($file) {
-                               $fd = fopen($file, "r");
-                               $md = \MarkdownDocument::createFromStream($fd);
-                               $md->compile(\MarkdownDocument::AUTOLINK | \MarkdownDocument::TOC);
-                               $html = $md->getHtml();
-                               fclose($fd);
-                               return $html;
-                       };
+                       $pld->quick = [$this->reference, "formatString"];
+                       $pld->file = [$this->reference, "formatFile"];
                        
                        $pld->ref = implode("/",  $this->baseUrl->params(
                                $this->baseUrl->mod($ctl->getRequest()->getRequestUrl())));
diff --git a/mdref/Generator.php b/mdref/Generator.php
new file mode 100644 (file)
index 0000000..7a58387
--- /dev/null
@@ -0,0 +1,180 @@
+<?php
+
+namespace mdref;
+
+/**
+ * Static mdref generator
+ */
+class Generator
+{
+       /**
+        * @var \mdref\Reference
+        */
+       private $reference;
+       
+       /**
+        * @var \mdref\Generator\Renderer
+        */
+       private $renderer;
+       
+       /**
+        * Create a new generator
+        * @param string $refs list of reference paths
+        * @param string $dir output directory
+        */
+       public function __construct($refs, $dir = null) {
+               $this->reference = new Reference(explode(PATH_SEPARATOR, $refs));
+               $this->renderer = new Generator\Renderer($dir ?: "public/static");
+       }
+       
+       /**
+        * Run the generator
+        */
+       public function run() {
+               $this->generateRoot();
+               foreach ($this->reference as $repo) {
+                       $iter = new \RecursiveIteratorIterator($repo, 
+                               \RecursiveIteratorIterator::SELF_FIRST);
+                       foreach ($iter as $ref) {
+                               $this->generateEntry($ref);
+                       }
+               }
+       }
+       
+       /**
+        * Generate index.html and LICENSE.html
+        */
+       private function generateRoot() {
+               printf("Generating index ...\n");
+               $data = $this->createPayload(null);
+               $data->ref = "index";
+               $this->renderer->persist($data);
+
+               printf("Generating LICENSE ...\n");
+               $data->text = file_get_contents(__DIR__."/../LICENSE");
+               $data->ref = "LICENSE";
+               $this->renderer->persist($data);
+       }
+       
+       /**
+        * Generate HTML for an entry
+        * @param \mdref\Entry $ref
+        */
+       private function generateEntry(Entry $ref) {
+               printf("Generating %s ...\n", $ref->getName());
+               $data = $this->createPayload($ref);
+               $this->renderer->persist($data);
+       }
+       
+       /**
+        * Create the view payload
+        * @param \mdref\Entry $ref
+        * @param \mdref\Generator\Renderer $view
+        * @return \stdClass
+        */
+       private function createPayload(Entry $ref = null) {
+               $pld = new \stdClass;
+               
+               $pld->quick = [$this->reference, "formatString"];
+               $pld->file = [$this->reference, "formatFile"];
+               $pld->refs = $this->reference;
+               $pld->view = $this->renderer;
+               if ($ref) {
+                       $pld->entry = $ref;
+                       $pld->ref = $ref->getName();
+               }
+               
+               return $pld;
+       }
+}
+
+namespace mdref\Generator;
+
+class Renderer
+{
+       /**
+        * @var string
+        */
+       private $dir;
+
+       /**
+        * @param string $dir output directory
+        */
+       public function __construct($dir = "public/static") {
+               $this->dir = $dir;
+       }
+       
+       /**
+        * HTML entity encode special characters
+        * @param string $string
+        * @return string
+        */
+       public function esc($string) {
+               return htmlspecialchars($string);
+       }
+       
+       /**
+        * Render mdref page
+        * @param \stdClass $pld
+        * @return string
+        */
+       public function render(\stdClass $pld) {
+               $content = "";
+               ob_start(function($data) use(&$content) {
+                       $content .= $data;
+                       return true;
+               });
+               static::renderFile("views/layout.phtml", (array) $pld);
+               ob_end_flush();
+               return $content;
+       }
+
+       /**
+        * Persist mdref page to output directory
+        * @param \stdClass $data
+        */
+       public function persist(\stdClass $data) {
+               $html = $this->render($data);
+               $file = sprintf("%s/%s.html", $this->dir, $data->ref);
+               $this->saveFile($file, $html);
+               $this->linkIndex(dirname($file));
+       }
+       
+       /**
+        * Save data to file (write to $file.tmp and rename to $file)
+        * @param string $file
+        * @param string $data
+        * @throws \Exception
+        */
+       private function saveFile($file, $data) {
+               $dir = dirname($file);
+               if (!is_dir($dir) && !mkdir($dir, 0755, true)) {
+                       throw new \Exception("Failed to create directory '$dir'");
+               }
+               if (!file_put_contents("$file.tmp", $data)) {
+                       throw new \Exception("Failed to save file '$file.tmp'");
+               }
+               if (!rename("$file.tmp", $file)) {
+                       throw new \Exception("Failed to rename to '$file'");
+               }
+       }
+       
+       private function linkIndex($dir) {
+               $index = "$dir.html";
+               $link = "$dir/index.html";
+               if (is_file($index) && !is_file($link)) {
+                       printf("Generating index for '%s'\n", substr($dir, strlen($this->dir)));
+                       link($index, $link);
+               }
+       }
+       
+       /**
+        * Render file
+        */
+       static private function renderFile() {
+               if (func_num_args() > 1) {
+                       extract(func_get_arg(1));
+               }
+               include func_get_arg(0);
+       }
+}
index 153004fdafc83dd75ea4e072fcad0c1afcc298a8..7c22dba37fee16e9b9b4cfd7b90746b892ced4fb 100644 (file)
@@ -43,5 +43,19 @@ class Reference implements \IteratorAggregate {
        public function getIterator() {
                return new \ArrayIterator($this->repos);
        }
+
+       public function formatString($string) {
+               $md = \MarkdownDocument::createFromString($string);
+               $md->compile(\MarkdownDocument::AUTOLINK);
+               return $md->getHtml();
+       }
        
+       public function formatFile($file) {
+               $fd = fopen($file, "r");
+               $md = \MarkdownDocument::createFromStream($fd);
+               $md->compile(\MarkdownDocument::AUTOLINK | \MarkdownDocument::TOC);
+               $html = $md->getHtml();
+               fclose($fd);
+               return $html;
+       }
 }
index affb2a0b0655eb79b86dd166ca4b48988acd201c..c236b7325400f01293b541ef8235d1d90e5a2ccc 100644 (file)
@@ -1,4 +1,7 @@
+DirectorySlash Off
 RewriteEngine On
+RewriteRule ^static.*\.html - [L]
+RewriteRule ^(static/.+) $1.html [L]
 RewriteCond %{REQUEST_FILENAME} -f [OR]
 RewriteCond %{REQUEST_FILENAME} -d [OR]
 RewriteCond %{REQUEST_FILENAME} -l
index 40f17d2524b9ad7a75bb9a08f41307bb8f463f82..b4175ed9b6abd5a69c20fe26cd44ea5799ff9881 100644 (file)
@@ -79,7 +79,7 @@ pre>code, pre>code code {
        color: #eee;
 }
 
-p, pre, table {
+p, pre, table, dl {
        margin: 1em 2em;
 }
 
@@ -216,7 +216,14 @@ table {
 th, td {
        padding: .4em;
 }
-
+dt {
+       font-weight: bold;
+       margin-top: 1em;
+}
+dd {
+       line-height: 1.2em;
+       margin-left: 1em;
+}
 h1, footer, table, .sidebar, pre>code, li h3 {
        box-shadow: 0 0 4px #708090;
 }
index f60e8a82201ca222e90e0f3b5882069860e76ec6..27e368a93ee728e229609384620d310d14213435 100644 (file)
@@ -97,8 +97,12 @@ $(function() {
                                        return "<a href=\"" + s.replace(/::|\\/g, "/") + "\">";
                                }
                        }
-                       if (-1 !== (j = s.indexOf("\\")) && s.substr(j+1,1) !== "n") {
-                               return "<a href=\"" + s.replace(/\\/g, "/").replace(/::|$/, "#") + "\">";
+                       if (-1 !== (j = s.lastIndexOf("\\")) && s.substr(j+1,1) !== "n") {
+                               t = s.substring(j+1);
+                               if (!mdref.is_constant(t)) {
+                                       return "<a href=\"" + s.replace(/\\/g, "/").replace(/::|$/, "#") + "\">";
+                               }
+                               return "<a href=\"" + s.substring(0,j).replace(/\\/g, "/") + "#" + t + "\">";
                        }
 
                        switch (s.toLowerCase()) {
index 69b4c74c26d0a85e8cecd9e23cb68de9dca79808..05367b98d1f65fb6120d88d0bb5aeaee0a89085f 100644 (file)
                        <?php endif; ?>
                        mdref
                </title>
-               <base href="<?= $baseUrl ?>">
-               <meta http-equiv="Content-Location" content="<?= $baseUrl . $ref ?>">
-               <link rel="stylesheet" href="index.css">
+               <?php if (isset($baseUrl)) : ?>
+                       <base href="<?= $baseUrl ?>">
+                       <meta http-equiv="Content-Location" content="<?= $baseUrl . $ref ?>">
+                       <link rel="stylesheet" href="index.css">
+               <?php else: ?>
+                       <style type="text/css">
+                               <?= file_get_contents(__DIR__."/../public/index.css"); ?>
+                       </style>
+               <?php endif; ?>
+               
                <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
        </head>
        <body>
@@ -25,7 +32,7 @@
                        <?php include __DIR__."/index.phtml" ?>
                <?php endif; ?>
 
-               <?php if ($_SERVER["SERVER_NAME"] != "localhost") : ?>
+               <?php if (@$_SERVER["SERVER_NAME"] != "localhost") : ?>
                <div id="disqus_thread"></div>
                <script>
                        var disqus_shortname = 'mdref';
                <footer>
                        <?php include __DIR__."/footer.phtml" ?>
                </footer>
-               <script src="index.js"></script>
+               <?php if (isset($baseUrl)) : ?>
+                       <script src="index.js"></script>
+               <?php else : ?>
+                       <script type="application/javascript">
+                               <?= file_get_contents(__DIR__."/../public/index.js"); ?>
+                       </script>
+               <?php endif; ?>
        </body>
 </html>
index e371d6d6ea5df5fa0b0400ead6691571e7eeb327..4ecce30d2af920594baec58a5a66197fc6189bee 100644 (file)
@@ -1,23 +1,30 @@
 <div class="sidebar">
+       <?php 
+               if (isset($baseUrl) || !isset($entry)) {
+                       $up = "";
+               } else {
+                       $up = str_repeat("../", count($entry->getParents()));
+               }
+       ?>
        <ul>
-               <li>&lsh; <a href="">Home</a>
+               <li>&lsh; <a href="<?= $up ?>">Home</a>
                        <?php if (isset($entry)) : /* @var \mdref\Entry $entry */ ?>
                        <ul>
                                <li>
                                        <?php foreach ($entry->getParents() as $parent) if ($parent->isFile()) : ?>
                                        &uarr; 
-                                               <a href="<?= $view->esc($parent->getName()) ?>">
+                                               <a href="<?= $up.$view->esc($parent->getName()) ?>">
                                                        <?= $view->esc($entry->getRepo()->getEntry($parent)) ?>
                                                </a>
                                                <ul>
                                                        <li>
                                        <?php endif; ?>
-                                                       &circlearrowright; <a href="<?= $view->esc($entry->getName()) ?>"
+                                                       &circlearrowright; <a href="<?= $up.$view->esc($entry->getName()) ?>"
                                                        ><?= $view->esc($entry) ?></a>
                                                        <ul>
                                                                <?php foreach ($entry as $sub) : /* @var \mdref\Entry $sub */ ?>
                                                                <li>
-                                                                       &rdsh; <a href="<?= $view->esc($sub->getName()) ?>"
+                                                                       &rdsh; <a href="<?= $up.$view->esc($sub->getName()) ?>"
                                                                        ><?= $view->esc($sub) ?></a>
                                                                </li>
                                                                <?php endforeach; ?>
@@ -44,7 +51,7 @@
        </ul>
        <?php if (isset($entry)) : ?>
        <div class="edit">
-               <a href="<?=$entry->getEditUrl()?>">Edit</a>
+               <a href="<?= $entry->getEditUrl() ?>">Edit</a>
        </div>
        <?php endif; ?>
 </div>