running on php-7.3 now
authorMichael Wallner <mike@php.net>
Tue, 12 Mar 2019 16:54:33 +0000 (17:54 +0100)
committerMichael Wallner <mike@php.net>
Tue, 12 Mar 2019 16:54:33 +0000 (17:54 +0100)
composer.json
mdref/Action.php
mdref/BaseUrl.php
mdref/Entry.php
mdref/Exception.php
mdref/ExceptionHandler.php
mdref/File.php
mdref/Reference.php
mdref/Repo.php
mdref/Tree.php
public/index.php

index 48578c2e8e424c3328758a57640ccc2d25dc2b71..b017f4147bf53f226e089d0badfc202418b92b75 100644 (file)
@@ -8,10 +8,21 @@
             "email": "mike@php.net"
         }
     ],
-
+    "bin": ["bin/ref2stub"],
     "autoload": {
         "psr-4": {
             "mdref\\": "mdref/"
         }
+    },
+    "require": {
+        "php": "^7.3",
+        "ext-ctype": "^7.3",
+        "ext-http": "^3.2",
+        "ext-filter": "^7.3",
+        "ext-pcre": "^7.3"
+    },
+    "suggests": {
+        "ext-discount": "A Markdown renderer is needed, this is the preferred one.",
+        "ext-cmark": "A Markdown renderer is needed, this is the fallback one."
     }
 }
index deb62e5a64c1260d59607950e29d0cefc8867ba6..7e1307bffff70e85412e45a221a1589777cffa35 100644 (file)
@@ -5,6 +5,11 @@ namespace mdref;
 use http\Env\Request;
 use http\Env\Response;
 use http\Message\Body;
+use stdClass;
+use function file_get_contents;
+use function htmlspecialchars;
+use function ob_start;
+use const ROOT;
 
 /**
  * Request handler
@@ -17,12 +22,12 @@ class Action {
        private $reference;
 
        /**
-        * @var \http\Request
+        * @var \http\Env\Request
         */
        private $request;
 
        /**
-        * @var \http\Response
+        * @var \http\Env\Response
         */
        private $response;
 
@@ -42,17 +47,21 @@ class Action {
                ob_start($res);
        }
 
