From cc51febb88600aa4fcd4881d880f698b450bb3ff Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Tue, 12 Mar 2019 17:54:33 +0100 Subject: [PATCH] running on php-7.3 now --- composer.json | 13 ++++- mdref/Action.php | 59 +++++++++++++------- mdref/BaseUrl.php | 34 ++++++++---- mdref/Entry.php | 110 ++++++++++++++++++++++++------------- mdref/Exception.php | 27 +++++++-- mdref/ExceptionHandler.php | 49 +++++++++++++---- mdref/File.php | 59 ++++++++++++++++---- mdref/Reference.php | 43 +++++++++++---- mdref/Repo.php | 48 +++++++++++----- mdref/Tree.php | 39 +++++++++---- public/index.php | 14 +++-- 11 files changed, 360 insertions(+), 135 deletions(-) diff --git a/composer.json b/composer.json index 48578c2..b017f41 100644 --- a/composer.json +++ b/composer.json @@ -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." } } diff --git a/mdref/Action.php b/mdref/Action.php index deb62e5..7e1307b 100644 --- a/mdref/Action.php +++ b/mdref/Action.php @@ -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); diff --git a/mdref/BaseUrl.php b/mdref/BaseUrl.php index 7476a82..2d1503d 100644 --- a/mdref/BaseUrl.php +++ b/mdref/BaseUrl.php @@ -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); diff --git a/mdref/Entry.php b/mdref/Entry.php index 589cf41..2ca35f5 100644 --- a/mdref/Entry.php +++ b/mdref/Entry.php @@ -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); } } diff --git a/mdref/Exception.php b/mdref/Exception.php index 013d861..89dea42 100644 --- a/mdref/Exception.php +++ b/mdref/Exception.php @@ -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); } } diff --git a/mdref/ExceptionHandler.php b/mdref/ExceptionHandler.php index b641629..eaab8ea 100644 --- a/mdref/ExceptionHandler.php +++ b/mdref/ExceptionHandler.php @@ -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\n<%s>%s\n", implode(" ", $title_tag), $title, $title_tag[0], diff --git a/mdref/File.php b/mdref/File.php index 48d53c4..6add1d5 100644 --- a/mdref/File.php +++ b/mdref/File.php @@ -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; } diff --git a/mdref/Reference.php b/mdref/Reference.php index 3e40cff..561bd1e 100644 --- a/mdref/Reference.php +++ b/mdref/Reference.php @@ -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); diff --git a/mdref/Repo.php b/mdref/Repo.php index 4b291ab..869b74e 100644 --- a/mdref/Repo.php +++ b/mdref/Repo.php @@ -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); } } diff --git a/mdref/Tree.php b/mdref/Tree.php index 11cada0..357f991 100644 --- a/mdref/Tree.php +++ b/mdref/Tree.php @@ -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(); } } diff --git a/public/index.php b/public/index.php index 75a80e5..2e43a16 100644 --- a/public/index.php +++ b/public/index.php @@ -1,12 +1,18 @@