fix weak property types
[mdref/mdref] / mdref / File.php
index 0ac3da26f1ac3f43d50a00c3aeb2e200ed60ec79..3c1ca0a6d276e37314178606f336d595ad3137c2 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
  */
@@ -10,52 +18,82 @@ class File {
         * @var resource
         */
        private $fd;
-       
+
        /**
         * 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() {
-               if (0 === fseek($this->fd, 1, SEEK_SET)) {
+       public function readTitle() : string {
+               if ($this->rewind(1)) {
                        return fgets($this->fd);
                }
+               throw Exception::fromLastError();
        }
-       
+
        /**
-        * Read the description of the refentry
+        * Read the description (first line) of the refentry
+        *
         * @return string
+        * @throws Exception
         */
-       public function readDescription() {
-               if (0 === fseek($this->fd, 0, SEEK_SET)
-               && (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() : ?string {
+               $desc = $this->readDescription();
+               while (false !== ($line = fgets($this->fd))) {
+                       if ($line[0] === "#") {
+                               break;
+                       } else {
+                               $desc .= $line;
+                       }
+               }
+               return $desc;
+       }
+
        /**
         * Read the first subsection of a global refentry
         * @return string
         */
-       public function readIntro() {
+       public function readIntro() : string {
                $intro = "";
-               if (0 === fseek($this->fd, 0, SEEK_SET)) {
+               if ($this->rewind()) {
                        $header = false;
-                       
+
                        while (!feof($this->fd)) {
                                if (false === ($line = fgets($this->fd))) {
                                        break;
                                }
                                /* search first header and read until next header*/
-                               if ("## " === substr($line, 0, 3)) {
+                               if ($this->isHeading($line)) {
                                        if ($header) {
                                                break;
                                        } else {
@@ -70,4 +108,57 @@ class File {
                }
                return $intro;
        }
+
+       /**
+        * Read section of $title
+        *
+        * @param $title
+        * @return string
+        */
+       public function readSection(string $title) : string {
+               $section = "";
+               if ($this->rewind()) {
+                       while (!feof($this->fd)) {
+                               if (false === ($line = fgets($this->fd))) {
+                                       break;
+                               }
+                               /* search for heading with $title and read until next heading */
+                               if ($this->isHeading($line, $title)) {
+                                       do {
+                                               if (false === $line = fgets($this->fd)) {
+                                                       break;
+                                               }
+                                               if ($this->isHeading($line)) {
+                                                       break;
+                                               }
+                                               $section .= $line;
+                                       } while (true);
+                               }
+                       }
+               }
+               return $section;
+       }
+
+       /**
+        * @param int $offset
+        * @return bool
+        */
+       private function rewind(int $offset = 0) : bool {
+               return 0 === fseek($this->fd, $offset, SEEK_SET);
+       }
+
+       /**
+        * @param string $line
+        * @param string $title
+        * @return bool
+        */
+       private function isHeading(string $line, ?string $title = null) : bool {
+               if ("## " !== substr($line, 0, 3)) {
+                       return false;
+               }
+               if (isset($title)) {
+                       return !strncmp(substr($line, 3), $title, strlen($title));
+               }
+               return true;
+       }
 }