-       function esc($txt) {
+       /**
+        * Shorthand for \htmlspecialchars()
+        * @param $txt string
+        * @return string
+        */
+       function esc(string $txt) : string {
                return htmlspecialchars($txt);
        }
 
        /**
         * Create the view payload
-        * @param \http\Controller $ctl
         * @return \stdClass
         */
-       private function createPayload() {
-               $pld = new \stdClass;
+       private function createPayload() : object {
+               $pld = new stdClass;
 
                $pld->esc = "htmlspecialchars";
                $pld->anchor = [$this->reference, "formatAnchor"];
@@ -69,10 +78,10 @@ class Action {
        }
 
        /**
-        * Redirect to canononical url
+        * Redirect to canonical url
         * @param string $cnn
         */
-       private function serveCanonical($cnn) {
+       private function serveCanonical(string $cnn) : void {
                $this->response->setHeader("Location", $this->baseUrl->mod(["path" => $cnn]));
                $this->response->setResponseCode(301);
                $this->response->send();
@@ -81,25 +90,27 @@ class Action {
        /**
         * Serve index.css
         */
-       private function serveStylesheet() {
+       private function serveStylesheet() : void {
                $this->response->setHeader("Content-Type", "text/css");
-               $this->response->setBody(new \http\Message\Body(fopen(ROOT."/public/index.css", "r")));
+               $this->response->setBody(new Body(\fopen(ROOT."/public/index.css", "r")));
                $this->response->send();
        }
 
        /**
         * Serve index.js
         */
-       private function serveJavascript() {
+       private function serveJavascript() : void {
                $this->response->setHeader("Content-Type", "application/javascript");
-               $this->response->setBody(new \http\Message\Body(fopen(ROOT."/public/index.js", "r")));
+               $this->response->setBody(new Body(\fopen(ROOT."/public/index.js", "r")));
                $this->response->send();
        }
 
        /**
         * Server a PHP stub
+        * @throws Exception
+        *
         */
-       private function serveStub() {
+       private function serveStub() : void {
                $name = $this->request->getQuery("ref", "s");
                $repo = $this->reference->getRepoForEntry($name);
                if (!$repo->hasStub($stub)) {
@@ -107,17 +118,17 @@ class Action {
                }
                $this->response->setHeader("Content-Type", "application/x-php");
                $this->response->setContentDisposition(["attachment" => ["filename" => "$name.stub.php"]]);
-               $this->response->setBody(new Body(fopen($stub, "r")));
+               $this->response->setBody(new Body(\fopen($stub, "r")));
                $this->response->send();
        }
 
        /**
         * Serve a preset
-        * @param \stdClass $pld
+        * @param object $pld
         * @return true to continue serving the payload
         * @throws Exception
         */
-       private function servePreset($pld) {
+       private function servePreset(object $pld) : bool {
                switch ($pld->ref) {
                case "AUTHORS":
                case "LICENSE":
@@ -139,15 +150,22 @@ class Action {
                return false;
        }
 
-       private function serve() {
+       /**
+        * Serve a payload
+        */
+       private function serve() : void {
                extract((array) func_get_arg(0));
                include ROOT."/views/layout.phtml";
+               $this->response->addHeader("Link", "<" . $this->baseUrl->path . "index.css>; rel=preload; as=style");
+               $this->response->addHeader("Link", "<" . $this->baseUrl->path . "index.js>; rel=preload; as=script");
                $this->response->send();
        }
 
-       public function handle() {
+       /**
+        * Request handler
+        */
+       public function handle() : void {
                try {
-
                        $pld = $this->createPayload();
 
                        if (strlen($pld->ref)) {
@@ -155,7 +173,8 @@ class Action {
                                if (($repo = $this->reference->getRepoForEntry($pld->ref, $cnn))) {
                                        if (strlen($cnn)) {
                                                /* redirect */
-                                               return $this->serveCanonical($cnn);
+                                               $this->serveCanonical($cnn);
+                                               return;
                                        } else {
                                                /* direct match */
                                                $pld->entry = $repo->getEntry($pld->ref);
index 7476a82c7079d78c0f95868a32ec8aeffdf9f704..2d1503dd7680ecfb9569d7b81e7c7a58c189abdb 100644 (file)
@@ -3,32 +3,44 @@
 namespace mdref;
 
 use http\Url;
+use function dirname;
+use function filter_input;
+use function strlen;
+use function substr;
+use function urldecode;
+use const FILTER_VALIDATE_BOOLEAN;
+use const INPUT_SERVER;
 
-class BaseUrl extends Url {
+class BaseUrl extends Url
+{
        /**
         * Create base URL
+        *
         * @param mixed $url
         */
        function __construct($url = null) {
-               $self = array(
-                       "scheme" => filter_input(INPUT_SERVER, "HTTPS", FILTER_VALIDATE_BOOLEAN) ? "https":"http",
-                       "path" => dirname(filter_input(INPUT_SERVER, "SCRIPT_NAME"))."/"
-               );
+               $self = [
+                       "scheme" => filter_input(INPUT_SERVER, "HTTPS", FILTER_VALIDATE_BOOLEAN)
+                               ? "https"
+                               : "http",
+                       "path"   => dirname(filter_input(INPUT_SERVER, "SCRIPT_NAME")) . "/",
+               ];
                parent::__construct($self, $url,
-                               self::JOIN_PATH |
-                               self::SANITIZE_PATH |
-                               self::STRIP_QUERY |
-                               self::STRIP_AUTH |
-                               self::FROM_ENV
+                       self::JOIN_PATH |
+                       self::SANITIZE_PATH |
+                       self::STRIP_QUERY |
+                       self::STRIP_AUTH |
+                       self::FROM_ENV
                );
        }
+
        /**
         * Extract path info
         *
         * @param mixed $url full request url
         * @return string
         */
-       function pathinfo($url) {
+       function pathinfo($url) : string {
                $url = new Url($this, $url, Url::FROM_ENV | Url::STRIP_QUERY);
                $info = substr($url, strlen($this));
                return urldecode($info);
index 589cf412eb72b4093d485261cba99d9d33ac64e2..2ca35f52921db291389eb3017cd1be3a1fb36bff 100644 (file)
@@ -2,10 +2,24 @@
 
 namespace mdref;
 
+use IteratorAggregate;
+use function array_pop;
+use function count;
+use function ctype_lower;
+use function ctype_upper;
+use function dirname;
+use function end;
+use function explode;
+use function implode;
+use function strlen;
+use function strtr;
+use function substr;
+use function trim;
+
 /**
  * A single reference entry
  */
-class Entry implements \IteratorAggregate {
+class Entry implements IteratorAggregate {
        /**
         * Compound name
         * @var string
@@ -26,7 +40,7 @@ class Entry implements \IteratorAggregate {
 
        /**
         * The file path, if the refentry exists
-        * @var type
+        * @var ?string
         */
        private $path;
 
@@ -38,9 +52,9 @@ class Entry implements \IteratorAggregate {
 
        /**
         * @param string $name the compound name of the ref entry, e.g. "pq/Connection/exec"
-        * @param \mdref\Repo $repo the containing repository
+        * @param Repo $repo the containing repository
         */
-       public function __construct($name, Repo $repo) {
+       public function __construct(string $name, Repo $repo) {
                $this->repo = $repo;
                $this->name = $name;
                $this->list = explode("/", $name);
@@ -51,7 +65,7 @@ class Entry implements \IteratorAggregate {
         * Get the compound name, e.g. "pq/Connection/exec"
         * @return string
         */
-       public function getName() {
+       public function getName() : string {
                return $this->name;
        }
 
@@ -59,7 +73,7 @@ class Entry implements \IteratorAggregate {
         * Get the containing repository
         * @return \mdref\Repo
         */
-       public function getRepo() {
+       public function getRepo() : Repo {
                return $this->repo;
        }
 
@@ -67,15 +81,17 @@ class Entry implements \IteratorAggregate {
         * Get the file path, if any
         * @return string
         */
-       public function getPath() {
+       public function getPath() : ?string {
                return $this->path;
        }
 
        /**
         * Get the file instance of this entry
+        *
         * @return \mdref\File
+        * @throws Exception
         */
-       public function getFile() {
+       public function getFile() : File {
                if (!$this->file) {
                        $this->file = new File($this->path);
                }
@@ -86,15 +102,17 @@ class Entry implements \IteratorAggregate {
         * Get edit URL
         * @return string
         */
-       public function getEditUrl() {
+       public function getEditUrl() : string {
                return $this->repo->getEditUrl($this->name);
        }
 
        /**
         * Read the title of the ref entry file
+        *
         * @return string
+        * @throws Exception
         */
-       public function getTitle() {
+       public function getTitle() : string {
                if ($this->isFile()) {
                        return trim($this->getFile()->readTitle());
                }
@@ -106,9 +124,11 @@ class Entry implements \IteratorAggregate {
 
        /**
         * Read the first line of the description of the ref entry file
+        *
         * @return string
+        * @throws Exception
         */
-       public function getDescription() {
+       public function getDescription() : string {
                if ($this->isFile()) {
                        return trim($this->getFile()->readDescription());
                }
@@ -120,9 +140,11 @@ class Entry implements \IteratorAggregate {
 
        /**
         * Read the full description of the ref entry file
+        *
         * @return string
+        * @throws Exception
         */
-       public function getFullDescription() {
+       public function getFullDescription() : string {
                if ($this->isFile()) {
                        return trim($this->getFile()->readFullDescription());
                }
@@ -134,9 +156,11 @@ class Entry implements \IteratorAggregate {
 
        /**
         * Read the intriductory section of the refentry file
+        *
         * @return string
+        * @throws Exception
         */
-       public function getIntro() {
+       public function getIntro() : string {
                if ($this->isFile()) {
                        return trim($this->getFile()->readIntro());
                }
@@ -150,7 +174,7 @@ class Entry implements \IteratorAggregate {
         * Check if the refentry exists
         * @return bool
         */
-       public function isFile() {
+       public function isFile() : bool {
                return strlen($this->path) > 0;
        }
 
@@ -158,7 +182,7 @@ class Entry implements \IteratorAggregate {
         * Check if this is the first entry of the reference tree
         * @return bool
         */
-       public function isRoot() {
+       public function isRoot() : bool {
                return count($this->list) === 1;
        }
 
@@ -166,11 +190,11 @@ class Entry implements \IteratorAggregate {
         * Get the parent ref entry
         * @return \mdref\Entry
         */
-       public function getParent() {
+       public function getParent() : ?Entry {
                switch ($dirn = dirname($this->name)) {
                case ".":
                case "/":
-                       break;
+                       return null;
                default:
                        return $this->repo->getEntry($dirn);
                }
@@ -180,7 +204,7 @@ class Entry implements \IteratorAggregate {
         * Get the list of parents up-down
         * @return array
         */
-       public function getParents() {
+       public function getParents() : array {
                $parents = array();
                for ($parent = $this->getParent(); $parent; $parent = $parent->getParent()) {
                        array_unshift($parents, $parent);
@@ -192,7 +216,7 @@ class Entry implements \IteratorAggregate {
         * Guess whether this ref entry is about a function or method
         * @return bool
         */
-       public function isFunction() {
+       public function isFunction() : bool {
                $base = end($this->list);
                return $base{0} === "_" || ctype_lower($base{0});
        }
@@ -201,16 +225,23 @@ class Entry implements \IteratorAggregate {
         * Guess whether this ref entry is about a namespace, interface or class
         * @return bool
         */
-       public function isNsClass() {
+       public function isNsClass() : bool {
                $base = end($this->list);
                return ctype_upper($base{0});
        }
 
+       /**
+        * @return mixed
+        */
        public function getEntryName() {
                return end($this->list);
        }
 
-       public function getNsName() {
+       /**
+        * Get namespaced name
+        * @return string
+        */
+       public function getNsName() : string {
                if ($this->isRoot()) {
                        return $this->getName();
                } elseif ($this->isFunction()) {
@@ -226,7 +257,7 @@ class Entry implements \IteratorAggregate {
         * Display name
         * @return string
         */
-       public function __toString() {
+       public function __toString() : string {
                $parts = explode("/", trim($this->getName(), "/"));
                $myself = array_pop($parts);
                if (!$parts) {
@@ -248,26 +279,27 @@ class Entry implements \IteratorAggregate {
        }
 
        /**
-        * Get the base name of this ref entry
+        * Get the dirname for child entries
         * @return string
         */
-       public function getBasename() {
+       public function getNextDirname() : string {
                return dirname($this->path) . "/" . basename($this->path, ".md");
        }
 
        /**
         * Guess whether there are any child nodes
         * @param string $glob
-        * @return boolean
+        * @param bool $loose
+        * @return bool
         */
-       function hasIterator($glob = null, $loose = false) {
+       function hasIterator(?string $glob = null, bool $loose = false) : bool {
                if (strlen($glob)) {
-                       return glob($this->getBasename() . "/$glob") ||
-                               ($loose && glob($this->getBasename() . "/*/$glob"));
+                       return glob($this->getNextDirname() . "/$glob") ||
+                               ($loose && glob($this->getNextDirname() . "/*/$glob"));
                } elseif ($this->isRoot()) {
                        return true;
-               } elseif ($this->getBasename() !== "/") {
-                       return is_dir($this->getBasename());
+               } elseif ($this->getNextDirname() !== "/") {
+                       return is_dir($this->getNextDirname());
                } else {
                        return false;
                }
@@ -277,7 +309,7 @@ class Entry implements \IteratorAggregate {
         * Guess whether there are namespace/interface/class child nodes
         * @return bool
         */
-       function hasNsClasses() {
+       function hasNsClasses() : bool {
                return $this->hasIterator("/[A-Z]*.md", true);
        }
 
@@ -285,19 +317,23 @@ class Entry implements \IteratorAggregate {
         * Guess whether there are function/method child nodes
         * @return bool
         */
-       function hasFunctions() {
+       function hasFunctions() : bool {
                return $this->hasIterator("/[a-z_]*.md");
        }
 
        /**
-        * Implements \IteratorAggregate
-        * @return \mdref\Tree child nodes
+        * Implements IteratorAggregate
+        * @return Tree child nodes
         */
-       function getIterator() {
-               return new Tree($this->getBasename(), $this->repo);
+       function getIterator() : Tree {
+               return new Tree($this->getNextDirname(), $this->repo);
        }
 
-       function getStructure() {
+       /**
+        * Get the structure of the refentry
+        * @return Structure
+        */
+       function getStructure() : Structure {
                return new Structure($this);
        }
 }
index 013d86106597524760c949ec2c72ed6fff305ee1..89dea42453bd1de7677670345b796f2aab7ac481 100644 (file)
@@ -2,17 +2,36 @@
 
 namespace mdref;
 
-use http\Response;
+use http\Env\Response;
+use http\Message\Body;
 
 class Exception extends \Exception
 {
-       function __construct($code, $message) {
+       /**
+        * Exception constructor with reversed arguments
+        *
+        * @param int $code HTTP code
+        * @param string $message reason message
+        */
+       function __construct(int $code, ?string $message = null) {
                parent::__construct($message, $code);
        }
 
-       function send(Response $res) {
+       /**
+        * Construct an Exception from error_get_last()'s message and code 500
+        * @return Exception
+        */
+       static function fromLastError() : Exception {
+               return new static(500, error_get_last()["message"]);
+       }
+
+       /**
+        * Send the error response
+        * @param Response $res
+        */
+       function send(Response $res) : void {
                $res->setResponseCode($this->code);
-               $res->setBody(new http\Message\Body);
+               $res->setBody(new Body);
                $res->getBody()->append($this->message);
        }
 }
index b641629f3c9372a63697e29ae2c8ee738ae5af1f..eaab8ea433fbfbff8e11f6253b44e26256de4f94 100644 (file)
@@ -2,7 +2,24 @@
 
 namespace mdref;
 
-use http\Env as HTTP;
+use http\Env;
+use function debug_print_backtrace;
+use function error_get_last;
+use function headers_sent;
+use function implode;
+use function ob_end_clean;
+use function ob_get_clean;
+use function ob_get_level;
+use function ob_start;
+use function register_shutdown_function;
+use function set_error_handler;
+use function set_exception_handler;
+use function sprintf;
+use const E_COMPILE_ERROR;
+use const E_CORE_ERROR;
+use const E_ERROR;
+use const E_PARSE;
+use const E_USER_ERROR;
 
 /**
  * Exception and error handler
@@ -16,15 +33,19 @@ class ExceptionHandler
 
        /**
         * Set up error/exception/shutdown handler
+        * @param Env\Response $r
         */
-       public function __construct(\http\Env\Response $r) {
+       public function __construct(Env\Response $r) {
                $this->response = $r;
                set_exception_handler($this);
                set_error_handler($this);
                register_shutdown_function($this);
        }
 
-       private static function cleanBuffers() {
+       /**
+        * Clean output buffers
+        */
+       private static function cleanBuffers() : void {
                while (ob_get_level()) {
                        if (!@ob_end_clean()) {
                                break;
@@ -34,14 +55,18 @@ class ExceptionHandler
 
        /**
         * The exception/error/shutdown handler callback
+        *
+        * @param \Throwable|string $e
+        * @param ?string $msg
+        * @throws \Exception
         */
-       public function __invoke($e = null, $msg = null) {
-               if ($e instanceof \Exception || $e instanceof \Throwable) {
+       public function __invoke($e = null, ?string $msg = null) : void {
+               if ($e instanceof \Throwable) {
                        try {
                                self::cleanBuffers();
                                echo static::htmlException($e);
                        } catch (\Exception $ignore) {
-                               headers_sent() or HTTP::setResponseCode(500);
+                               headers_sent() or Env::setResponseCode(500);
                                die("FATAL ERROR:\n$e\n$ignore");
                        }
                } elseif (isset($e, $msg)) {
@@ -64,13 +89,14 @@ class ExceptionHandler
 
        /**
         * Format an exception as HTML and send appropriate exception info as HTTP headers
-        * @param Throwable $e
+        * @param \Throwable $e
         * @param array $title_tag
         * @param array $message_tag
         * @param array $trace_tag
         * @return string
         */
-       public static function htmlException(/*\Throwable*/ $e, array $title_tag = ["h1"], array $message_tag = ["p"], array $trace_tag = ["pre", "style='font-size:smaller;overflow-x:scroll'"]) {
+       public static function htmlException(\Throwable $e, array $title_tag = ["h1"], array $message_tag = ["p"],
+                       array $trace_tag = ["pre", "style='font-size:smaller;overflow-x:scroll'"]) : string {
                if ($e instanceof Exception) {
                        $code = $e->getCode() ?: 500;
                } else {
@@ -78,7 +104,7 @@ class ExceptionHandler
                }
                
                for ($html = ""; $e; $e = $e->getPrevious()) {
-                       $html .= static::htmlError(HTTP::getResponseStatusForCode($code),
+                       $html .= static::htmlError(Env::getResponseStatusForCode($code),
                                $e->getMessage(), $code, $e->getTraceAsString(), 
                                $title_tag, $message_tag, $trace_tag);
                }
@@ -96,8 +122,9 @@ class ExceptionHandler
         * @param array $trace_tag
         * @return string
         */
-       public static function htmlError($title, $message, $code, $trace = null, array $title_tag = ["h1"], array $message_tag = ["p"], array $trace_tag = ["pre", "style='font-size:smaller;overflow-x:scroll'"]) {
-               HTTP::setResponseCode($code);
+       public static function htmlError($title, $message, $code, $trace = null, array $title_tag = ["h1"],
+                       array $message_tag = ["p"], array $trace_tag = ["pre", "style='font-size:smaller;overflow-x:scroll'"]) : string {
+               Env::setResponseCode($code);
                
                $html = sprintf("<%s>%s</%s>\n<%s>%s</%s>\n",
                                implode(" ", $title_tag), $title, $title_tag[0],
index 48d53c44150e12acff06b522073ecceeb902645d..6add1d56b7221df4bba940c0fcbc4f96ad3a57f3 100644 (file)
@@ -2,6 +2,14 @@
 
 namespace mdref;
 
+use function feof;
+use function fgets;
+use function fopen;
+use function fseek;
+use function strncmp;
+use function substr;
+use const SEEK_SET;
+
 /**
  * A ref entry file
  */
@@ -13,39 +21,53 @@ class File {
 
        /**
         * Open the file
+        *
         * @param string $path
+        * @throws Exception
         */
-       public function __construct($path) {
-               $this->fd = fopen($path, "rb");
+       public function __construct(string $path) {
+               if (!$this->fd = fopen($path, "rb")) {
+                       throw Exception::fromLastError();
+               }
        }
 
        /**
         * Read the title of the refentry
+        *
         * @return string
+        * @throws Exception
         */
-       public function readTitle() {
+       public function readTitle() : string {
                if ($this->rewind(1)) {
                        return fgets($this->fd);
                }
+               throw Exception::fromLastError();
        }
 
        /**
         * Read the description (first line) of the refentry
+        *
         * @return string
+        * @throws Exception
         */
-       public function readDescription() {
-               if ($this->rewind()
-               && (false !== fgets($this->fd))
+       public function readDescription() : ?string {
+               if (!$this->rewind()) {
+                       throw Exception::fromLastError();
+               }
+               if (false !== fgets($this->fd)
                && (false !== fgets($this->fd))) {
                        return fgets($this->fd);
                }
+               return null;
        }
 
        /**
         * Read the full description (first section) of the refentry
+        *
         * @return string
+        * @throws Exception
         */
-       public function readFullDescription() {
+       public function readFullDescription() : ?string {
                $desc = $this->readDescription();
                while (false !== ($line = fgets($this->fd))) {
                        if ($line{0} === "#") {
@@ -61,7 +83,7 @@ class File {
         * Read the first subsection of a global refentry
         * @return string
         */
-       public function readIntro() {
+       public function readIntro() : string {
                $intro = "";
                if ($this->rewind()) {
                        $header = false;
@@ -87,7 +109,13 @@ class File {
                return $intro;
        }
 
-       public function readSection($title) {
+       /**
+        * Read section of $title
+        *
+        * @param $title
+        * @return string
+        */
+       public function readSection(string $title) : string {
                $section = "";
                if ($this->rewind()) {
                        while (!feof($this->fd)) {
@@ -111,11 +139,20 @@ class File {
                return $section;
        }
 
-       private function rewind($offset = 0) {
+       /**
+        * @param int $offset
+        * @return bool
+        */
+       private function rewind(int $offset = 0) : bool {
                return 0 === fseek($this->fd, $offset, SEEK_SET);
        }
 
-       private function isHeading($line, $title = null) {
+       /**
+        * @param string $line
+        * @param string $title
+        * @return bool
+        */
+       private function isHeading(string $line, ?string $title = null) : bool {
                if ("## " !== substr($line, 0, 3)) {
                        return false;
                }
index 3e40cfff7705d52c016018fedca332e47185562f..561bd1e2e8515f56890d7442deb2a95c23ce7c80 100644 (file)
@@ -2,13 +2,19 @@
 
 namespace mdref;
 
+use ArrayIterator;
+use Iterator;
+use IteratorAggregate;
+use function is_numeric;
+use function preg_replace;
+
 /**
  * The complete available reference
  */
-class Reference implements \IteratorAggregate {
+class Reference implements IteratorAggregate {
        /**
         * List of mdref repositories
-        * @var array
+        * @var Repo[]
         */
        private $repos = array();
 
@@ -25,34 +31,44 @@ class Reference implements \IteratorAggregate {
        /**
         * Lookup the repo containing a ref entry
         * @param string $entry requested reference entry, e.g. "pq/Connection/exec"
-        * @param type $canonical
+        * @param string $canonical
         * @return \mdref\Repo|NULL
         */
-       public function getRepoForEntry($entry, &$canonical = null) {
+       public function getRepoForEntry(string $entry, string &$canonical = null) : ?Repo {
                foreach ($this->repos as $repo) {
                        /** @var $repo Repo */
                        if ($repo->hasEntry($entry, $canonical)) {
                                return $repo;
                        }
                }
+               return null;
        }
 
        /**
-        * Implements \IteratorAggregate
-        * @return \ArrayIterator repository list
+        * Implements IteratorAggregate
+        * @return ArrayIterator repository list
         */
-       public function getIterator() {
-               return new \ArrayIterator($this->repos);
+       public function getIterator() : Iterator {
+               return new ArrayIterator($this->repos);
        }
 
-       public function formatAnchor($anchor) {
+       /**
+        * @param string $anchor
+        * @return string
+        */
+       public function formatAnchor(string $anchor) : string {
                if (is_numeric($anchor)) {
                        return "L$anchor";
                }
                return preg_replace("/[^[:alnum:]\.:_]/", ".", $anchor);
        }
 
-       public function formatString($string) {
+       /**
+        * @param string $string
+        * @return string
+        * @throws \Exception
+        */
+       public function formatString(string $string) : string {
                if (extension_loaded("discount")) {
                        $md = \MarkdownDocument::createFromString($string);
                        $md->compile(\MarkdownDocument::AUTOLINK);
@@ -65,7 +81,12 @@ class Reference implements \IteratorAggregate {
                throw new \Exception("No Markdown implementation found");
        }
 
-       public function formatFile($file) {
+       /**
+        * @param string $file
+        * @return string
+        * @throws \Exception
+        */
+       public function formatFile(string $file) : string {
                if (extension_loaded("discount")) {
                        $fd = fopen($file, "r");
                        $md = \MarkdownDocument::createFromStream($fd);
index 4b291ab6579020923cdb1e305dc66cbeb1716d5d..869b74e4f7acb28b787face9a490b878a5f683d8 100644 (file)
@@ -3,10 +3,22 @@
 namespace mdref;
 
 
+use InvalidArgumentException;
+use IteratorAggregate;
+use function basename;
+use function current;
+use function file_get_contents;
+use function glob;
+use function is_file;
+use function realpath;
+use function rtrim;
+use function sprintf;
+use function trim;
+
 /**
  * A reference repo
  */
-class Repo implements \IteratorAggregate {
+class Repo implements IteratorAggregate {
        /**
         * The name of the repository
         * @var string
@@ -30,11 +42,10 @@ class Repo implements \IteratorAggregate {
         * @param string $path
         * @throws \InvalidArgumentException
         */
-       public function __construct($path) {
+       public function __construct(string $path) {
                if (!($mdref = current(glob("$path/*.mdref")))) {
-                       throw new \InvalidArgumentException(
-                               sprintf("Not a reference, could not find '*.mdref': '%s'",
-                                       $path));
+                       throw new InvalidArgumentException(
+                               sprintf("Not a reference, could not find '*.mdref': '%s'", $path));
                }
 
                $this->path = realpath($path);
@@ -46,7 +57,7 @@ class Repo implements \IteratorAggregate {
         * Get the repository's name
         * @return string
         */
-       public function getName() {
+       public function getName() : string {
                return $this->name;
        }
 
@@ -55,7 +66,7 @@ class Repo implements \IteratorAggregate {
         * @param string $file
         * @return string
         */
-       public function getPath($file = "") {
+       public function getPath(string $file = "") : string {
                return $this->path . "/$file";
        }
 
@@ -64,16 +75,18 @@ class Repo implements \IteratorAggregate {
         * @param string $entry
         * @return string
         */
-       public function getEditUrl($entry) {
+       public function getEditUrl(string $entry) : string {
                return sprintf($this->edit, $entry);
        }
 
        /**
         * Get the file path of an entry in this repo
+        *
         * @param string $entry
+        * @param string|null $canonical
         * @return string file path
         */
-       public function hasEntry($entry, &$canonical = null) {
+       public function hasEntry(string $entry, ?string &$canonical = null) : ?string {
                $trim = rtrim($entry, "/");
                $file = $this->getPath("$trim.md");
                if (is_file($file)) {
@@ -87,6 +100,7 @@ class Repo implements \IteratorAggregate {
                        $canonical = $this->getName() . "/" . $entry;
                        return $file;
                }
+               return null;
        }
 
        /**
@@ -94,7 +108,7 @@ class Repo implements \IteratorAggregate {
         * @param string $file
         * @return string entry
         */
-       public function hasFile($file) {
+       public function hasFile(string $file) : ?string {
                if (($file = realpath($file))) {
                        $path = $this->getPath();
                        $plen = strlen($path);
@@ -109,9 +123,15 @@ class Repo implements \IteratorAggregate {
                                return  $dirname . "/". $basename;
                        }
                }
+               return null;
        }
 
-       public function hasStub(&$path = null) {
+       /**
+        * Check whether the repo has a stub file to serve
+        * @param string|null $path receives the path if there's a stub
+        * @return bool
+        */
+       public function hasStub(string &$path = null) : bool {
                $path = $this->getPath($this->getName() . ".stub.php");
                return is_file($path) && is_readable($path);
        }
@@ -122,7 +142,7 @@ class Repo implements \IteratorAggregate {
         * @return \mdref\Entry
         * @throws \OutOfBoundsException
         */
-       public function getEntry($entry) {
+       public function getEntry(string $entry) : Entry {
                return new Entry($entry, $this);
        }
 
@@ -130,7 +150,7 @@ class Repo implements \IteratorAggregate {
         * Get the root Entry instance
         * @return \mdref\Entry
         */
-       public function getRootEntry() {
+       public function getRootEntry() : Entry {
                return new Entry($this->name, $this);
        }
 
@@ -138,7 +158,7 @@ class Repo implements \IteratorAggregate {
         * Implements \IteratorAggregate
         * @return \mdref\Tree
         */
-       public function getIterator() {
+       public function getIterator() : Tree {
                return new Tree($this->path, $this);
        }
 }
index 11cada07293495da26112af7c8e13b66d421bc04..357f99174f52e6811eb09865bdfe29ef2758c699 100644 (file)
@@ -2,7 +2,24 @@
 
 namespace mdref;
 
-class Tree implements \RecursiveIterator {
+use Iterator;
+use RecursiveIterator;
+use function array_filter;
+use function array_search;
+use function basename;
+use function current;
+use function dirname;
+use function glob;
+use function is_dir;
+use function key;
+use function next;
+use function pathinfo;
+use function preg_match;
+use function reset;
+use function strcmp;
+use function usort;
+
+class Tree implements RecursiveIterator {
        /**
         * The repository
         * @var \mdref\Repo
@@ -25,7 +42,7 @@ class Tree implements \RecursiveIterator {
         * @param string $path
         * @param \mdref\Repo $repo
         */
-       public function __construct($path, Repo $repo) {
+       public function __construct(string $path, Repo $repo) {
                if (realpath($path)."/" === $repo->getPath()) {
                        $list = [$path ."/". $repo->getName() .".md"];
                } elseif (!($list = glob("$path/*.md"))) {
@@ -42,7 +59,7 @@ class Tree implements \RecursiveIterator {
         * @param array $list
         * @return callable
         */
-       private function generateFilter(array $list) {
+       private function generateFilter(array $list) : \Closure {
                return function($v) use($list) {
                        if ($v{0} === ".") {
                                return false;
@@ -63,7 +80,7 @@ class Tree implements \RecursiveIterator {
        /**
         * @return callable
         */
-       private function generateSorter() {
+       private function generateSorter() : \Closure {
                return function($a, $b) {
                        $ab = basename($a, ".md");
                        $bb = basename($b, ".md");
@@ -106,14 +123,14 @@ class Tree implements \RecursiveIterator {
         * Implements \Iterator
         * @return \mdref\Entry
         */
-       public function current() {
+       public function current() : Entry {
                return $this->repo->getEntry($this->repo->hasFile(current($this->iter)));
        }
 
        /**
         * Implements \Iterator
         */
-       public function next() {
+       public function next() : void {
                next($this->iter);
        }
 
@@ -121,14 +138,14 @@ class Tree implements \RecursiveIterator {
         * Implements \Iterator
         * @return int
         */
-       public function key() {
+       public function key() : int {
                return key($this->iter);
        }
 
        /**
         * Implements \Iterator
         */
-       public function rewind() {
+       public function rewind() : void {
                $this->iter = $this->list;
                reset($this->iter);
        }
@@ -137,7 +154,7 @@ class Tree implements \RecursiveIterator {
         * Implements \Iterator
         * @return bool
         */
-       public function valid() {
+       public function valid() : bool {
                return null !== key($this->iter);
        }
 
@@ -145,7 +162,7 @@ class Tree implements \RecursiveIterator {
         * Implements \RecursiveIterator
         * @return bool
         */
-       public function hasChildren() {
+       public function hasChildren() : bool {
                return $this->current()->hasIterator();
        }
 
@@ -153,7 +170,7 @@ class Tree implements \RecursiveIterator {
         * Implements \RecursiveIterator
         * @return \mdref\Tree
         */
-       public function getChildren() {
+       public function getChildren() : Iterator {
                return $this->current()->getIterator();
        }
 }
index 75a80e5fd40cdb6e3ac5c460a2b8c383442577a5..2e43a16b22b6bf5a46973c561963242a9d7e31b2 100644 (file)
@@ -1,12 +1,18 @@
 <?php
 
-use mdref\Action;
-use mdref\BaseUrl;
-use mdref\Reference;
-use mdref\ExceptionHandler;
+namespace mdref;
 
 use http\Env\Request;
 use http\Env\Response;
+use function ini_get;
+use function ini_set;
+use function spl_autoload_register;
+use function strncmp;
+use function strtr;
+use const GLOB_ONLYDIR;
+use const PATH_SEPARATOR;
+use const REFS;
+use const ROOT;
 
 define("ROOT", dirname(__DIR__));
 define("REFS", getenv("REFPATH") ?: implode(PATH_SEPARATOR, glob(ROOT."/refs/*", GLOB_ONLYDIR)));