Passed
Push — main ( 4844f1...183dc0 )
by Marc
03:18
created

AbstractIndexer::loadIndex()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 7
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 11
rs 10
1
<?php declare(strict_types=1);
2
namespace html_go\indexing;
3
4
use InvalidArgumentException;
5
use html_go\exceptions\InternalException;
6
7
abstract class AbstractIndexer
8
{
9
    protected string $parentDir;
10
    protected string $commonDir;
11
    protected string $userDataDir;
12
13
    protected string $pageInxFile;
14
    protected string $catInxFile;
15
    protected string $postInxFile;
16
    protected string $tagInxFile;
17
    protected string $tag2postInxFile;
18
    protected string $cat2postInxFile;
19
    protected string $menuInxFile;
20
21
    function __construct(string $parentDir) {
22
        if (($path = \realpath($parentDir)) === false) {
23
            throw new InternalException("realpath() function failed on [$parentDir]"); // @codeCoverageIgnore
24
        }
25
        $this->parentDir = $path;
26
27
        $this->commonDir = $path.DS.'content'.DS.'common';
28
        if (\is_dir($this->commonDir) === false) {
29
            throw new InvalidArgumentException("The content/common directory cannot be found [$this->commonDir]");
30
        }
31
32
        $this->userDataDir = $path.DS.'content'.DS.'user-data';
33
        if (\is_dir($this->userDataDir) === false) {
34
            throw new InvalidArgumentException("The content/user-data directory cannot be found [$this->userDataDir]");
35
        }
36
        $indexDir = $path.DS.'cache'.DS.'indexes';
37
        $this->pageInxFile = $indexDir.DS.'page.inx';
38
        $this->catInxFile = $indexDir.DS.'category.inx';
39
        $this->postInxFile = $indexDir.DS.'post.inx';
40
        $this->tagInxFile = $indexDir.DS.'tag.inx';
41
        $this->tag2postInxFile = $indexDir.DS.'tag2post.inx';
42
        $this->cat2postInxFile = $indexDir.DS.'cat2post.inx';
43
        $this->menuInxFile = $indexDir.DS.'menu.inx';
44
    }
45
46
    /**
47
     * Load the given index file.
48
     * @param string $filename
49
     * @throws InternalException
50
     * @throws InvalidArgumentException
51
     * @return array<string, \stdClass>
52
     */
53
    protected function loadIndex(string $filename): array {
54
        if (\file_exists($filename) === false) {
55
            throw new InvalidArgumentException("Index file does not exist [$filename]. Call 'redindex()'"); // @codeCoverageIgnore
56
        }
57
        if (($data = \file_get_contents($filename)) === false) {
58
            throw new InternalException("file_get_contents() failed [$filename]"); // @codeCoverageIgnore
59
        }
60
        if (($data = \unserialize($data)) === false) {
61
            throw new InternalException("unserialize() failed [$filename]"); // @codeCoverageIgnore
62
        }
63
        return $data;
64
    }
65
66
    /**
67
     * Recursively scans a folder heirarchy returning the all the files and folders
68
     * in an array.
69
     * @return array<int, string>
70
     * @throws InternalException
71
     */
72
    protected function scanDirectory(string $rootDir): array {
73
        static $files = [];
74
        if (($handle = \opendir($rootDir)) === false) {
75
            throw new InternalException("opendir() failed [$rootDir]"); // @codeCoverageIgnore
76
        }
77
        while (($entry = \readdir($handle)) !== false) {
78
            $path = $rootDir.DS.$entry;
79
            if (\is_dir($path)) {
80
                if ($entry === '.' || $entry === '..') {
81
                    continue;
82
                }
83
                $this->scanDirectory($path);
84
                continue;
85
            }
86
            $files[] = $path;
87
        }
88
        \closedir($handle);
89
        return $files;
90
    }
91
92
    /**
93
     * @return array<int, string>
94
     * @throws InternalException
95
     */
96
    protected function parseDirectory(string $pattern): array {
97
        if (($files = \glob($pattern, GLOB_NOSORT)) === false) {
98
            throw new InternalException("glob() failed [$pattern]"); // @codeCoverageIgnore
99
        }
100
        return $files;
101
    }
102
103
    /**
104
     * Writes data to an index file, creating the file if necessary.
105
     * @param string $filepath
106
     * @param array<mixed> $index
107
     * @throws InternalException
108
     */
109
    protected function writeIndex(string $filepath, array $index): void {
110
        $index = \serialize($index);
111
        if (\file_put_contents($filepath, print_r($index, true)) === false) {
112
            throw new InternalException("file_put_contents() failed [$filepath]"); // @codeCoverageIgnore
113
        }
114
    }
115
116
    /**
117
     * Creates and populates an index Element class.
118
     * @param string $key The index key
119
     * @param string $path The filepath
120
     * @param string $section 'pages', 'posts', 'categories' or 'tags'
121
     * @param string $optional When populating with variable arguments, use the
122
     * following <b>named parameters<b>:
123
     * <ul>
124
     *   <li>type:</li>
125
     *   <li>category:</li>
126
     *   <li>username:</li>
127
     *   <li>date:</li>
128
     *   <li>tags:</li>
129
     * </ul>
130
     * @return \stdClass
131
     */
132
    protected function createElementClass(string $key, string $path, string $section, string ...$optional): \stdClass {
133
        $obj = new \stdClass();
134
        $obj->key = $key;
135
        $obj->path = $path;
136
        $obj->section = $section;
137
        $obj->type = $this->checkSetOrDefault($optional, 'type', EMPTY_VALUE);
138
        $obj->category = $this->checkSetOrDefault($optional, 'category', EMPTY_VALUE);
139
        $obj->username = $this->checkSetOrDefault($optional, 'username', EMPTY_VALUE);
140
        $obj->date = $this->checkSetOrDefault($optional, 'date', EMPTY_VALUE);
141
142
        $tags = [];
143
        $tagList = EMPTY_VALUE;
144
        if (isset($optional['tags'])) {
145
            $tagList = $optional['tags'];
146
        }
147
        if (!empty($tagList)) {
148
            $tags = \explode(',', $tagList);
149
        }
150
        $obj->tags = $tags;
151
        return $obj;
152
    }
153
154
    /**
155
     * Checks if the given key is set in the given array. If so, returns the value,
156
     * otherwise returns the default value.
157
     * @param array<mixed> $ar
158
     * @param string $key
159
     * @param mixed $default
160
     * @return mixed
161
     */
162
    private function checkSetOrDefault(array $ar, string $key, mixed $default): mixed {
163
        if (isset($ar[$key])) {
164
            return $ar[$key];
165
        }
166
        return $default;
167
    }
168
169
    abstract function reindex(): void;
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
170
}
171